diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/CREDITS linux.ac/CREDITS --- linux.vanilla/CREDITS Sat May 26 16:52:51 2001 +++ linux.ac/CREDITS Sat May 26 16:58:42 2001 @@ -78,8 +78,8 @@ D: Unix98 pty support. D: APM update to 1.2 spec. D: /devfs hacking. -S: 322 N. Riverside Dr. -S: Neptune, NJ 07753 +S: 7 Kiwi Loop +S: Howell, NJ 07731 S: USA N: Erik Andersen @@ -674,6 +674,14 @@ S: K1N 6Z9 S: CANADA +N: Jeff Dike +E: jdike@karaya.com +W: http://user-mode-linux.sourceforge.net +D: User mode kernel port +S: RR1 Box 67C +S: Deering NH 03244 +S: USA + N: Eddie C. Dost E: ecd@skynet.be D: Linux/Sparc kernel hacker @@ -1051,6 +1059,11 @@ S: 2400 AG, Alphen aan den Rijn S: The Netherlands +N: Enver Haase +E: ehaase@inf.fu-berlin.de +W: http://www.inf.fu-berlin.de/~ehaase +D: Driver for the Commodore A2232 serial board + N: Bruno Haible E: haible@ma2s2.mathematik.uni-karlsruhe.de D: SysV FS, shm swapping, memory management fixes @@ -2883,6 +2896,14 @@ S: X/OS Experts in Open Systems BV S: Kruislaan 419 S: 1098 VA Amsterdam +S: The Netherlands + +N: Jeroen Vreeken +E: pe1rxq@amsat.org +W: http://www.chello.nl/~j.vreeken/ +D: SE401 usb webcam driver +S: Maastrichterweg 63 +S: 5554 GG Valkenswaard S: The Netherlands N: Peter Shaobo Wang diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/Configure.help linux.ac/Documentation/Configure.help --- linux.vanilla/Documentation/Configure.help Sat May 26 16:52:52 2001 +++ linux.ac/Documentation/Configure.help Sat May 26 17:25:22 2001 @@ -1,4 +1,5 @@ -# Maintained by Axel Boldt (axel@uni-paderborn.de) +# Maintained by Eric S. Raymond +# and by Steven P. Cole # # This version of the Linux kernel configuration help texts # corresponds to the kernel versions 2.4.x. @@ -6,20 +7,20 @@ # Translations of this file available on the WWW: # # - Japanese, maintained by the JF Project (JF@linux.or.jp), at -# http://www.linux.or.jp/JF/JFdocs/Configure.help/ +# # - Russian, by kaf@linux.nevod.perm.su, at -# http://nevod.perm.su/service/linux/doc/kernel/Configure.help +# # - French, by Pierre Tane (tanep@bigfoot.com), at -# http://www.traduc.org/kernelfr +# # - Spanish, by Carlos Perelló Marín (fperllo@ehome.encis.es), at -# http://visar.csustan.edu/~carlos/ +# # XXX: Site has moved, new location has no Configure.help trans. # - Italian, by Alessandro Rubini (rubini@linux.it), at -# ftp://ftp-pavia1.linux.it/pub/linux/Configure.help +# # XXX: ftp-pavia1.linux.it: Non-existent host/domain # - Polish, by Cezar Cichocki (cezar@cs.net.pl), at -# http://www.cs.net.pl/~cezar/Kernel -# - German, by SuSE, at http://www.suse.de/~ke/kernel . This patch +# +# - German, by SuSE, at . This patch # also includes infrastructure to support different languages. # # To access a document on the WWW, you need to have a direct Internet @@ -31,7 +32,7 @@ # # Information about what a kernel is, what it does, how to patch and # compile it and much more is contained in the Kernel-HOWTO, available -# at http://www.linuxdoc.org/docs.html#howto . Before you start +# at . Before you start # compiling, make sure that you have the necessary versions of all # programs and libraries required to compile and run this kernel; they # are listed in the file Documentation/Changes. Make sure to read the @@ -93,6 +94,12 @@ you say Y here, you will be offered the choice of using features or drivers that are currently considered to be in the alpha-test phase. +Prompt for drivers for obsolete features and hardware +CONFIG_OBSOLETE + Obsolete drivers have usually been replaced by more recent software that + can talk to the same hardware. Obsolete hardware is things like MGA + monitors that you are very unlikely to see on today's systems. + Symmetric Multi Processing CONFIG_SMP This enables support for systems with more than one CPU. If you have @@ -115,22 +122,40 @@ Management" code will be disabled if you say Y here. See also the files Documentation/smp.tex, Documentation/smp.txt, - Documentation/i386/IO-APIC.txt, Documentation/nmi_watchdog.txt and the - SMP-FAQ on the WWW at http://www.irisa.fr/prive/mentre/smp-faq/ . + Documentation/i386/IO-APIC.txt, Documentation/nmi_watchdog.txt and + the SMP-FAQ on the WWW at . If you don't know what to do here, say N. -APIC and IO-APIC Support on Uniprocessors +IO-APIC Support on Uniprocessors CONFIG_X86_UP_IOAPIC APIC (Advanced Programmable Interrupt Controller) is a scheme for delivering hardware interrupt requests to the CPU. It is commonly - used on systems with several CPU's. If you have a single-CPU system - which uses APIC, you can say Y here to use it. If you say Y here - even though your machine doesn't have APIC, then the kernel will - still run with no slowdown at all. + used on systems with several CPUs. If you have a single-CPU system + which has a processor that has an integrated APIC, you can say Y + here to enable and use it. If you say Y here even though your + machine doesn't have an APIC, then the kernel will still run with no + slowdown at all. The advantage of APIC support is the possibility + to use performance counters, and the APIC based NMI watchdog which + detects hard lockups. + + An IO-APIC is an SMP-capable replacement for PC-style interrupts + controllers, most SMP systems and a small number of uniprocessor + systems have these chips. Linux will try to detect and use this + chip, if it's not found then Linux falls back to PC-style interrupt + handling. - If you have system with several CPU's, you do not need to say Y - here: APIC will be used automatically. +APIC Support on Uniprocessors +CONFIG_X86_UP_APIC + APIC (Advanced Programmable Interrupt Controller) is a scheme for + delivering hardware interrupt requests to the CPU. It is commonly + used on systems with several CPUs. If you have a single-CPU system + which has a processor that has an integrated APIC, you can say Y + here to enable and use it. If you say Y here even though your + machine doesn't have an APIC, then the kernel will still run with no + slowdown at all. The advantage of APIC support is the possibility + to use performance counters, and the APIC based NMI watchdog which + detects hard lockups. Kernel math emulation CONFIG_MATH_EMULATION @@ -245,6 +270,18 @@ The module will be called floppy.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +iSeries Virtual I/O Disk Support +CONFIG_VIODASD + If you are running on an iSeries system and you want to use + virtual disks created and managed by OS/400, say Y. + +iSeries Virtual I/O Disk IDE Emulation +CONFIG_VIODASD_IDE + This causes the iSeries virtual disks to look like IDE disks. + If you have programs or utilities that only support certain + kinds of disks, this option will cause iSeries virtual disks + to pretend to be IDE disks, which may satisfy the program. + Support for PowerMac floppy CONFIG_MAC_FLOPPY If you have a SWIM-3 (Super Woz Integrated Machine 3; from Apple) @@ -305,18 +342,18 @@ bits of, say, a sound file). This is also safe if the file resides on a remote file server. If you want to do this, you will first have to acquire and install a kernel patch from - ftp://ftp.kerneli.org/pub/kerneli/ , and then you need to + , and then you need to say Y to this option. Note that alternative ways to use encrypted file systems are provided by the cfs package, which can be gotten from - ftp://ftp.kerneli.org/pub/kerneli/net-source/ , and the newer tcfs - package, available at http://tcfs.dia.unisa.it/ . You do not need to + , and the newer tcfs + package, available at . You do not need to say Y here if you want to use one of these. However, using cfs requires saying Y to "NFS file system support" below while using tcfs requires applying a kernel patch. An alternative steganography solution is provided by StegFS, also available from - ftp://ftp.kerneli.org/pub/kerneli/net-source/ . + . To use the loop device, you need the losetup utility and a recent version of the mount program, both contained in the util-linux @@ -418,11 +455,11 @@ topics, is contained in Documentation/ide.txt. For detailed information about hard drives, consult the Disk-HOWTO and the Multi-Disk-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . To fine-tune ATA/IDE drive/interface parameters for improved performance, look for the hdparm package at - http://www.ibiblio.org/pub/Linux/system/hardware . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -451,7 +488,7 @@ If you are unsure, then just choose the Enhanced IDE/MFM/RLL driver instead of this one. For more detailed information, read the Disk-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Use old disk-only driver on primary interface CONFIG_BLK_DEV_HD_IDE @@ -507,11 +544,11 @@ to say Y or M to "ISO 9660 CDROM file system support". Read the CDROM-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto and the file + and the file Documentation/cdrom/ide-cd. Note that older versions of lilo (the Linux boot loader) cannot properly deal with IDE/ATAPI CDROMs, so install lilo-16 or higher, available from - ftp://metalab.unc.edu/pub/Linux/system/boot/lilo . + . If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -550,7 +587,7 @@ The LS-120 and the IDE/ATAPI Iomega ZIP drive are also supported by this driver. For information about jumper settings and the question of when a ZIP drive uses a partition table, see - http://www.win.tue.nl/~aeb/linux/zip/zip-1.html . + . (ATAPI PD-CD/CDR drives are not supported by this driver; support for PD-CD/CDR drives is available if you answer Y to "SCSI emulation support", below). @@ -660,7 +697,7 @@ for these drives, but you can change that by saying Y to the following question "Use DMA by default when available". You can get the latest version of the hdparm utility from - ftp://metalab.unc.edu/pub/Linux/system/hardware/ . + . Read the comments at the beginning of drivers/ide/ide-dma.c and the file Documentation/ide.txt for more information. @@ -737,7 +774,7 @@ This card is 2,4, or 8 channel master mode support only. SCSI support required!!! - http://www.3ware.com/ + Please read the comments at the top of drivers/scsi/3w-xxxx.c @@ -875,9 +912,9 @@ This is a driver for the OPTi 82C621 EIDE controller. Please read the comments at the top of drivers/ide/opti621.c. -ServerWorks OSB4 chipset support (EXPERIMENTAL) +ServerWorks OSB4 chipset support CONFIG_BLK_DEV_OSB4 - This driver adds PIO/DMA support for the Serverworks OSB4 chipset + This driver adds PIO/(U)DMA support for the ServerWorks OSB4 chipset. Intel PIIXn chipsets support CONFIG_BLK_DEV_PIIX @@ -905,7 +942,7 @@ If unsure, say N. -PROMISE PDC20246/PDC20262/PDC20267 support +PROMISE PDC20246/PDC20262/PDC20265/PDC20267 support CONFIG_BLK_DEV_PDC202XX Promise Ultra33 or PDC20246 Promise Ultra66 or PDC20262 @@ -979,12 +1016,10 @@ VIA82CXXX chipset support CONFIG_BLK_DEV_VIA82CXXX This allows you to configure your chipset for a better use while - running (U)DMA: it will allow you to enable efficiently the second - channel dma usage, as it may not be set by BIOS. It allows you to - pass a kernel command line at boot time in order to set fifo - config. If no command line is provided, it will try to set fifo + running PIO/(U)DMA, it will allow you to enable efficiently the second + channel dma usage, as it may not be set by BIOS. It will try to set fifo configuration at its best. It will allow you to get information from - /proc/ide/via provided you enabled "proc" support. + /proc/ide/via provided you enabled "/proc file system" support. Please read the comments at the top of drivers/ide/via82cxxx.c @@ -993,12 +1028,6 @@ If unsure, say N. -VIA82CXXX Tuning support (WIP) -CONFIG_VIA82CXXX_TUNING - Please read the comments at the top of drivers/ide/via82cxxx.c - - If unsure, say N. - Other IDE chipset support CONFIG_IDE_CHIPSETS Say Y here if you want to include enhanced support for various IDE @@ -1292,7 +1321,7 @@ driver. See include/linux/pg.h for details. You can obtain the most recent version of cdrecord from - ftp://ftp.fokus.gmd.de/pub/unix/cdrecord/ . Versions 1.6.1a3 and + . Versions 1.6.1a3 and later fully support this driver. ATEN EH-100 protocol @@ -1493,7 +1522,7 @@ More information about Software RAID on Linux is contained in the Software-RAID mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . There you will also + . There you will also learn where to get the supporting user space utilities raidtools. If unsure, say N. @@ -1521,7 +1550,7 @@ Information about Software RAID on Linux is contained in the Software-RAID mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . There you will also + . There you will also learn where to get the supporting user space utilities raidtools. If you want to compile this as a module ( = code which can be @@ -1531,6 +1560,15 @@ If unsure, say Y. +RAID-1/RAID-5 code (DANGEROUS) +CONFIG_RAID15_DANGEROUS + This new RAID1/RAID5 code has been freshly merged, and has not seen + enough testing yet. While there are no known bugs in it, it might + destroy your file systems, eat your data and start World War III. + You have been warned. + + If unsure, say N. + RAID-1 (mirroring) mode CONFIG_MD_RAID1 A RAID-1 set consists of several disk drives which are exact copies @@ -1543,7 +1581,7 @@ Information about Software RAID on Linux is contained in the Software-RAID mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . There you will also + . There you will also learn where to get the supporting user space utilities raidtools. If you want to use such a RAID-1 set, say Y. This code is also @@ -1566,7 +1604,7 @@ Information about Software RAID on Linux is contained in the Software-RAID mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . There you will also + . There you will also learn where to get the supporting user space utilities raidtools. If you want to use such a RAID-4/RAID-5 set, say Y. This code is @@ -1582,25 +1620,31 @@ This is a machine with a R4400 133/150 MHz CPU. To compile a Linux kernel that runs on these, say Y here. For details about Linux on the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - http://oss.sgi.com/mips . + . Support for Algorithmics P4032 (EXPERIMENTAL) CONFIG_ALGOR_P4032 This is an evaluation board of the British company Algorithmics. The board uses the R4300 and a R5230 CPUs. For more information about - this board see http://www.algor.co.uk . + this board see . Support for BAGET MIPS series CONFIG_BAGET_MIPS This enables support for the Baget, a Russian embedded system. For more details about the Baget see the Linux/MIPS FAQ on - http://oss.sgi.com/mips . + . + +Baget AMD LANCE support +CONFIG_BAGETLANCE + Say Y to enable kernel support for AMD Lance ethernet cards on the + MIPS-32-based Baget embedded system. This chipset is better known + via the NE2100 cards. Support for DECstations CONFIG_DECSTATION This enables support for DEC's MIPS based workstations. For details - see the Linux/MIPS FAQ on http://oss.sgi.com/mips and the - DECstation porting pages on http://decstation.unix-ag.org . + see the Linux/MIPS FAQ on and the + DECstation porting pages on . If you have one of the following DECstation Models you definitely want to choose R4xx0 for the CPU Type: @@ -1643,14 +1687,14 @@ This is a machine with a R4000 100 MHz CPU. To compile a Linux kernel that runs on these, say Y here. For details about Linux on the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - http://oss.sgi.com/mips. + . Support for Olivetti M700 CONFIG_OLIVETTI_M700 This is a machine with a R4000 100 MHz CPU. To compile a Linux kernel that runs on these, say Y here. For details about Linux on the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - http://oss.sgi.com/mips. + . Support for SGI IP22 CONFIG_SGI_IP22 @@ -1737,6 +1781,16 @@ whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. +NinjaSCSI-3 / NinjaSCSI-32Bi (16bit) PCMCIA support +CONFIG_PCMCIA_NINJA_SCSI + If you intend to attach this type of PCMCIA SCSI host adapter to + your computer, say Y here and read Documentation/README.nsp_cs. + + This driver is also available as a module called nsp_cs.o ( = + 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. + CPU type CONFIG_CPU_R3000 Please make sure to pick the right CPU type. Linux/MIPS is not @@ -1773,7 +1827,7 @@ For a general introduction to Linux networking, it is highly recommended to read the NET-3-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Socket filtering CONFIG_FILTER @@ -1898,8 +1952,8 @@ MAC address match support CONFIG_IP_NF_MATCH_MAC - mac matching allows you to match packets based on the source - ethernet address of the packet. + MAC matching allows you to match packets based on the source + Ethernet address of the packet. If you want to compile it as a module, say M here and read Documentation/modules.txt. If unsure, say `N'. @@ -2033,7 +2087,7 @@ CONFIG_IP_NF_TARGET_MARK This option adds a `MARK' target, which allows you to create rules in the `mangle' table which alter the netfilter mark (nfmark) field - associated with the packet packet prior to routing. This can change + associated with the packet prior to routing. This can change the routing method (see `IP: use netfilter MARK value as routing key') and can also be used by other subsystems to change their behavior. @@ -2188,72 +2242,6 @@ If in doubt, say N. -IP6 tables support (required for filtering/masq/NAT) -CONFIG_IP6_NF_IPTABLES - ip6tables is a general, extensible packet identification framework. - Currently only the packet filtering and packet mangling subsystem - for IPv6 use this, but connection tracking is going to follow. - Say 'Y' or 'M' here if you want to use either of those. - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -IPv6 limit match support -CONFIG_IP6_NF_MATCH_LIMIT - limit matching allows you to control the rate at which a rule can be - matched: mainly useful in combination with the LOG target ("LOG - target support", below) and to avoid some Denial of Service attacks. - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -MAC address match support -CONFIG_IP6_NF_MATCH_MAC - mac matching allows you to match packets based on the source - ethernet address of the packet. - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -netfilter mark match support -CONFIG_IP6_NF_MATCH_MARK - Netfilter mark matching allows you to match packets based on the - `nfmark' value in the packet. This can be set by the MARK target - (see below). - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -Packet filtering -CONFIG_IP6_NF_FILTER - Packet filtering defines a table `filter', which has a series of - rules for simple packet filtering at local input, forwarding and - local output. See the man page for iptables(8). - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -Packet mangling -CONFIG_IP6_NF_MANGLE - This option adds a `mangle' table to iptables: see the man page for - iptables(8). This table is used for various packet alterations - which can effect how the packet is routed. - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -MARK target support -CONFIG_IP6_NF_TARGET_MARK - This option adds a `MARK' target, which allows you to create rules - in the `mangle' table which alter the netfilter mark (nfmark) field - associated with the packet packet prior to routing. This can change - the routing method (see `IP: use netfilter MARK value as routing - key') and can also be used by other subsystems to change their - behavior. - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - SYN flood protection CONFIG_SYN_COOKIES Normal TCP/IP networking is open to an attack known as "SYN @@ -2269,7 +2257,7 @@ is no need for the legitimate users to change their TCP/IP software; SYN cookies work transparently to them. For technical information about SYN cookies, check out - ftp://koobera.math.uic.edu/syncookies.html . + . If you are SYN flooded, the source address reported by the kernel is likely to have been forged by the attacker; it is only reported as @@ -2298,7 +2286,7 @@ To find out what type of Alpha system you have, you may want to check out the Linux/Alpha FAQ, accessible on the WWW from - http://www.alphalinux.org . In summary: + . In summary: Alcor/Alpha-XLT AS 600 Alpha-XL XL-233, XL-266 @@ -2343,7 +2331,7 @@ which is command line driven, and ARC, which uses menus and arrow keys. Details about the Linux/Alpha booting process are contained in the Linux/Alpha FAQ, accessible on the WWW from - http://www.alphalinux.org . + . The usual way to load Linux on an Alpha machine is to use MILO (a bootloader that lets you pass command line parameters to the @@ -2416,7 +2404,7 @@ Say Y here if you have dumb serial boards other than the four standard COM 1/2/3/4 ports. This may happen if you have an AST FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto ), or other custom + from ), or other custom serial port hardware which acts similar to standard serial port hardware. If you only use the standard COM 1/2/3/4 ports, you can say N here to save some memory. You can also say Y if you have an @@ -2516,7 +2504,7 @@ For the moment, you should probably say N, unless you want to test the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ , or need to use the 810 Xserver in + , or need to use the 810 Xserver in XFree 3.3.6. This driver is available as a module. If you want to compile it as a @@ -2530,7 +2518,7 @@ For the moment, you should probably say N, unless you want to test the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ . + . Intel I810/I810 DC100/I810e support CONFIG_AGP_I810 @@ -2545,7 +2533,7 @@ For the moment, you should probably say N, unless you want to test the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ . + . AMD Irongate support CONFIG_AGP_AMD @@ -2554,7 +2542,7 @@ For the moment, you should probably say N, unless you want to test the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ . + . Generic SiS support CONFIG_AGP_SIS @@ -2566,21 +2554,32 @@ For the moment, you should probably say N, unless you want to test the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ . + . -ALI M1541 support +ALI chipset support CONFIG_AGP_ALI This option gives you AGP support for the GLX component of the - XFree86 4.x on the ALi M1541 chipset. + XFree86 4.x on the following ALi chipsets. The supported chipsets + include M1541, M1621, M1631, M1632, M1641,M1647,and M1651. + For ALi-chipset question, ALi welcome you refer to + - This chipset can do AGP 1x and 2x, but note that there is an + The M1541 chipset can do AGP 1x and 2x, but note that there is an acknowledged incompatibility with Matrox G200 cards. Due to timing issues, this chipset cannot do AGP 2x with the G200. This is a hardware limitation. AGP 1x seems to be fine, though. For the moment, you should probably say N, unless you want to test the GLX component for XFree86 3.3.6, which can be downloaded from - http://utah-glx.sourceforge.net/ . + . + +Support for ISA-bus hardware +CONFIG_ISA + Find out whether you have ISA slots on your motherboard. ISA is the + name of a bus system, i.e. the way the CPU talks to the other stuff + inside your box. Other bus systems are PCI, EISA, Microchannel (MCA) or + VESA. ISA is an older system, now being displaced by PCI; newer boards + don't support it. If you have ISA, say Y, otherwise N. PCI support CONFIG_PCI @@ -2590,7 +2589,7 @@ VESA. If you have PCI, say Y, otherwise N. The PCI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , contains valuable + , contains valuable information about which PCI hardware does work under Linux and which doesn't. @@ -2602,7 +2601,7 @@ VESA. If you have PCI, say Y, otherwise N. The PCI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , contains valuable + , contains valuable information about which PCI hardware does work under Linux and which doesn't. @@ -2614,7 +2613,7 @@ VESA. If you have PCI, say Y, otherwise N. The PCI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , contains valuable + , contains valuable information about which PCI hardware does work under Linux and which doesn't. @@ -2788,6 +2787,23 @@ If unsure, say Y. +PNPBIOS support +CONFIG_PNPBIOS + The PNPBIOS as defined in "Plug and Play BIOS Specification Version 1.0A + May 5, 1994" is used in Linux to autodetect built-in mainboard resources + (e.g. parallel port resources). + + Other features (e.g. change resources, ESCD, event notification, Docking + station information, ISAPNP services) are not used. + + Note: ACPI is expected to supersede PNPBIOS some day, currently it + co-exists nicely. + + See latest pcmcia-cs (stand-alone package) for a nice "lspnp" tools, + or have a look at /proc/bus/pnp. + + If unsure, say Y. + Support for hot-pluggable devices CONFIG_HOTPLUG Say Y here if you want to plug devices into your computer while @@ -2800,7 +2816,7 @@ example, used on modern desktops as well as laptops, is USB. Enable HOTPLUG and KMOD, and build a modular kernel. Get agent - software (at http://linux-hotplug.sourceforge.net) and install it. + software (at ) and install it. Then your kernel will automatically call out to a user mode "policy agent" (/sbin/hotplug) to load modules and set up software needed to use devices as you hotplug them. @@ -2817,7 +2833,7 @@ To use your PC-cards, you will need supporting software from David Hinds' pcmcia-cs package (see the file Documentation/Changes for location). Please also read the PCMCIA-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto + This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -2837,11 +2853,11 @@ If unsure, say Y. -i82365/Yenta compatible bridge support +i82365 compatible bridge support CONFIG_I82365 - Say Y here to include support for PCMCIA and CardBus host bridges - that are register compatible with the Intel i82365 and/or the Yenta - specification: this includes virtually all modern PCMCIA bridges. + Say Y here to include support for PCMCIA bridges that are register + compatible with the Intel i82365 specification: this includes virtually + all common PCMCIA bridges that are not 'Yenta' Cardbus bridges. "Bridge" is the name used for the hardware inside your computer that PCMCIA cards are plugged into. If unsure, say Y. @@ -2860,12 +2876,12 @@ and some programs won't run unless you say Y here. In particular, if you want to run the DOS emulator dosemu under Linux (read the DOSEMU-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto ), you'll need to say Y + ), you'll need to say Y here. You can find documentation about IPC with "info ipc" and also in section 6.4 of the Linux Programmer's Guide, available from - http://www.linuxdoc.org/docs.html#guide . + . BSD Process Accounting CONFIG_BSD_PROCESS_ACCT @@ -2887,7 +2903,7 @@ interface consists of a system call, but if you say Y to "/proc file system support", a tree of modifiable sysctl entries will be generated beneath the /proc/sys directory. They are explained in the - files in Documentation/sysctl/. Note that enabling this option will + files in Documentation/sysctl/ . Note that enabling this option will enlarge the kernel by at least 8 KB. As it is generally a good thing, you should say Y here unless @@ -2928,7 +2944,7 @@ want to say Y here. Information about ELF is contained in the ELF HOWTO available from - http://www.linuxdoc.org/docs.html#howto . + . If you find that after upgrading from Linux kernel 1.2 and saying Y here, you still can't run any ELF binaries (they just crash), then @@ -2983,7 +2999,7 @@ programs that need an interpreter to run like Java, Python or Emacs-Lisp. It's also useful if you often run DOS executables under the Linux DOS emulator DOSEMU (read the DOSEMU-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto ). Once you have + ). Once you have registered such a binary class with the kernel, you can start one of those programs simply by typing in its name at a shell prompt; Linux will automatically feed it to the correct interpreter. @@ -3053,7 +3069,7 @@ The program SVGATextMode can be used to utilize SVGA video cards to their full potential in text mode. Download it from - ftp://metalab.unc.edu/pub/Linux/utils/console . + . Say Y. @@ -3092,7 +3108,7 @@ You need an utility program called fbset to make full use of frame buffer devices. Please read Documentation/fb/framebuffer.txt and the Framebuffer-HOWTO at - http://www.tahallah.demon.co.uk/programming/prog.html for more + for more information. Say Y here and to the driver for your graphics board below if you @@ -3110,6 +3126,26 @@ hardware found in Acorn RISC PCs and other ARM-based machines. If unsure, say N. +Permedia2 support +CONFIG_FB_PM2 + This is the frame buffer device driver for the Permedia2 AGP frame buffer + card from ASK, aka `Graphic Blaster Exxtreme'. There is a product page + at . + +Enable FIFO disconnect feature +CONFIG_FB_PM2_FIFO_DISCONNECT + Support the Permedia2 FIFOI disconnect feature (see CONFIG_FB_PM2). + +Generic Permedia2 PCI board support +CONFIG_FB_PM2_PCI + Say Y to enable support for Permedia2 AGP frame buffer card from 3Dlabs + (aka `Graphic Blaster Exxtreme') on the PCI bus. + +Phase5 CVisionPPC/BVisionPPC support +CONFIG_FB_PM2_CVPPC + Say Y to enable support for the Amiga Phase 5 CVisionPPC BVisionPPC + framebuffer cards. Phase 5 is no longer with us, alas. + Amiga native chipset support CONFIG_FB_AMIGA This is the frame buffer device driver for the builtin graphics @@ -3184,10 +3220,6 @@ Say N unless you have such a graphics board or plan to get one before you next recompile the kernel. -Permedia2 support (EXPERIMENTAL) -CONFIG_FB_PM2 - Say Y here if this is your graphics board. - Apollo support CONFIG_APOLLO Say Y here if you want to run Linux on an MC680x0-based Apollo @@ -3205,6 +3237,11 @@ This is the frame buffer device driver for the builtin graphics chipset found in Ataris. +Amiga FrameMaster II/Rainbow II support +CONFIG_FB_FM2 + This is the frame buffer device driver for the Amiga FrameMaster + card from BSC (exhibited 1992 but not shipped as a CBM product). + Open Firmware frame buffer device support CONFIG_FB_OF Say Y if you want support with Open Firmware for your graphics @@ -3256,6 +3293,11 @@ module will be called aty128fb.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +Link-Up Systems LCD support (EXPERIMENTAL) +CONFIG_FB_L7200 + This driver supports the L7200 Color LCD. + Say Y if you want graphics support. + PowerMac "control" frame buffer device support CONFIG_FB_CONTROL This driver supports a frame buffer for the graphics adapter in the @@ -3451,7 +3493,7 @@ also load i2c-matroxfb to get it to run. The driver starts in monitor mode and you must use the matroxset - tool (available at ftp://platan.vc.cvut.cz/pub/linux/matrox-latest) + tool (available at ) to switch it to PAL or NTSC or to swap primary and secondary head outputs. Secondary head driver also always start in 640x480 resolution, you must use fbset to change it. @@ -3476,7 +3518,7 @@ The driver starts in monitor mode and currently does not support output in TV modes. You must use the matroxset tool (available - at ftp://platan.vc.cvut.cz/pub/linux/matrox-latest) to swap primary + at ) to swap primary and secondary head outputs. Secondary head driver always start in 640x480 resolution and you must use fbset to change it. @@ -3541,11 +3583,46 @@ CONFIG_FB_CGTHREE This is the frame buffer device driver for the CGthree frame buffer. +CGfourteen (SX) support +CONFIG_FB_CGFOURTEEN + This is the frame buffer device driver for the CGfourteen frame buffer + on Desktop SPARCsystems with the SX graphics option. + +P9100 (Sparcbook 3 only) support +CONFIG_FB_P9100 + This is the frame buffer device driver for the P9100 card supported + on Sparcbook 3 machines. + +Leo (ZX) support +CONFIG_FB_LEO + This is the frame buffer device driver for the SBUS-based Sun ZX (leo) + frame buffer cards. + +IGA 168x display support +CONFIG_FB_IGA + This is the framebuffer device for the INTERGRAPHICS 1680 and + successor frame buffer cards. + TCX (SS4/SS5 only) support CONFIG_FB_TCX This is the frame buffer device driver for the TCX 24/8bit frame buffer. +HD64461 Frame Buffer support +CONFIG_FB_HIT + This is the frame buffer device driver for the Hitachi HD64461 LCD frame + buffer card. + +SIS 630/540 display support +CONFIG_FB_SIS + This is the frame buffer device driver for the SiS 630 and 640 Super + Socket 7 UMA cards. Specs available at . + +IMS Twin Turbo display support +CONFIG_FB_IMSTT + The IMS Twin Turbo is a PCI-based frame buffer card bundled with many + Macintosh and compatible computers. + Virtual Frame Buffer support (ONLY FOR TESTING!) CONFIG_FB_VIRTUAL This is a `virtual' frame buffer device. It operates on a chunk of @@ -3566,7 +3643,7 @@ CONFIG_FB_SA1100 This is a framebuffer device for the SA-1100 LCD Controller. - See http://www.linux-fbdev.org/ for information on framebuffer + See for information on framebuffer devices. If you plan to use the LCD display with your SA-1100 system, say @@ -3684,7 +3761,7 @@ Documentation/parport.txt and drivers/parport/BUGS-parport. For extensive information about drivers for many devices attaching - to the parallel port see http://www.torque.net/linux-pp.html on the + to the parallel port see on the WWW. It is possible to share a single parallel port among several devices @@ -3740,12 +3817,6 @@ other non-standard types of parallel ports. This causes a performance loss, so most people say N. -Sun Ultra/AX-style hardware -CONFIG_PARPORT_AX - Say Y here if you need support for the parallel port hardware on Sun - Ultra/AX machines. This code is also available as a module (say M), - called parport_ax.o. If in doubt, saying N is the safe plan. - Amiga built-in parallel port support CONFIG_PARPORT_AMIGA Say Y here if you need support for the parallel port hardware on @@ -3850,13 +3921,13 @@ For an excellent introduction to Linux networking, please read the NET-3-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This option is also necessary if you want to use the full power of term (term is a program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected Unix computer; for more information, read - http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html ). + ). If you say Y here and also to "/proc file system support" and "Sysctl support" below, you can change various aspects of the @@ -3873,7 +3944,7 @@ intend to participate in the MBONE, a high bandwidth network on top of the Internet which carries audio and video broadcasts. More information about the MBONE is on the WWW at - http://www-itg.lbl.gov/mbone/ . Information about the multicast + . Information about the multicast capabilities of the various network cards is contained in Documentation/networking/multicast.txt. For most people, it's safe to say N. @@ -3927,9 +3998,9 @@ addresses of forwarded packets. If you are interested in this, please see the preliminary - documentation at http://www.compendium.com.ar/policy-routing.txt and - ftp://post.tepkom.ru/pub/vol2/Linux/docs/advanced-routing.tex . You - will need supporting software from ftp://ftp.inr.ac.ru/ip-routing/ + documentation at and + . You + will need supporting software from . If unsure, say N. @@ -3976,7 +4047,7 @@ destination addresses of packets that pass through it, in a manner you specify. General information about Network Address Translation can be gotten from the document - http://www.csn.tu-chemnitz.de/~mha/linux-ip-nat/diplom/nat.html + IP: kernel level autoconfiguration CONFIG_IP_PNP @@ -4001,6 +4072,21 @@ want to use BOOTP, a BOOTP server must be operating on your network. Read Documentation/nfsroot.txt for details. +DHCP support +CONFIG_IP_PNP_DHCP + If you want your Linux box to mount its whole root filesystem (the + one containing the directory /) from some other computer over the + net via NFS and you want the IP address of your computer to be + discovered automatically at boot time using the DHCP protocol (a + special protocol designed for doing this job), say Y here. In case + the boot ROM of your network card was designed for booting Linux and + does DHCP itself, providing all necessary information on the kernel + command line, you can say N here. + + If unsure, say Y. Note that if you want to use DHCP, a DHCP server + must be operating on your network. Read Documentation/nfsroot.txt + for details. + RARP support CONFIG_IP_PNP_RARP If you want your Linux box to mount its whole root file system (the @@ -4022,7 +4108,7 @@ appear on a different network than it physically is, or to use mobile-IP facilities (allowing laptops to seamlessly move between networks without changing their IP addresses; check out - http://anchor.cs.binghamton.edu/~mobileip/LJ/index.html ). + ). Saying Y to this option will produce two modules ( = code which can be inserted in and removed from the running kernel whenever you @@ -4063,7 +4149,7 @@ Kernel side support for Sparse Mode PIM (Protocol Independent Multicast) version 1. This multicast routing protocol is used widely because Cisco supports it. You need special software to use it - (pimd-v1). Please see http://netweb.usc.edu/pim/ for more + (pimd-v1). Please see for more information about PIM. Say Y if you want to use PIM-SM v1. Note that you can say N here if @@ -4076,41 +4162,6 @@ gated-5). This routing protocol is not used widely, so say N unless you want to play with it. -PC/TCP compatibility mode -CONFIG_INET_PCTCP - If you have been having difficulties telnetting to your Linux - machine from a DOS system that uses (broken) PC/TCP networking - software (all versions up to OnNet 2.0) over your local Ethernet try - saying Y here. Everyone else says N. - - People having problems with NCSA telnet should see the file - Documentation/networking/ncsa-telnet. - -Path MTU Discovery (normally enabled) -CONFIG_PATH_MTU_DISCOVERY - MTU (maximal transfer unit) is the size of the chunks we send out - over the net. "Path MTU Discovery" means that, instead of always - sending very small chunks, we start out sending big ones and if we - then discover that some host along the way likes its chunks smaller, - we adjust to a smaller size. This is good, so most people say Y - here. - - However, some DOS software (versions of DOS NCSA telnet and Trumpet - Winsock in PPP mode) is broken and won't be able to connect to your - Linux machine correctly in all cases (especially through a terminal - server) unless you say N here. See - Documentation/networking/ncsa-telnet for the location of fixed NCSA - telnet clients. If in doubt, say Y. - -Disable NAGLE algorithm (normally enabled) -CONFIG_TCP_NAGLE_OFF - The NAGLE algorithm works by requiring an acknowledgment before - sending small IP frames (packets). This keeps tiny telnet and - rlogin packets from congesting Wide Area Networks. Most people - strongly recommend to say N here, thereby leaving NAGLE - enabled. Those programs that would benefit from disabling this - facility can do it on a per connection basis themselves. - IP: Allow large windows (not recommended if <16 MB of memory) CONFIG_SKB_LARGE On high speed, long distance networks the performance limit on @@ -4154,9 +4205,9 @@ Features of this new protocol include: expanded address space, authentication and privacy, and seamless interoperability with the current version of IP (IP version 4). For general information about - IPv6, see http://playground.sun.com/pub/ipng/html/ipng-main.html ; + IPv6, see ; for specific information about IPv6 under Linux read the HOWTO at - http://www.bieringer.de/linux/IPv6/ and the file net/ipv6/README in + and the file net/ipv6/README in the kernel source. If you want to use IPv6, please upgrade to the newest net-tools as @@ -4202,9 +4253,9 @@ used for local networks of Windows machines. You need it if you want to access Novell NetWare file or print servers using the Linux Novell client ncpfs (available from - ftp://metalab.unc.edu/pub/Linux/system/filesystems/ ) or from within + ) or from within the Linux DOS emulator DOSEMU (read the DOSEMU-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto ). In order to do the + ). In order to do the former, you'll also have to say Y to "NCP file system support", below. @@ -4214,13 +4265,13 @@ To turn your Linux box into a fully featured NetWare file server and IPX router, say Y here and fetch either lwared from - ftp://metalab.unc.edu/pub/Linux/system/network/daemons/ or mars_nwe - from ftp://ftp.gwdg.de/pub/linux/misc/ncpfs . For more information, + or mars_nwe + from . For more information, read the IPX-HOWTO available from - http://www.linuxdoc.org/docs.html#howto . + . General information about how to connect Linux, Windows machines and - Macs is on the WWW at http://www.eats.com/linux_mac_win.html . + Macs is on the WWW at . The IPX driver would enlarge your kernel by about 16 KB. This driver is also available as a module ( = code which can be inserted in and @@ -4238,7 +4289,7 @@ same address). The way this is done is to create a virtual internal "network" inside your box and to assign an IPX address to this network. Say Y here if you want to do this; read the IPX-HOWTO at - http://www.linuxdoc.org/docs.html#howto for details. + for details. The full internal IPX network enables you to allocate sockets on different virtual nodes of the internal network. This is done by @@ -4251,12 +4302,15 @@ 'special' sockets to sockets listening on the primary network is disabled. This might break existing applications, especially RIP/SAP daemons. A RIP/SAP daemon that works well with the full internal net - can be found on ftp://ftp.gwdg.de/pub/linux/misc/ncpfs . + can be found on . If you don't know what you are doing, say N. IPX: SPX networking (EXPERIMENTAL) CONFIG_SPX + * Orphaned entry retained 20 April 2001 by Petr Vandrovec * + * If you read this note from the configurator, please contact * + * the Configure.help maintainer listed in MAINTAINERS * The Sequenced Packet eXchange protocol is a transport layer protocol built on top of IPX. It is used in Novell NetWare systems for client-server applications and is similar to TCP (which runs on top @@ -4268,7 +4322,7 @@ space programs lwared or mars_nwe for the server side). Say Y here if you have use for SPX; read the IPX-HOWTO at - http://www.linuxdoc.org/docs.html#howto for details. + for details. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -4284,7 +4338,7 @@ To find some tools to use with the kernel layer support, please look at Patrick Caulfield's web site: - http://linux.dreamtime.org/decnet/ + More detailed documentation is available in the Documentation/networking/decnet.txt file. @@ -4314,7 +4368,7 @@ network link driver", "Routing messages" and "Network packet filtering". The first two are required to allow configuration via rtnetlink (currently you need Alexey Kuznetsov's iproute2 package - from ftp://ftp.inr.ac.ru). The "Network packet filtering" option + from ). The "Network packet filtering" option will be required for the forthcoming routing daemon to work. See Documentation/networking/decnet.txt for more information. @@ -4338,15 +4392,15 @@ want to join the conversation, say Y. You will need to use the netatalk package so that your Linux box can act as a print and file server for Macs as well as access AppleTalk printers. Check out - http://www.zettabyte.net/netatalk/ on the WWW for details. EtherTalk + on the WWW for details. EtherTalk is the name used for AppleTalk over Ethernet and the cheaper and slower LocalTalk is AppleTalk over a proprietary Apple network using serial links. EtherTalk and LocalTalk are fully supported by Linux. General information about how to connect Linux, Windows machines and - Macs is on the WWW at http://www.eats.com/linux_mac_win.html . The + Macs is on the WWW at . The NET-3-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , contains valuable + , contains valuable information as well. This driver is also available as a module ( = code which can be @@ -4434,9 +4488,9 @@ Amateur Radio support CONFIG_HAMRADIO If you want to connect your Linux box to an amateur radio, answer Y - here. You want to read http://www.tapr.org/tapr/html/pkthome.html + here. You want to read and the HAM-HOWTO and the AX25-HOWTO, both available from - http://www.linuxdoc.org/docs.html#howto . + . Note that the answer to this question won't directly affect the kernel: saying N will just cause this configure script to skip all @@ -4460,10 +4514,10 @@ Information about where to get supporting software for Linux amateur radio as well as information about how to configure an AX.25 port is contained in the AX25-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . You might also want to + . You might also want to check out the file Documentation/networking/ax25.txt in the kernel source. More information about digital amateur radio in general is - on the WWW at http://www.tapr.org/tapr/html/pkthome.html . + on the WWW at . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -4496,10 +4550,10 @@ A comprehensive listing of all the software for Linux amateur radio users as well as information about how to configure an AX.25 port is contained in the AX25-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . You also might want to + . You also might want to check out the file Documentation/networking/ax25.txt. More information about digital amateur radio in general is on the WWW at - http://www.tapr.org/tapr/html/pkthome.html . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -4515,10 +4569,10 @@ A comprehensive listing of all the software for Linux amateur radio users as well as information about how to configure an AX.25 port is contained in the AX25-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . You also might want to + . You also might want to check out the file Documentation/networking/ax25.txt. More information about digital amateur radio in general is on the WWW at - http://www.tapr.org/tapr/html/pkthome.html . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -4579,7 +4633,7 @@ Currently, this driver supports Ottawa PI/PI2, Paccomm/Gracilis PackeTwin, and S5SCC/DMA boards. They are detected automatically. If you have one of these cards, say Y here and read the AX25-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . This driver can operate multiple boards simultaneously. If you compile it as a module (by saying M instead of Y), it will be called @@ -4595,7 +4649,7 @@ certain parameters, such as channel access timing, clock mode, and DMA channel. This is accomplished with a small utility program, dmascc_cfg, available at - http://www.nt.tuwien.ac.at/~kkudielk/Linux/ . Please be sure to get + . Please be sure to get at least version 1.27 of dmascc_cfg, as older versions will not work with the current driver. @@ -4604,7 +4658,7 @@ These cards are used to connect your Linux box to an amateur radio in order to communicate with other computers. If you want to use this, read Documentation/networking/z8530drv.txt and the AX25-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . Also + available from . Also make sure to say Y to "Amateur Radio AX.25 Level 2" support. If you want to compile this as a module ( = code which can be @@ -4641,7 +4695,7 @@ connect to a parallel interface. The driver supports the picpar and par96 designs. To configure the driver, use the sethdlc utility available in the standard ax25 utilities package. For information on - the modems, see http://www.baycom.de and the file + the modems, see and the file Documentation/networking/baycom.txt. If you want to compile this driver as a module ( = code which can be @@ -4655,7 +4709,7 @@ connect to a parallel interface. The driver supports the EPP designs. To configure the driver, use the sethdlc utility available in the standard ax25 utilities package. For information on the - modems, see http://www.baycom.de and the file + modems, see and the file Documentation/networking/baycom.txt. If you want to compile this driver as a module ( = code which can be @@ -4674,7 +4728,7 @@ driver and still provided in case this driver does not work with your serial interface chip. To configure the driver, use the sethdlc utility available in the standard ax25 utilities package. For - information on the modems, see http://www.baycom.de and + information on the modems, see and Documentation/networking/baycom.txt. If you want to compile this driver as a module ( = code which can be @@ -4691,7 +4745,7 @@ the full duplex driver. This driver is depreciated. To configure the driver, use the sethdlc utility available in the standard ax25 utilities package. For information on the modems, see - http://www.baycom.de and Documentation/networking/baycom.txt. + and Documentation/networking/baycom.txt. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -4708,7 +4762,7 @@ To configure the driver, use the sethdlc, smdiag and smmixer utilities available in the standard ax25 utilities package. For information on how to key the transmitter, see - http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html and + and Documentation/networking/soundmodem.txt. If you want to compile this driver as a module ( = code which can be @@ -4805,8 +4859,8 @@ if you want that) and the lower level data link layer protocol LAPB (say Y to "LAPB Data Link Driver" below if you want that). - You can read more about X.25 at http://www.sangoma.com/x25.htm and - http://www.cisco.com/univercd/data/doc/software/11_0/rpcg/cx25.htm . + You can read more about X.25 at and + . Information about X.25 for Linux is contained in the files Documentation/networking/x25.txt and Documentation/networking/x25-iface.txt. @@ -4866,8 +4920,8 @@ - etc... For more informations, please refer to: - http://www.freshmeat.net/projects/etherdivert - http://perso.wanadoo.fr/magpie/EtherDivert.html + + If unsure, say N @@ -4888,7 +4942,7 @@ Note that if your box acts as a bridge, it probably contains several Ethernet devices, but the kernel is not able to recognize more than one at boot time without help; for details read the Ethernet-HOWTO, - available from in http://www.linuxdoc.org/docs.html#howto . + available from in . If you want to compile this code as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -4927,7 +4981,7 @@ Over this socket, the kernel can send and receive datagrams carrying information. It is documented on many systems in netlink(7), a HOWTO is provided as well, for example on - http://snafu.freedom.org/linux2.2/docs/netlink-HOWTO.html + So far, the kernel uses this feature to publish some network related information if you say Y to "Routing messages", below. You also need @@ -4943,7 +4997,7 @@ CONFIG_RTNETLINK If you say Y here, user space programs can receive some network related routing information over the netlink. 'rtmon', supplied - with the iproute2 package (ftp://ftp.inr.ac.ru), can read and + with the iproute2 package (), can read and interpret this data. Information sent to the kernel over this link is ignored. @@ -5253,7 +5307,7 @@ isapnp support. Please read Documentation/telephony/ixj.txt. For more information on these cards, see Quicknet's web site at: - http://www.quicknet.net/ . + . If you do not have any Quicknet telephony cards, you can safely say N here. @@ -5338,6 +5392,117 @@ the performances of the driver, and the size of your syslog files! Keep the debugging level to 0 during normal operations. +Fusion MPT device support +CONFIG_FUSION + LSI Logic Fusion(TM) Message Passing Technology (MPT) device support + provides high performance SCSI host initiator, and LAN [1] interface + services to a host system. The Fusion architecture is capable of + duplexing these protocols on high-speed Fibre Channel + (up to 2 GHz x 2 ports = 4 GHz) and parallel SCSI (up to Ultra-320) + physical medium. + + [1] LAN is not supported on parallel SCSI medium. + + These drivers require a Fusion MPT compatible PCI adapter installed in + the host system. MPT adapters contain specialized I/O processors + to handle I/O workload, and more importantly to offload this work + from the host CPU(s). + + If you have Fusion MPT hardware and want to use it, you can say + Y or M here to add MPT (base + ScsiHost) drivers. + = build lib (fusion.o), and link [static] into the kernel [2] proper + = compiled as [dynamic] modules [3] named: (mptbase.o, mptscsih.o) + + [2] In order enable capability to boot the linux kernel natively + from a Fusion MPT target device, you MUST answer Y here! + (currently requires CONFIG_BLK_DEV_SD) + [3] This support 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 as modules, say M here and read + Documentation/modules.txt. + + If unsure, say N. + + If you say Y or M here you will get a choice of these + additional protocol and support module options: Module Name: + Enhanced SCSI error reporting (isense.o) + Fusion MPT misc device (ioctl) driver (mptctl.o) + Fusion MPT LAN driver (mptlan.o) + + --- + Fusion MPT is trademark of LSI Logic Corporation, and it's architecture + is based on LSI Logic's Message Passing Interface (MPI) specification. + +Fusion MPT enhanced SCSI error reporting [optional] module +CONFIG_FUSION_ISENSE + The isense module (roughly stands for Interpret SENSE data) is + completely optional. It simply provides extra English readable + strings in SCSI Error Report(s) that might be generated from the + Fusion MPT SCSI Host driver, for example when a target device + returns a SCSI check condition on a I/O. Without this module + loaded you might see: + + SCSI Error Report =-=-= (ioc0,scsi5:0) + SCSI_Status=02h (CHECK_CONDITION) + Original_CDB[]: 2A 00 00 00 00 41 00 00 02 00 + SenseData[12h]: 70 00 02 00 00 00 00 0A 00 00 00 00 04 02 02 00 00 00 + SenseKey=2h (NOT READY); FRU=02h + ASC/ASCQ=29h/00h + + Where otherwise, if this module had been loaded, you would see: + + SCSI Error Report =-=-= (ioc0,scsi5:0) + SCSI_Status=02h (CHECK_CONDITION) + Original_CDB[]: 2A 00 00 00 00 41 00 00 02 00 - "WRITE(10)" + SenseData[12h]: 70 00 02 00 00 00 00 0A 00 00 00 00 04 02 02 00 00 00 + SenseKey=2h (NOT READY); FRU=02h + ASC/ASCQ=29h/00h "LOGICAL UNIT NOT READY, INITIALIZING CMD. REQUIRED" + + Say M for "Enhanced SCSI error reporting" to compile this optional module, + creating a driver named: isense.o + + NOTE: Support for building this feature into the kernel is not + available, due to kernel size considerations. + +Fusion MPT misc device (ioctl) driver [optional] module +CONFIG_FUSION_CTL + The Fusion MPT misc device driver provides specialized control + of MPT adapters via system ioctl calls. Use of ioctl calls to + the MPT driver requires that you create and use a misc device + node ala: + mknod /dev/mptctl c 10 240 + + One use of this ioctl interface is to perform an upgrade (reflash) + of the MPT adapter firmware. Refer to readme file(s) distributed + with the Fusion MPT linux driver for additional details. + + If enabled by saying M to this, a driver named: mptctl.o + will be compiled. + + If unsure whether you really want or need this, say no. + +Fusion MPT LAN driver [optional] +CONFIG_FUSION_LAN + This module supports LAN IP traffic over Fibre Channel port(s) + on Fusion MPT compatible hardware (LSIFC9xx chips). + The physical interface used is defined in RFC 2625. + Please refer to that document for details. + + Installing this driver requires the knowledge to configure and + activate a new network interface, "fc0", using standard Linux tools. + + If enabled by saying M to this, a driver named: mptlan.o + will be compiled. + + If unsure whether you really want or need this, say no. + + NOTES: This feature is NOT available nor supported for linux-2.2.x + kernels. You must be building a linux-2.3.x or linux-2.4.x kernel + in order to configure this option. + Support for building this feature into the linux kernel is not + yet available. + SCSI support? CONFIG_SCSI If you want to use a SCSI hard disk, SCSI tape drive, SCSI CDROM or @@ -5350,7 +5515,7 @@ port version of the 100 MB IOMEGA ZIP drive. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . The + . The SCSI-Programming-HOWTO contains information about how to add or remove an SCSI device from a running Linux machine without rebooting. @@ -5368,7 +5533,7 @@ If you want to use a SCSI hard disk or the SCSI or parallel port version of the IOMEGA ZIP drive under Linux, say Y and read the SCSI-HOWTO, the Disk-HOWTO and the Multi-Disk-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . This is NOT for SCSI + . This is NOT for SCSI CDROMs. This driver is also available as a module ( = code which can be @@ -5394,11 +5559,25 @@ If you don't understand what's going on, go with the default. +Extra SCSI Tapes +CONFIG_ST_EXTRA_DEVS + This controls the amount of additional space allocated in tables for + drivers that are loaded as modules after the kernel is booted. In + the event that the SCSI core itself was loaded as a module, this + value is the number of additional tapes that can be loaded after the + first host driver is loaded. + + Admittedly this isn't pretty, but there are tons of race conditions + involved with resizing the internal arrays on the fly. Someday this + flag will go away, and everything will work automatically. + + If you don't understand what's going on, go with the default. + SCSI tape support CONFIG_CHR_DEV_ST If you want to use a SCSI tape drive under Linux, say Y and read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , and + , and drivers/scsi/README.st in the kernel source. This is NOT for SCSI CDROMs. @@ -5419,10 +5598,10 @@ commands for tapes (QIC-157) and can be driven by the standard driver st. For more information, you may have a look at the SCSI-HOWTO - ftp://metalab.unc.edu/pub/Linux/docs/HOWTO and + and drivers/scsi/README.osst in the kernel source. More info on the OnStream driver may be found on - http://linux1.onstream.nl/test/ + Please also have a look at the standard st docu, as most of it applies to osst as well. @@ -5436,7 +5615,7 @@ CONFIG_BLK_DEV_SR If you want to use a SCSI CDROM under Linux, say Y and read the SCSI-HOWTO and the CDROM-HOWTO at - http://www.linuxdoc.org/docs.html#howto . Also make sure to say Y + . Also make sure to say Y or M to "ISO 9660 CDROM file system support" later. This driver is also available as a module ( = code which can be @@ -5474,12 +5653,12 @@ directly, so you need some additional software which knows how to talk to these devices using the SCSI protocol: - For scanners, look at SANE (http://www.mostang.com/sane). For CD + For scanners, look at SANE (). For CD writer software look at cdrecord - (http://www.fokus.gmd.de/research/cc/glone/employees/joerg.schilling/private/cdrecord.html) + () and for burning a "disk at once": cdrdao - (http://www.ping.de/sites/daneb/cdrdao.html). Cdparanoia is a high - quality digital reader of audio CDs (http://www.xiph.org/paranoia). + (). Cdparanoia is a high + quality digital reader of audio CDs (). For other devices, it's possible that you'll have to write the driver software yourself. Please read the file Documentation/scsi-generic.txt for more information. @@ -5498,7 +5677,7 @@ This will typically cause the kernel to panic if an error is detected, but it would have probably crashed if the panic weren't there. Comments/questions/problems to linux-scsi mailing list - please. See http://www.andante.org/scsi_queue.html for more + please. See for more up-to-date information. Probe all LUNs on each SCSI device @@ -5558,7 +5737,7 @@ must be manually specified in this case. It is explained in section 3.3 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . You might also want to + . You might also want to read the file drivers/scsi/README.aha152x. This driver is also available as a module ( = code which can be @@ -5570,7 +5749,7 @@ CONFIG_SCSI_AHA1542 This is support for a SCSI host adapter. It is explained in section 3.4 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Note that Trantor was + . Note that Trantor was purchased by Adaptec, and some former Trantor products are being sold under the Adaptec name. If it doesn't work out of the box, you may have to change some settings in drivers/scsi/aha1542.h. @@ -5584,7 +5763,7 @@ CONFIG_SCSI_AHA1740 This is support for a SCSI host adapter. It is explained in section 3.5 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + . If it doesn't work out of the box, you may have to change some settings in drivers/scsi/aha1740.h. @@ -5658,7 +5837,7 @@ configuration options. You should read drivers/scsi/aic7xxx_old/README.aic7xxx at a minimum before contacting the maintainer with any questions. The SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , can also be of great + , can also be of great help. If you want to compile this driver as a module ( = code which can be @@ -5737,7 +5916,7 @@ IBM ServeRAID Support CONFIG_SCSI_IPS This is support for the IBM ServeRAID hardware RAID controllers. - See http://www.developer.ibm.com/welcome/netfinity/serveraid.html + See for more information. If this driver does not work correctly without modification please contact the author by email at ipslinux@us.ibm.com. @@ -5752,7 +5931,7 @@ CONFIG_SCSI_BUSLOGIC This is support for BusLogic MultiMaster and FlashPoint SCSI Host Adapters. Consult the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , and the files + , and the files README.BusLogic and README.FlashPoint in drivers/scsi for more information. If this driver does not work correctly without modification, please contact the author, Leonard N. Zubkoff, by @@ -5784,7 +5963,7 @@ CONFIG_SCSI_DTC3280 This is support for DTC 3180/3280 SCSI Host Adapters. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , and the file + , and the file drivers/scsi/README.dtc3x80. This driver is also available as a module ( = code which can be @@ -5801,7 +5980,7 @@ Note that this driver is obsolete; if you have one of the above SCSI Host Adapters, you should normally say N here and Y to "EATA ISA/EISA/PCI support", below. Please read the SCSI-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + from . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -5815,7 +5994,7 @@ host adapters could also use this driver but are discouraged from doing so, since this driver only supports hard disks and lacks numerous features. You might want to have a look at the SCSI-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -5829,7 +6008,7 @@ this hardware. If the driver doesn't work out of the box, you may have to change some settings in drivers/scsi/u14-34f.c. Read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Note that there is also + . Note that there is also another driver for the same hardware: "UltraStor SCSI support", below. You should say Y to both only if you want 24F support as well. @@ -5864,7 +6043,7 @@ other adapters based on the Future Domain chipsets (Quantum ISA-200S, ISA-250MG; Adaptec AHA-2920A; and at least one IBM board). It is explained in section 3.7 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . NOTE: Newer Adaptec AHA-2920C boards use the Adaptec AIC-7850 chip and should use the aic7xxx driver ("Adaptec AIC7xxx chipset SCSI @@ -5894,7 +6073,7 @@ This is the generic NCR family of SCSI controllers, not to be confused with the NCR 53c7 or 8xx controllers. It is explained in section 3.8 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + . If it doesn't work out of the box, you may have to change some settings in drivers/scsi/g_NCR5380.h. @@ -5918,12 +6097,21 @@ port or memory mapped. You should know what you have. The most common card, Trantor T130B, uses port mapped mode. +NCR Dual 700 MCA SCSI support +CONFIG_SCSI_NCR_D700 + This is a driver for the Microchannel Dual 700 card produced by + NCR and commonly used in 345x/35xx/4100 class machines. It always + tries to negotiate sync and uses tag command queueing. + + Unless you have an NCR manufactured machine, the chances are that you do + not have this SCSI card, so say N. + NCR53c7,8xx SCSI support CONFIG_SCSI_NCR53C7xx This is a driver for the 53c7 and 8xx NCR family of SCSI controllers, not to be confused with the NCR 5380 controllers. It is explained in section 3.8 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + . If it doesn't work out of the box, you may have to change some settings in drivers/scsi/53c7,8xx.h. Please read drivers/scsi/README.ncr53c7xx for the available boot time command line options. @@ -6221,7 +6409,7 @@ CONFIG_SCSI_INITIO This is support for the Initio 91XXU(W) SCSI host adapter. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -6232,7 +6420,7 @@ CONFIG_SCSI_PAS16 This is support for a SCSI host adapter. It is explained in section 3.10 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + . If it doesn't work out of the box, you may have to change some settings in drivers/scsi/pas16.h. @@ -6245,7 +6433,7 @@ CONFIG_SCSI_INIA100 This is support for the Initio INI-A100U2W SCSI host adapter. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -6256,7 +6444,7 @@ CONFIG_SCSI_PCI2000 This is support for the PCI2000I EIDE interface card which acts as a SCSI host adapter. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module called pci2000.o ( = code which can be inserted in and removed from the running kernel @@ -6267,7 +6455,7 @@ CONFIG_SCSI_PCI2220I This is support for the PCI2220i EIDE interface card which acts as a SCSI host adapter. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module called pci2220i.o ( = code which can be inserted in and removed from the running kernel @@ -6278,7 +6466,7 @@ CONFIG_SCSI_PSI240I This is support for the PSI240i EIDE interface card which acts as a SCSI host adapter. Please read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module called psi240i.o ( = code which can be inserted in and removed from the running kernel @@ -6297,7 +6485,7 @@ Information about this driver is contained in drivers/scsi/README.qlogicfas. You should also read the SCSI-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -6315,7 +6503,7 @@ Please read the file drivers/scsi/README.qlogicisp. You should also read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -6344,7 +6532,7 @@ CONFIG_SCSI_SEAGATE These are 8-bit SCSI controllers; the ST-01 is also supported by this driver. It is explained in section 3.9 of the SCSI-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . If it + available from . If it doesn't work out of the box, you may have to change some settings in drivers/scsi/seagate.h. @@ -6357,7 +6545,7 @@ CONFIG_SCSI_T128 This is support for a SCSI host adapter. It is explained in section 3.11 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + . If it doesn't work out of the box, you may have to change some settings in drivers/scsi/t128.h. Note that Trantor was purchased by Adaptec, and some former Trantor products are being sold under the Adaptec name. @@ -6372,7 +6560,7 @@ This is support for the UltraStor 14F, 24F and 34F SCSI-2 host adapter family. This driver is explained in section 3.12 of the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . If it doesn't work out + . If it doesn't work out of the box, you may have to change some settings in drivers/scsi/ultrastor.h. @@ -6413,7 +6601,7 @@ You want to read the start of drivers/scsi/eata.c and the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Note that there is also another driver for the same hardware available: "EATA-DMA support". You should say Y to only one of them. @@ -6453,7 +6641,7 @@ This is support for the NCR53c406a SCSI host adapter. For user configurable parameters, check out drivers/scsi/NCR53c406.c in the kernel source. Also read the SCSI-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -6535,7 +6723,7 @@ CONFIG_SCSI_AM53C974 This is support for the AM53/79C974 SCSI host adapters. Please read drivers/scsi/README.AM53C974 for details. Also, the SCSI-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto , is for + available from , is for you. Note that there is another driver for AM53C974 based adapters: @@ -6585,7 +6773,7 @@ For more information about this driver and how to use it you should read the file drivers/scsi/README.ppa. You should also read the SCSI-HOWTO, which is available from - http://www.linuxdoc.org/docs.html#howto . If you use this driver, + . If you use this driver, you will still be able to use the parallel port for other tasks, such as a printer; it is safe to compile both drivers into the kernel. @@ -6612,7 +6800,7 @@ For more information about this driver and how to use it you should read the file drivers/scsi/README.ppa. You should also read the SCSI-HOWTO, which is available from - http://www.linuxdoc.org/docs.html#howto . If you use this driver, + . If you use this driver, you will still be able to use the parallel port for other tasks, such as a printer; it is safe to compile both drivers into the kernel. @@ -6647,66 +6835,6 @@ Generally, saying N is fine. -Parallel port SCSI device support -CONFIG_PPSCSI - There are many external CD-ROM and disk devices that connect through - your computer's parallel port. Lots of them are actually SCSI - devices using a parallel port SCSI adapter. This option enables the - ppSCSI subsystem which contains drivers for many of these external - drives. You may also want to look at CONFIG_PARIDE (Parallel port - IDE device support). - - If you built ppSCSI support into your kernel, you may still build - the individual protocol modules and high-level drivers as loadable - modules. If you build this support as a module, it will be called - ppscsi.o. - - To use the ppSCSI support, you must say Y or M here and also to at - least one protocol driver (e.g. "Shuttle EPST adapter", "Iomega VPI0 - adapter", "Shining ScarSCI adapter" etc.). - -Adaptec APA-348 adapter -CONFIG_PPSCSI_T348 - This option enables support for the APA-348 adapter from Adaptec - (also known as Trantor T348). If you build this as a module it will - be called t348.o. - -Adaptec APA-358 adapter -CONFIG_PPSCSI_T358 - This option enables support for the APA-358 adapter from Adaptec - (also known as Trantor T358). If you build this as a module it will - be called t358.o. - -Iomega VPI0 adapter -CONFIG_PPSCSI_VPI0 - This option enables support for the Iomega VPI0 adapter found in the - original ZIP-100 drives and the Jaz Traveller. If you build this as - a module it will be called vpi0.o. - -OnSpec 90c26 adapter -CONFIG_PPSCSI_ONSCSI - This option enables support for the OnSpec 90c26 in its SCSI adapter - mode. If you build this as a module it will be called onscsi.o. - -Shining SparSCI adapter -CONFIG_PPSCSI_SPARCSI - This option enables support for the WBS-11A parallel port SCSI - adapter. This adapter has been marketed by LinkSys as the - "ParaSCSI+" and by Shining Technologies as the "SparCSI". If you - build this as a module it will be called sparcsi.o. - -Shuttle EPSA-2 adapter -CONFIG_PPSCSI_EPSA2 - This option enables support for the Shuttle Technologies EPSA2 - parallel port SCSI adapter. EPAS2 is a predecessor to the EPST. If - you build this as a module it will be called epsa2.o. - -Shuttle EPST adapter -CONFIG_PPSCSI_EPST - This option enables support for the Shuttle Technologies EPST - parallel port SCSI adapter. If you build this as a module is will - be called epst.o. - SCSI Debug host simulator. (EXPERIMENTAL) CONFIG_SCSI_DEBUG This is a host adapter simulator that can be programmed to simulate @@ -6906,6 +7034,11 @@ say M here and read Documentation/modules.txt. The module will be called ohci1394.o. +Video1394 support +CONFIG_IEEE1394_VIDEO1394 + Say Y here if you want support for IEEE1394 FireWire interfacing + to digital cameras using the video1394 conventions. + Raw IEEE 1394 I/O support CONFIG_IEEE1394_RAWIO Say Y here if you want support for the raw device. This is generally @@ -6936,11 +7069,11 @@ telephone line with a modem either via UUCP (UUCP is a protocol to forward mail and news between unix hosts over telephone lines; read the UUCP-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto ) or dialing up a shell + ) or dialing up a shell account or a BBS, even using term (term is a program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected Unix computer. Read - http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html ). + ). You'll have to say Y if your computer contains a network card that you want to use under Linux (make sure you know its name because you @@ -6956,7 +7089,7 @@ Make sure to read the NET-3-HOWTO. Eventually, you will have to read Olaf Kirch's excellent and free book "Network Administrator's - Guide", to be found in http://www.linuxdoc.org/docs.html#guide . If + Guide", to be found in . If unsure, say Y. Dummy net driver support @@ -6969,7 +7102,7 @@ thing often comes in handy, the default is Y. It won't enlarge your kernel either. What a deal. Read about it in the Network Administrator's Guide, available from - http://www.linuxdoc.org/docs.html#guide . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -7012,16 +7145,16 @@ Normally, your access provider has to support SLIP in order for you to be able to use it, but there is now a SLIP emulator called SLiRP around (available via FTP (user: anonymous) from - ftp://metalab.unc.edu/pub/Linux/system/network/serial/ ) which + ) which allows you to use SLIP over a regular dial up shell connection. If you plan to use SLiRP, make sure to say Y to CSLIP, below. The NET-3-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , explains how to + , explains how to configure SLIP. Note that you don't need this option if you just want to run term (term is a program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected Unix computer. Read - http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html ). SLIP + ). SLIP support will enlarge your kernel by about 4 KB. If unsure, say N. If you want to compile this as a module ( = code which can be @@ -7037,10 +7170,10 @@ on both ends. Ask your access provider if you are not sure and answer Y, just in case. You will still be able to use plain SLIP. If you plan to use SLiRP, the SLIP emulator (available from - ftp://metalab.unc.edu/pub/Linux/system/network/serial/ ) which + ) which allows you to use SLIP over a regular dial up shell connection, you definitely want to say Y here. The NET-3-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , explains how to configure + , explains how to configure CSLIP. This won't enlarge your kernel. Keepalive and linefill @@ -7064,14 +7197,14 @@ PPP (Point to Point Protocol) is a newer and better SLIP. It serves the same purpose: sending Internet traffic over telephone (and other serial) lines. Ask your access provider if they support it, because - otherwise you can't use it; most internet access providers these + otherwise you can't use it; most Internet access providers these days support PPP rather than SLIP. To use PPP, you need an additional program called pppd as described - in Documentation/networking/ppp.txt and in the PPP-HOWTO, available - at http://www.linuxdoc.org/docs.html#howto . If you upgrade - from an older kernel, you might need to upgrade pppd as well. The - PPP option enlarges your kernel by about 16 KB. + in the PPP-HOWTO, available at + Make sure that you have the version of pppd recommended in + Documentation/Changes. The PPP option enlarges your kernel by about + 16 KB. There are actually two versions of PPP: the traditional PPP for asynchronous lines, such as regular analog phone lines, and @@ -7172,8 +7305,8 @@ This driver requires a specially patched pppd daemon. The patch to pppd, along with binaries of a patched pppd package can be found at: - http://www.shoshin.uwaterloo.ca/~mostrows - + + Wireless LAN (non-hamradio) CONFIG_NET_RADIO Support for wireless LANs and everything having to do with radio, @@ -7190,17 +7323,17 @@ driver (or Linux). If you wish to use Wireless Extensions with wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch the tools from - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html . + . Some user-level drivers for scarab devices which don't require special kernel support are available from - ftp://shadow.cabi.net/pub/Linux . + . STRIP (Metricom Starmode radio IP) CONFIG_STRIP Say Y if you have a Metricom radio and intend to use Starmode Radio IP. STRIP is a radio protocol developed for the MosquitoNet project - (on the WWW at http://mosquitonet.stanford.edu/ ) to send Internet + (on the WWW at ) to send Internet traffic using Metricom radios. Metricom radios are small, battery powered, 100kbit/sec packet radio transceivers, about the size and weight of a cellular telephone. (You may also have heard them called @@ -7232,12 +7365,12 @@ If you want to use an ISA WaveLAN card under Linux, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Some more specific + . Some more specific information is contained in Documentation/networking/wavelan.txt and in the source code drivers/net/wavelan.p.h. You will also need the wireless tools package available from - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html . + . Please read the man pages contained therein. This driver is also available as a module ( = code which can be @@ -7251,7 +7384,7 @@ Aironet makes Arlan, a class of wireless LAN adapters. These use the www.Telxon.com chip, which is also used on several similar cards. This driver is tested on the 655 and IC2200 series cards. Look at - http://www.ylenurme.ee/~elmer/655/ for the latest information. + for the latest information. The driver is built as two modules, arlan and arlan-proc. The latter is the /proc interface and is not needed most of time. @@ -7392,7 +7525,7 @@ To use your PC-cards, you will need supporting software from David Hinds' pcmcia-cs package (see the file Documentation/Changes for location). You also want to check out the PCMCIA-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + from . If unsure, say N. @@ -7517,7 +7650,7 @@ To use your PC-cards, you will need supporting software from David Hinds' pcmcia-cs package (see the file Documentation/Changes for location). You also want to check out the PCMCIA-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + from . Hermes support (Orinoco/WavelanIEEE/PrismII/Symbol 802.11b cards) CONFIG_PCMCIA_HERMES @@ -7531,11 +7664,11 @@ To use your PC-cards, you will need supporting software from David Hinds' pcmcia-cs package (see the file Documentation/Changes for location). You also want to check out the PCMCIA-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + from . You will also very likely also need the Wireless Tools in order to - configure your card and that /etc/pcmcia/wireless.opts works : - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html + configure your card and that /etc/pcmcia/wireless.opts works: + Aviator/Raytheon 2.4MHz wireless support CONFIG_PCMCIA_RAYCS @@ -7592,12 +7725,12 @@ Documentation/networking/PLIP.txt. The cables can be up to 15m long. Mode 0 works also if one of the machines runs DOS/Windows and has some PLIP software installed, e.g. the Crynwr PLIP packet driver - (http://oak.oakland.edu/simtel.net/msdos/pktdrvr-pre.html ) and + () and winsock or NCSA's telnet. If you want to use PLIP, say Y and read the PLIP mini-HOWTO as well as the NET-3-HOWTO, both available from - http://www.linuxdoc.org/docs.html#howto . Note that the PLIP + . Note that the PLIP protocol has been changed and this PLIP driver won't work together with the PLIP support in Linux versions 1.0.x. This option enlarges your kernel by about 8 KB. @@ -7620,7 +7753,7 @@ Say Y if you want this and read Documentation/networking/eql.txt. You may also want to read section 6.2 of the NET-3-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + from . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -7699,7 +7832,7 @@ network, usually at the phone company) can carry several logical point-to-point connections to other computers connected to the frame relay network. For a general explanation of the protocol, check out - http://www.frforum.com/ on the WWW. To use frame relay, you need + on the WWW. To use frame relay, you need supporting hardware (called FRAD) and certain programs from the net-tools package as explained in Documentation/networking/framerelay.txt. @@ -7778,7 +7911,7 @@ the price of an external router. If you have one of those cards and wish to use your Linux box as a WAN router, say Y here and also to the WAN driver for your card, below. You will then need the - wan-tools package which is available from ftp://ftp.sangoma.com . + wan-tools package which is available from . Read Documentation/networking/wan-router.txt for more information. The WAN routing support is also available as a module called @@ -7802,7 +7935,7 @@ At the moment, few devices support fast switching (tulip is one of them, a modified 8390 driver can be found at - ftp://ftp.inr.ac.ru/ip-routing/fastroute/fastroute-8390.tar.gz ). + ). If unsure, say N. @@ -7812,7 +7945,7 @@ during periods of extremal congestion. At the moment only a couple of device drivers support it (really only one -- tulip, a modified 8390 driver can be found at - ftp://ftp.inr.ac.ru/ip-routing/fastroute/fastroute-8390.tar.gz ). + ). Really, this option is applicable to any machine attached to a fast enough network, and even a 10 Mb NIC is able to kill a not very slow @@ -7839,15 +7972,15 @@ This code is considered to be experimental. To administer these schedulers, you'll need the user-level utilities - from the package iproute2+tc at ftp://ftp.inr.ac.ru/ip-routing/ . + from the package iproute2+tc at . That package also contains some documentation; for more, check out - http://snafu.freedom.org/linux2.2/iproute-notes.html . + . This Quality of Service (QoS) support will enable you to use Differentiated Services (diffserv) and Resource Reservation Protocol (RSVP) on your Linux router if you also say Y to "QoS support", "Packet classifier API" and to some classifiers below. Documentation - and software is at http://icawww1.epfl.ch/linux-diffserv/ . + and software is at . If you say Y here and to "/proc file system" below, you will be able to read status information about packet schedulers from the file @@ -7977,7 +8110,7 @@ Differentiated Services (diffserv) and Resource Reservation Protocol (RSVP) on your Linux router if you also say Y to "Packet classifier API" and to some classifiers below. Documentation and software is at - http://icawww1.epfl.ch/linux-diffserv/ . + . Note that the answer to this question won't directly affect the kernel: saying N will just cause this configure script to skip all @@ -8000,7 +8133,7 @@ This will enable you to use Differentiated Services (diffserv) and Resource Reservation Protocol (RSVP) on your Linux router. Documentation and software is at - http://icawww1.epfl.ch/linux-diffserv/ . + . ### Add #tristate ' TC index classifier' CONFIG_NET_CLS_TCINDEX @@ -8112,7 +8245,7 @@ To actually use the COSA or SRP board, you will need user-space utilities for downloading the firmware to the cards and to set them - up. Look at the http://www.fi.muni.cz/~kas/cosa/ for more + up. Look at the for more information about the cards (including the pointer to the user-space utilities). You can also read the comment at the top of the drivers/net/cosa.c for details about the cards and the driver @@ -8127,7 +8260,7 @@ CONFIG_DSCC4 This is a driver for Etinc PCISYNC boards based on the Infineon (ex. Siemens) DSCC4 chipset. It is supposed to work with the four - ports card. Take a look at http://www.cogenit.fr/dscc4 + ports card. Take a look at for further informations about the driver and his configuration. The driver will be compiled as a module ( = code which can be @@ -8198,7 +8331,7 @@ module will be called sbni.o). You can find more information and last versions of drivers and - utilities at http://www.granch.ru . If you have any question you + utilities at . If you have any question you can send email to sbni@granch.ru. Say N if unsure. @@ -8212,7 +8345,7 @@ Router". You will need the wan-tools package which is available from - ftp://ftp.sangoma.com . Read Documentation/networking/wan-router.txt + . Read Documentation/networking/wan-router.txt for more information. Note that the answer to this question won't directly affect the @@ -8222,8 +8355,8 @@ Sangoma WANPIPE(tm) multiprotocol cards CONFIG_VENDOR_SANGOMA - WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com) - is a family of intelligent multiprotocol WAN adapters with data + WANPIPE from Sangoma Technologies Inc. ( + is a family of intelligent multiprotocol WAN adapters with data transfer rates up to 4Mbps. They are also known as Synchronous Data Link Adapters (SDLA) and are designated as S514-PCI or S508-ISA. These cards support @@ -8310,7 +8443,7 @@ Read linux/Documentation/networking/comx.txt for help on configuring and using COMX interfaces. Further info on these cards can be found - at http://www.itc.hu or . + at or . You must say Y to "/proc file system support" (CONFIG_PROC_FS) to use this driver. @@ -8324,7 +8457,7 @@ MultiGate family. Say Y if you have one of these. You will need additional firmware to use these cards, which are - downloadable from ftp://ftp.itc.hu/. + downloadable from . If you want to compile this as a module, say M and read Documentation/modules.txt. The module will be called comx-hw-comx.o. @@ -8348,7 +8481,7 @@ configuration. The ISDN interface of this card is Teles 16.3 compatible, you should enable it in the ISDN configuration menu. The driver for the flash ROM of this card is available separately on - ftp://ftp.itc.hu/. + . If you want to compile this as a module, say M and read Documentation/modules.txt. The module will be called @@ -8403,8 +8536,8 @@ Cyclom 2X(tm) multiprotocol cards (EXPERIMENTAL) CONFIG_CYCLADES_SYNC - Cyclom 2X from Cyclades Corporation (http://www.cyclades.com and - http://www.cyclades.com.br) is an intelligent multiprotocol WAN + Cyclom 2X from Cyclades Corporation (and + is an intelligent multiprotocol WAN adapter with data transfer rates up to 512 Kbps. These cards support the X.25 and SNA related protocols. If you have one or more of these cards, say Y to this option. The next questions will ask you about @@ -8412,10 +8545,10 @@ supported). While no documentation is available at this time please grab the - wanconfig tarball in http://www.conectiva.com.br/~acme/cycsyn-devel - (with minor changes to make it compile with the current wanrouter + wanconfig tarball in + (with minor changes to make it compile with the current wanrouter include files; efforts are being made to use the original package - available at ftp://ftp.sangoma.com ). + available at ). Feel free to contact me or the cycsyn-devel mailing list at acme@conectiva.com.br and cycsyn-devel@bazar.conectiva.com.br for @@ -8440,7 +8573,7 @@ Say Y to this option if your Linux box contains a WAN card supported by this driver and you are planning to connect the box to a WAN ( = Wide Area Network). You will need supporting software from - http://hq.pm.waw.pl/hdlc/. + . Generic HDLC driver currently supports raw HDLC, Cisco HDLC, Frame Relay, synchronous Point-to-Point Protocol (PPP) and X.25. @@ -8464,7 +8597,7 @@ CONFIG_N2 This driver is for RISCom/N2 single or dual channel ISA cards made by SDL Communications Inc. If you have such a card, - say Y here and see http://hq.pm.waw.pl/pub/hdlc/ + say Y here and see Note that N2csu and N2dds cards are not supported by this driver. @@ -8474,8 +8607,8 @@ CONFIG_C101 This driver is for C101 SuperSync ISA cards made by Moxa Technologies Co., Ltd. If you have such a card, - say Y here and see http://hq.pm.waw.pl/pub/hdlc/ - + say Y here and see +x If unsure, say N here. Ethernet (10 or 100Mbit) @@ -8496,7 +8629,7 @@ If your Linux machine will be connected to an Ethernet and you have an Ethernet network interface card (NIC) installed in your computer, say Y here and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . You will then also have + . You will then also have to say Y to the driver for your particular NIC. Note that the answer to this question won't directly affect the @@ -8507,7 +8640,7 @@ CONFIG_NET_VENDOR_SMC If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all @@ -8518,7 +8651,7 @@ CONFIG_WD80x3 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -8530,7 +8663,7 @@ CONFIG_ULTRAMCA If you have a network (Ethernet) card of this type and are running an MCA based system (PS/2), say Y and read the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -8542,7 +8675,7 @@ CONFIG_ULTRA If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Important: There have been many reports that, with some motherboards mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible, @@ -8561,7 +8694,7 @@ CONFIG_ULTRA32 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -8576,7 +8709,7 @@ another SMC9192/9194 based chipset. Say Y if you want it compiled into the kernel, and read the file Documentation/networking/smc9.txt and the Ethernet-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + from . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you @@ -8590,7 +8723,7 @@ with ISA NE2000 cards (they have their own driver, "NE2000/NE1000 support" below). If you have a PCI NE2000 network (Ethernet) card, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver also works for the following NE2000 clone cards: RealTek RTL-8029 Winbond 89C940 Compex RL2000 KTI ET32P2 @@ -8607,7 +8740,7 @@ CONFIG_NET_VENDOR_RACAL If you have a network (Ethernet) card belonging to this class, such as the NI5010, NI5210 or NI6210, say Y and read the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all @@ -8618,7 +8751,7 @@ CONFIG_NI5010 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Note that this is still + . Note that this is still experimental code. This driver is also available as a module ( = code which can be @@ -8631,7 +8764,7 @@ CONFIG_NI52 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -8643,7 +8776,7 @@ CONFIG_NI65 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -8656,7 +8789,7 @@ This is a driver for the Fast Ethernet PCI network cards based on the RTL8139 chips. If you have one of those, say Y and read Documentation/networking/8139too.txt as well as the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -8689,7 +8822,7 @@ the SiS 900 and SiS 7016 chips. The SiS 900 core is also embedded in SiS 630 and SiS 540 chipsets. If you have one of those, say Y and read the Ethernet-HOWTO, available at - http://www.linuxdoc.org/docs.html#howto . Please read + . Please read Documentation/networking/sis900.txt and comments at the beginning of drivers/net/sis900.c for more information. @@ -8705,7 +8838,7 @@ CONFIG_YELLOWFIN Say Y here if you have a Packet Engines G-NIC PCI Gigabit Ethernet adapter. This adapter is used by the Beowulf Linux cluster project. - See http://cesdis.gsfc.nasa.gov/linux/drivers/yellowfin.html for + See for more information about this driver in particular and Beowulf in general. @@ -8730,9 +8863,9 @@ connection. Further documentation and the necessary scripts can be found at: - http://www.jacksonville.net/~fventuri/ - http://home.adelphia.net/~siglercm/sb1000.html - http://linuxpower.cx/~cable/ + + + If you don't have this card, of course say N. @@ -8812,7 +8945,7 @@ CONFIG_LANCE If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Some LinkSys cards are + . Some LinkSys cards are of this type. If you want to compile this driver as a module ( = code which can be @@ -8824,13 +8957,13 @@ CONFIG_SGI_IOC3_ETH If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . 3COM cards CONFIG_NET_VENDOR_3COM If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all @@ -8841,7 +8974,7 @@ CONFIG_EL1 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Also, consider buying a + . Also, consider buying a new card, since the 3c501 is slow, broken, and obsolete: you will have problems. Some people suggest to ping ("man ping") a nearby machine every minute ("man cron") when using this card. @@ -8856,7 +8989,7 @@ CONFIG_EL2 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -8869,7 +9002,7 @@ Information about this network (Ethernet) card can be found in Documentation/networking/3c505.txt. If you have a card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -8881,7 +9014,7 @@ CONFIG_EL16 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -8893,7 +9026,7 @@ CONFIG_ELMC If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -8905,7 +9038,7 @@ CONFIG_ELMC_II If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -8917,7 +9050,7 @@ CONFIG_EL3 If you have a network (Ethernet) card belonging to the 3Com EtherLinkIII series, say Y and read the Ethernet-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + from . If your card is not working you may need to use the DOS setup disk to disable Plug & Play mode, and to select the default @@ -8933,7 +9066,7 @@ CONFIG_3C515 If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet network card, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -8953,7 +9086,7 @@ "Hurricane" (3c555/3cSOHO) PCI If you have such a card, say Y and read the Ethernet-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . More specific + from . More specific information is in Documentation/networking/vortex.txt and in the comments at the beginning of drivers/net/3c59x.c. @@ -8968,7 +9101,7 @@ bus system (that's the way the cards talks to the other components of your computer) is ISA (as opposed to EISA, VLB or PCI), say Y. Make sure you know the name of your card. Read the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . If unsure, say Y. @@ -8988,7 +9121,7 @@ support" below. You might also want to have a look at the Ethernet-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto (even though ARCnet + from (even though ARCnet is not really Ethernet). This driver is also available as a module ( = code which can be @@ -8997,18 +9130,6 @@ module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. -Enable arc0e (ARCnet "ether-encap" packet format) -CONFIG_ARCNET_ETH - This allows you to use "Ethernet encapsulation" with your ARCnet - card via the virtual arc0e device. You only need arc0e if you want - to talk to nonstandard ARCnet software, specifically, - DOS/Windows-style "NDIS" drivers. You do not need to say Y here to - communicate with industry-standard RFC1201 implementations, like the - arcether.com packet driver or most DOS/Windows ODI drivers. RFC1201 - is included automatically as the arc0 device. Please read the - ARCnet documentation in Documentation/networking/arcnet.txt for more - information about using arc0e and arc0s. - Enable old ARCNet packet format (RFC 1051) CONFIG_ARCNET_1051 This allows you to use RFC1051 with your ARCnet card via the virtual @@ -9022,6 +9143,15 @@ documentation in Documentation/networking/arcnet.txt for more information about using arc0e and arc0s. +Enable standard ARCNet packet format (RFC 1201) +CONFIG_ARCNET_1201 + This allows you to use RFC1201 with your ARCnet card via the virtual + arc0 device. You need to say Y here to communicate with + industry-standard RFC1201 implementations, like the arcether.com + packet driver or most DOS/Windows ODI drivers. Please read the ARCnet + documentation in Documentation/networking/arcnet.txt for more + information about using arc0. + ARCnet COM90xx (normal) chipset driver CONFIG_ARCNET_COM90xx This is the chipset driver for the standard COM90xx cards. If you @@ -9076,7 +9206,7 @@ CONFIG_E2100 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9089,7 +9219,7 @@ Support for CS89x0 chipset based Ethernet cards. If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto as well as + as well as Documentation/networking/cs89x0.txt. If you want to compile this as a module ( = code which can be @@ -9102,7 +9232,7 @@ CONFIG_DEPCA If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto as well as + as well as drivers/net/depca.c. If you want to compile this as a module ( = code which can be @@ -9117,7 +9247,7 @@ cards. If this is for you, say Y and read Documentation/networking/ewrk3.txt in the kernel source as well as the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -9129,7 +9259,7 @@ CONFIG_SEEQ8005 This is a driver for the SEEQ 8005 network (Ethernet) card. If this is for you, read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -9141,7 +9271,7 @@ CONFIG_AT1700 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9154,7 +9284,7 @@ CONFIG_FMV18X If you have a Fujitsu FMV-181/182/183/184 network (Ethernet) card, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you use an FMV-183 or FMV-184 and it is not working, you may need to disable Plug & Play mode of the card. @@ -9171,7 +9301,7 @@ driver supports intel i82595{FX,TX} based boards. Note however that the EtherExpress PRO/100 Ethernet card has its own separate driver. Please read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9183,7 +9313,7 @@ CONFIG_EEXPRESS If you have an EtherExpress16 network (Ethernet) card, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Note that the Intel + . Note that the Intel EtherExpress16 card used to be regarded as a very poor choice because the driver was very unreliable. We now have a new driver that should do better. @@ -9198,7 +9328,7 @@ CONFIG_HAMACHI If you have a Gigabit Ethernet card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -9210,7 +9340,7 @@ CONFIG_HPLAN_PLUS If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9222,7 +9352,7 @@ CONFIG_HPLAN If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9234,7 +9364,7 @@ CONFIG_HP100 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -9246,7 +9376,7 @@ CONFIG_NE2000 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Many Ethernet cards + . Many Ethernet cards without a specific driver are compatible with NE2000. If you have a PCI NE2000 card however, say N here and Y to "PCI @@ -9266,19 +9396,19 @@ This driver is for the National Semiconductor DP83810 series, including the 83815 chip. More specific information and updates are available from - http://www.scyld.com/network/natsemi.html + SK_G16 support CONFIG_SK_G16 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . NE/2 (ne2000 MCA version) support CONFIG_NE2_MCA If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9321,7 +9451,7 @@ CONFIG_NET_PCI This is another class of network cards which attach directly to the bus. If you have one of those, say Y and read the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all @@ -9333,7 +9463,7 @@ CONFIG_PCNET32 If you have a PCnet32 or PCnetPCI based network (Ethernet) card, answer Y here and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9345,7 +9475,7 @@ CONFIG_AC3200 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9357,7 +9487,7 @@ CONFIG_LNE390 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9369,7 +9499,7 @@ CONFIG_NE3210 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Note that this driver + . Note that this driver will NOT WORK for NE3200 cards as they are completely different. This driver is also available as a module ( = code which can be @@ -9382,7 +9512,7 @@ CONFIG_APRICOT If you have a network (Ethernet) controller of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -9396,7 +9526,7 @@ These include the DE425, DE434, DE435, DE450 and DE500 models. If you have a network card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . More specific + . More specific information is contained in Documentation/networking/de4x5.txt. This driver is also available as a module ( = code which can be @@ -9414,7 +9544,7 @@ (smc9332dst), you can also try the driver for "Generic DECchip" cards, above. However, most people with a network card of this type will say Y here.) Do read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . More specific + . More specific information is contained in Documentation/networking/tulip.txt. This driver is also available as a module ( = code which can be @@ -9429,7 +9559,7 @@ PCI/EISA Ethernet switch cards. These include the SE-4 and the SE-6 models. If you have a network card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . More specific + . More specific information is contained in Documentation/networking/dgrs.txt. This driver is also available as a module ( = code which can be @@ -9442,7 +9572,7 @@ CONFIG_EEPRO100 If you have an Intel EtherExpress PRO/100 PCI network (Ethernet) card, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9466,7 +9596,7 @@ CONFIG_ETH16I If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9479,7 +9609,7 @@ If you have a PCI Ethernet network card based on the ThunderLAN chip which is supported by this driver, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Devices currently supported by this driver are Compaq Netelligent, Compaq NetFlex and Olicom cards. Please read the file @@ -9507,7 +9637,7 @@ PCI DM9102(A)/DM9132/DM9801 support CONFIG_DM9102 This driver is for DM9102(A)/DM9132/DM9801 compatible PCI cards from - Davicom ( http://www.davicom.com.tw ). If you have such a network + Davicom ( ). If you have such a network (Ethernet) card, say Y. Some information is contained in the file Documentation/networking/dmfe.txt. @@ -9521,7 +9651,7 @@ CONFIG_ES3210 If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9534,7 +9664,13 @@ This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC, which is based on the SMC83c17x (EPIC/100). More specific information and updates are available from - http://www.scyld.com/network/epic100.html + + +DEC LANCE ethernet controller support +CONFIG_DECLANCE + This driver is for the series of Ethernet controllers produced by + DEC (now Compaq) based on the AMD Lance chipset, including the + DEPCA series. (This chipset is better known via the NE2100 cards.) SGI Seeq ethernet controller support CONFIG_SGISEEQ @@ -9545,14 +9681,14 @@ CONFIG_SUNDANCE This driver is for the Sundance "Alta" chip. More specific information and updates are available from - http://www.scyld.com/network/sundance.html + Winbond W89c840 PCI Ethernet support CONFIG_WINBOND_840 This driver is for the Winbond W89c840 chip. It also works with the TX9882 chip on the Compex RL100-ATX board. More specific information and updates are available from - http://www.scyld.com/network/drivers.html + Zenith Z-Note support (EXPERIMENTAL) CONFIG_ZNET @@ -9560,14 +9696,14 @@ (Ethernet) card, and this is the Linux driver for it. Note that the IBM Thinkpad 300 is compatible with the Z-Note and is also supported by this driver. Read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Pocket and portable adapters CONFIG_NET_POCKET Cute little network (Ethernet) devices which attach to the parallel port ("pocket adapters"), commonly used with laptops. If you have one of those, say Y and read the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to plug a network (or some other) card into the PCMCIA (or PC-card) slot of your laptop instead (PCMCIA is the standard for @@ -9576,7 +9712,7 @@ Documentation/Changes) and you can say N here. Laptop users should read the Linux Laptop home page at - http://www.cs.utexas.edu/users/kharker/linux-laptop/ . + . Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all @@ -9587,7 +9723,7 @@ CONFIG_ATP This is a network (Ethernet) device which attaches to your parallel port. Read drivers/net/atp.c as well as the Ethernet-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto , if you + available from , if you want to use this. If you intend to use this driver, you should have said N to the "Parallel printer support", because the two drivers don't like each other. @@ -9602,7 +9738,7 @@ This is a network (Ethernet) device which attaches to your parallel port. Read Documentation/networking/DLINK.txt as well as the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , if you want to use + , if you want to use this. It is possible to have several devices share a single parallel port and it is safe to compile the corresponding drivers into the kernel. @@ -9617,7 +9753,7 @@ This is a network (Ethernet) device which attaches to your parallel port. Read Documentation/networking/DLINK.txt as well as the Ethernet-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , if you want to use + , if you want to use this. It is possible to have several devices share a single parallel port and it is safe to compile the corresponding drivers into the kernel. @@ -9635,14 +9771,14 @@ connected to such a Token Ring network and want to use your Token Ring card under Linux, say Y here and to the driver for your particular card below and read the Token-Ring mini-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . Most people can + from . Most people can say N here. IBM Tropic chipset based adapter support CONFIG_IBMTR This is support for all IBM Token Ring cards that don't use DMA. If you have such a beast, say Y and read the Token-Ring mini-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . Warning: this driver will almost definitely fail if more than one active Token Ring card is present. @@ -9660,7 +9796,7 @@ If you have such an adapter, say Y and read the Token-Ring mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9669,7 +9805,7 @@ Also read the file Documentation/networking/olympic.txt or check the Linux Token Ring Project site for the latest information at - http://www.linuxtr.net . + . IBM Lanstreamer chipset PCI adapter support CONFIG_IBMLS @@ -9677,7 +9813,7 @@ If you have such an adapter, say Y and read the Token-Ring mini-HOWTO available via FTP (user:anonymous) from - ftp://metalab.unc/edu/pub/Linux/docs/HOWTO. + . This driver is also available as a modules ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9697,10 +9833,10 @@ If you have such an adapter and would like to use it, say Y and read the Token-Ring mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Also read the file Documentation/networking/tms380tr.txt or - check http://www.auk.cx/tms380tr/ . + check . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -9722,6 +9858,18 @@ The module will be called tmspci.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +Generic TMS380 ISA support +CONFIG_TMSISA + This tms380 module supports generic TMS380-based ISA cards. + + These cards are known to work: + - SysKonnect TR4/16 ISA (SK-4190) + + This driver is available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called tmsisa.o. If you want to compile it + as a module, say M here and read Documentation/modules.txt. + Madge Smart 16/4 PCI Mk2 support CONFIG_ABYSS This tms380 module supports the Madge Smart 16/4 PCI Mk2 @@ -9750,7 +9898,7 @@ If you have such an adapter and would like to use it, say Y or M and read the Token-Ring mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto and the file + and the file Documentation/networking/smctr.txt. This driver is also available as a module ( = code which can be @@ -9773,7 +9921,9 @@ Sun Lance support CONFIG_SUNLANCE This driver supports the "le" interface present on all 32-bit Sparc - systems, on some older Ultra systems and as an Sbus option. + systems, on some older Ultra systems and as an Sbus option. These + cards are based on the AMD Lance chipset, which is better known + via the NE2100 cards. This support is also available as a module called sunlance.o ( = code which can be inserted in and removed from the running kernel @@ -9814,7 +9964,7 @@ say Y to "QoS and/or fair queueing" above. To set up and configure shaper devices, you need the shapecfg - program, available from ftp://shadow.cabi.net/pub/Linux in the + program, available from in the shaper package. This driver is also available as a module ( = code which can be @@ -9921,7 +10071,7 @@ CONFIG_CD_NO_IDESCSI If you have a CDROM drive that is neither SCSI nor IDE/ATAPI, say Y here, otherwise N. Read the CDROM-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all @@ -10066,6 +10216,22 @@ include/linux/sbpcd.h before compiling the new kernel. Read the file Documentation/cdrom/sbpcd. +Matsushita/Panasonic, ... third CDROM controller support +CONFIG_SBPCD3 + Say Y here only if you have three CDROM controller cards of this type + (usually only if you have more than six drives). You should enter + the parameters for the second, third and fourth interface card into + include/linux/sbpcd.h before compiling the new kernel. Read + the file Documentation/cdrom/sbpcd. + +Matsushita/Panasonic, ... fourth CDROM controller support +CONFIG_SBPCD4 + Say Y here only if you have four CDROM controller cards of this type + (usually only if you have more than eight drives). You should enter + the parameters for the second, third and fourth interface card into + include/linux/sbpcd.h before compiling the new kernel. Read + the file Documentation/cdrom/sbpcd. + Aztech/Orchid/Okano/Wearnes/TXC/CyDROM CDROM support CONFIG_AZTCD This is your driver if you have an Aztech CDA268-01A, Orchid @@ -10170,13 +10336,18 @@ The module will be called isp16.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +iSeries Virtual I/O CD Support +CONFIG_VIOCD + If you are running Linux on an IBM iSeries system and you want to + read a CD drive owned by OS/400, say Y here. + Quota support CONFIG_QUOTA If you say Y here, you will be able to set per user limits for disk usage (also called disk quotas). Currently, it works only for the ext2 file system. You need additional software in order to use quota support; for details, read the Quota mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . Probably the quota + . Probably the quota support is only useful for multi user systems. If unsure, say N. Memory Technology Device (MTD) support @@ -10232,7 +10403,7 @@ Ramix PMC551 PCI Mezzanine ram card support CONFIG_MTD_PMC551 This provides a MTD device driver for the Ramix PMC551 RAM PCI card - from Ramix Inc. (http://www.ramix.com/products/memory/pmc551.html). + from Ramix Inc. (). These devices come in memory configurations from 32M - 1G. If you have one, you probably want to enable this. @@ -10274,7 +10445,7 @@ AMD and other flash manufactures that provides a universal method for probing the capabilities of flash devices. If you wish to support any device that is CFI-compliant, you need to enable this - option. Visit (http://www.amd.com/products/nvd/overview/cfi.html) + option. Visit () for more information on CFI. CFI support for Intel/Sharp Extended Command Set chips @@ -10321,7 +10492,7 @@ to get on with their job of driving the flash chips without having to know about the paging. If you have one of these boards, you probably want to enable this mapping driver. More info is at - (http://www.itc.hu/). + (). Flash chip mapping on Nora CONFIG_MTD_NORA @@ -10337,7 +10508,7 @@ This provides a 'mapping' driver which supports the way in which the flash chips are connected in the Octagon-5066 Single Board Computer. More information on the board is available at - (http://www.octagonsystems.com/Products/5066/5066.html). + (). Flash chip mapping on RPXlite PPC board CONFIG_MTD_RPXLITE @@ -10345,14 +10516,14 @@ a strange sparse mapping. This 'mapping' driver supports that arrangement, allowing the CFI probe and command set driver code to communicate with the chips on the RPXLite board. More at - (http://www.embeddedplanet.com/rpx_lite_specification_sheet.htm). + (). Flash chip mapping on Tempustech VMAX SBC301 CONFIG_MTD_VMAX This provides a 'mapping' driver which supports the way in which the flash chips are connected in the Tempustech VMAX SBC301 Single Board Computer. More information on the board is available at - (http://www.tempustech.com/tt301.htm). + (). Direct chardevice access to MTD devices CONFIG_MTD_CHAR @@ -10438,6 +10609,28 @@ of debug messages to the system log. Select this if you are having a problem with USB support and want to see more of what is going on. +USB fetch large config +CONFIG_USB_LARGE_CONFIG + This option changes the initial request for a config descriptor so + that some poorly designed devices will still work. Some APC UPSes + need it. Basically, the usb subsystem sends a request for a short + (8 byte) config, just to find out how large the real config is. + Incorrectly implemented devices may choke on this small config + request. This option make the initial request for a quite large + config (1009 bytes), and things just work. + + If you have an APC UPS, say Y; otherwise say N. + +USB long timeout +CONFIG_USB_LONG_TIMEOUT + This option makes the standard time out a bit longer. Basically, + some devices are just slow to respond, so this makes usb more + patient. There should be no harm in selecting this, but it is + needed for some MGE Ellipse UPSes. + + If you have an MGE Ellipse UPS, or you see timeouts in HID + transactions, say Y; otherwise say N. + UHCI (intel PIIX4, VIA, ...) support? CONFIG_USB_UHCI The Universal Host Controller Interface is a standard by Intel for @@ -10478,10 +10671,6 @@ The module will be called uhci.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -UHCI unlink optimizations (EXPERIMENTAL) -CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE - This option currently does nothing. You may say Y or N. - OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support CONFIG_USB_OHCI The Open Host Controller Interface is a standard by @@ -10514,6 +10703,22 @@ The module will be called hid.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +/dev/usb/hiddev raw HID device support (EXPERIMENTAL) +CONFIG_USB_HIDDEV + Say Y here if you want to support HID devices (from the USB + specification standpoint) that aren't strictly user interface + devices, like monitor controls and Uninterruptable Power Supplies. + This module supports these devices separately using a separate + event interface on /dev/usb/hiddevX (char 180:96 to 180:111). + This driver requires CONFIG_USB_HID. + + If unsure, say N. + + 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 hiddev.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + USB HIDBP Keyboard (basic) support CONFIG_USB_KBD Say Y here if you don't want to use the generic HID driver for your @@ -10710,7 +10915,7 @@ USB Handspring Visor Driver CONFIG_USB_SERIAL_VISOR Say Y here if you want to connect to your HandSpring Visor through - its USB docking station. See http://usbvisor.sourceforge.net for + its USB docking station. See for more information on using this driver. This code is also available as a module ( = code which can be @@ -10734,7 +10939,7 @@ Say Y here if you want to use a FTDI SIO single port USB to serial converter device. The implementation I have is called the USC-1000. - See http://reality.sgi.com/bryder_wellington/ftdi_sio for more + See for more information on this driver and the device. This code is also available as a module ( = code which can be @@ -10760,7 +10965,7 @@ and was developed with their support. You must also include firmware to support your particular device(s). - See http://www.linuxcare.com.au/hugh/keyspan.html for + See for more information. This code is also available as a module ( = code which can be @@ -10892,7 +11097,7 @@ (Y or M in config) Video For Linux (under Character Devices) to use this driver. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -10910,13 +11115,42 @@ This driver uses the Video For Linux API. You must say Y or M to "Video For Linux" (under Character Devices) to use this driver. Information on this API and pointers to "v4l" programs may be found - on the WWW at http://roadrunner.swansea.uk.linux.org/v4l.shtml . + on the WWW at . This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called ov511.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +USB Communication Class Ethernet driver +CONFIG_USB_CDCETHER + This driver supports devices conforming to the Communication + Device Class Ethernet Control Model. This is used in some + cable modems (Ericsson, Broadcom and others). For more + details on the specification, get the Communication + Device Class specification from + + 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 CDCether.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +USB SE401 Camera support +CONFIG_USB_SE401 + Say Y here if you want to connect this type of camera to your + computer's USB port. See Documentation/usb/se401.txt for more + information and for a list of supported cameras. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" (under Multimedia Devices) to use this driver. + Information on this API and pointers to "v4l" programs may be found + on the WWW at http://roadrunner.swansea.uk.linux.org/v4l.shtml . + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called se401.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + Philips webcam support CONFIG_USB_PWC Say Y or M here if you want to use one of these Philips USB webcams: @@ -10972,6 +11206,36 @@ The module will be called pegasus.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +USB KLSI KL5USB101-based ethernet device support (EXPERIMENTAL)' +CONFIG_USB_KAWETH + Say Y here if you want to use one of the following 10Mbps only + USB Ethernet adapters based on the KLSI KL5KUSB101B chipset: + 3Com 3C19250 + ADS USB-10BT + ATEN USB Ethernet + ASANTE USB To Ethernet Adapter + AOX Endpoints USB Ethernet + Correga K.K. + D-Link DSB-650C and DU-E10 + Entrega / Portgear E45 + Jaton USB Ethernet Device Adapter + Kingston Technology USB Ethernet Adapter + Linksys USB10T + Mobility USB-Ethernet Adapter + NetGear EA-101 + Peracom Enet and Enet2 + Portsmith Express Ethernet Adapter + Shark Pocket Adapter + SMC 2202USB + Sony Vaio port extender + + This driver is likely to work with most 10Mbps only USB Ethernet + adapters, including some "no brand" devices. + 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 kaweth.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + USB Kodak DC-2xx Camera support CONFIG_USB_DC2XX Say Y here if you want to connect this type of still camera to @@ -10988,7 +11252,7 @@ CONFIG_USB_MDC800 Say Y here if you want to connect this type of still camera to your computer's USB port. This driver can be used with gphoto 0.4.3 - and higher (look at http://www.gphoto.org ). + and higher (look at ). To use it create a device node with "mknod /dev/mustek c 180 32" and configure it in your software. @@ -11074,7 +11338,7 @@ 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 + brought to you by the DAB-Team (This driver can be taken as an example for URB-based bulk, control, and isochronous transactions. URB's are explained in Documentation/usb/URB.txt. @@ -11084,12 +11348,29 @@ The module will be called dabusb.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +Host-to-Host USB networking +CONFIG_USB_USBNET + This driver supports network links over USB with USB "Network" + or "data transfer" cables, often used to network laptops to PCs. + Such cables have chips from suppliers such as NetChip and Prolific. + Intelligent USB devices could also use this approach to provide + Internet access, using standard USB cabling. + + These links will have names like "usb0", "usb1", etc. They act + like two-node Ethernets, so you can use 802.1d Ethernet Bridging + (CONFIG_BRIDGE) to simplify your network routing. + + This code is also available as a kernel module (code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called usbnet.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + PLUSB driver CONFIG_USB_PLUSB A driver for the Prolific PL-2302 USB-to-USB network device. This 'USB cable' connects two hosts via a point-to-point network with - bandwidth of 5 Mbit/s. Configure this driver after connecting the - USB cable via ifconfig plusb0 10.0.0.1 pointopoint 10.0.0.2 (and + bandwidth of 6 Mbit/s. Configure this driver after connecting the + USB cable via "ifconfig plusb0 10.0.0.1 pointopoint 10.0.0.2" (and vice versa on the other host). This code is also available as a module ( = code which can be @@ -11119,13 +11400,56 @@ (Y or M in config) Video For Linux (under Character Devices) to use this driver. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called dsbr100.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +Always do synchronous disk IO for UBD +CONFIG_BLK_DEV_UBD_SYNC + The User-Mode Linux port includes a driver called UBD which will let + you access arbitrary files on the host computer as block devices. + Writes to such a block device are not immediately written to the + host's disk; this may cause problems if, for example, the User-Mode + Linux 'Virtual Machine' uses a journalling filesystem and the host + computer crashes. + + Synchronous operation (i.e. always writing data to the host's disk + immediately) is configurable on a per-UBD basis by using a special + kernel command line option. Alternatively, you can say Y here to turn + on synchronous operation by default for all block. + + If you're running a journalling filesystem (like reiserfs, for example) + in your virtual machine, you will want to say Y here. If you care for + the safety of the data in your virtual machine, Y is a wise choice too. + In all other cases (for example, if you're just playing around with + User-Mode Linux) you can choose N. + +Prompt for filesystems other than ext2, proc, /dev/pts, /dev +CONFIG_PROMPT_FS + Most User-Mode Linux users will use one of the pre-built root + filesystem images, and won't access any other filesystems. + + If you say N here, then the kernel will be compiled with support + for quota, procfs, ext2, the /dev/pts filesystem, optionally the + /dev filesystem, and no other filesystem options. + + If you are doing filesystem hacking, or want to use NFS or cramfs + (or any other filesystem option), you should say Y here and you'll + be asked the normal filesystem questions. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause this configure script to skip all + the questions about filesystems except devfs. If unsure, say N. + +Enable ptrace proxy +CONFIG_PT_PROXY + This option enables a debugging interface which allows gdb to debug + the kernel without needing to actually attach to kernel threads. + If you want to do kernel debugging, say Y here; otherwise say N. + Microtek USB scanner support CONFIG_USB_MICROTEK Say Y here if you want support for the Microtek X6USB and possibly @@ -11133,14 +11457,14 @@ scsi generic device to the rest of the system. A patched version of SANE is necessary to use the scanner. It's available at - http://fachschaft.cup.uni-muenchen.de/~neukum/scanner.html + This driver can be compiled as a module. USB Bluetooth support CONFIG_USB_BLUETOOTH Say Y here if you want to connect a USB Bluetooth device to your computer's USB port. You will need the Bluetooth stack (available - at http://developer.axis.com/software/index.shtml) to fully use + at to fully use the device. This code is also available as a module ( = code which can be @@ -11225,7 +11549,7 @@ by about 44 KB. The Ext2fs-Undeletion mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , gives information about + , gives information about how to retrieve deleted files on ext2fs file systems. To change the behavior of ext2 file systems, you can use the tune2fs @@ -11234,13 +11558,13 @@ Ext2fs partitions can be read from within DOS using the ext2tool command line tool package (available via FTP (user: anonymous) from - ftp://metalab.unc.edu/pub/Linux/system/filesystems/ext2 ) and from + ) and from within Windows NT using the ext2nt command line tool package from - ftp://metalab.unc.edu/pub/Linux/utils/dos . Explore2fs is a + . Explore2fs is a graphical explorer for ext2fs partitions which runs on Windows 95 and Windows NT and includes experimental write support; it is available from - http://jnewbigin-pc.it.swin.edu.au/Linux/Explore2fs.htm . + . If you want to compile this file system as a module ( = code which can be inserted in and removed from the running kernel whenever you @@ -11270,6 +11594,12 @@ called bfs.o. Note that the file system of your root partition (the one containing the directory /) cannot be compiled as a module. +CMS file system support +CONFIG_CMS_FS + Read only support for CMS minidisk file systems found on IBM mainframe + systems. Only the basic format is supported so far. + If you don't know what CMS is you probably don't want to know any more. + Compressed ROM file system support CONFIG_CRAMFS Saying Y here includes support for CramFs (Compressed ROM File @@ -11341,7 +11671,7 @@ driver. If you have a CDROM drive and want to do more with it than just listen to audio CDs and watch its LEDs, say Y (and read Documentation/filesystems/isofs.txt and the CDROM-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto ), thereby enlarging + from ), thereby enlarging your kernel by about 27 KB; otherwise say N. If you want to compile this as a module ( = code which can be @@ -11355,7 +11685,7 @@ which allows for long filenames in unicode format (unicode is the new 16 bit character code, successor to ASCII, which encodes the characters of almost all languages of the world; see - http://www.unicode.org for more information). Say Y here if you want + for more information). Say Y here if you want to be able to read Joliet CDROMs under Linux. UDF File System support (read only) @@ -11430,8 +11760,8 @@ they are compressed; to access compressed MSDOS partitions under Linux, you can either use the DOS emulator DOSEMU, described in the DOSEMU-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , or try dmsdosfs in - ftp://metalab.unc.edu/pub/Linux/system/filesystems/dosfs . If you + , or try dmsdosfs in + . If you intend to use dosemu with a non-compressed MSDOS partition, say Y here) and MSDOS floppies. This means that file access becomes transparent, i.e. the MSDOS files look and behave just like all @@ -11491,7 +11821,7 @@ To get utilities for initializing/checking UMSDOS file system, or latest patches and/or information, visit the UMSDOS home page at - http://www.voyager.hr/~mnalis/umsdos/ . + . This option enlarges your kernel by about 28 KB and it only works if you said Y to both "DOS FAT fs support" and "MSDOS fs support" @@ -11584,7 +11914,7 @@ programs nfsd and mountd (but does not need to have NFS file system support enabled in its kernel). NFS is explained in the Network Administrator's Guide, available from - http://www.linuxdoc.org/docs.html#guide , on its man page: "man + , on its man page: "man nfs", and in the NFS-HOWTO. A superior but less widely used alternative to NFS is provided by @@ -11604,7 +11934,7 @@ below. You cannot compile this driver as a module in this case. There are two packages designed for booting diskless machines over the net: netboot and etherboot, both available via FTP from - ftp://metalab.unc.edu/pub/Linux/system/boot/ethernet/ . + . If you don't know what all this is about, say N. @@ -11646,7 +11976,7 @@ as well. Please read the NFS-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . The NFS server is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -11681,7 +12011,7 @@ say M here and read Documentation/modules.txt. If unsure, say N. FreeVxFS file system support (VERITAS VxFS(TM) compatible) -CONFIG_VXFS_FS +CONFIG_FREEVXFS_FS FreeVxFS is a filesystem driver that support the VERITAS VxFS(TM) filesystem format. VERITAS VxFS(TM) is the standard filesystem of SCO UnixWare (and possibly others) and optionally available @@ -11713,12 +12043,16 @@ NTFS write support (DANGEROUS) CONFIG_NTFS_RW If you say Y here, you will (maybe) be able to write to NTFS file - systems as well as read from them. The read-write support in - NTFS is far from being complete and is not well tested. If you - say Y here, back up your NTFS volume first since it may get - damaged. Also, make sure to run chkdsk from within Microsoft - Windows NT after having performed any writes to a NTFS partition - from Linux to detect any problems as early as possible. + systems as well as read from them. The read-write support in NTFS + is far from being complete and is not well tested. If you say Y + here, back up your NTFS volume first, since it will probably get + damaged. Also, download the Linux-NTFS project distribution from + Sourceforge at and + always run the included ntfsfix utility after writing to an NTFS + partition from Linux to fix some of the damage done by the driver. + You should run ntfsfix _after_ unmounting the partition in Linux + but _before_ rebooting into Windows. When Windows next boots, + chkdsk will be run automatically to fix the remaining damage. Please note that write support is limited to Windows NT4 and earlier versions. @@ -11738,7 +12072,7 @@ Xenix, Wyse, UnixWare, Dell Unix and System V programs under Linux and is often needed to run commercial software that's only available for those systems. It's available via FTP (user: anonymous) from - ftp://tsx-11.mit.edu/pub/linux/BETA ). + ). If you only intend to mount files from some other Unix over the network using NFS, you don't need the System V file system support @@ -11780,7 +12114,7 @@ fs/affs/Changes. With this driver you can also mount disk files used by Bernd - Schmidt's Un*X Amiga Emulator (http://www.freiburg.linux.de/~uae/ ). + Schmidt's Un*X Amiga Emulator (). If you want to do this, you will also need to say Y or M to "Loop device support", above. @@ -11870,7 +12204,7 @@ automounter (amd), which is a pure user space daemon. To use the automounter you need the user-space tools from - ftp://ftp.kernel.org/pub/linux/daemons/autofs/testing-v4 ; you also + ; you also want to answer Y to "NFS file system support", below. If you want to compile this as a module ( = code which can be @@ -11892,7 +12226,7 @@ This implementation only offers read-only access. If you don't know what all this is about, it's safe to say N. For more information - about EFS see its home page at http://aeschi.ch.eu.org/efs/ . + about EFS see its home page at . If you want to compile the EFS file system support as a module ( = code which can be inserted in and removed from the running kernel @@ -11904,12 +12238,36 @@ JFFS is the Journaling Flash File System developed by Axis Communications in Sweden, aimed at providing a crash/powerdown-safe filesystem for disk-less embedded devices. Further information is - available at (http://developer.axis.com/software/jffs/). + available at (). JFFS debugging verbosity CONFIG_JFFS_FS_VERBOSE Determines the verbosity level of the JFFS debugging messages. +Journalling Flash Filesystem version 2 +CONFIG_JFFS2_FS + JFFS2 is the second generation of the Journalling Flash File System + for use on diskless embedded devices. It provides improved wear + levelling, compression and support for hard links. You cannot use + this on normal block devices, only on 'MTD' devices. + + Further information should be made available soon at + + +JFFS2 debugging verbosity +CONFIG_JFFS2_FS_DEBUG + This controls the amount of debugging messages produced by the JFFS2 + code. Set it to zero for use in production systems. For evaluation, + testing and debugging, it's advisable to set it to one. This will + enable a few assertions and will print debugging messages at the + KERN_DEBUG loglevel, where they won't normally be visible. Level 2 + is unlikely to be useful - it enables extra debugging in certain + areas which at one point needed debugging, but when the bugs were + located and fixed, the detailed messages were relegated to level 2. + + If reporting bugs, please try to have available a full dump of the + messages at debug level 1 while the misbehaviour was occurring. + UFS file system support (read-only) CONFIG_UFS_FS BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD, @@ -11957,6 +12315,33 @@ If unsure, say N. +Acorn partition support +CONFIG_ACORN_PARTITION + Support hard disks partitioned under Acorn operating systems. + +Native filecore partition support +CONFIG_ACORN_PARTITION_ADFS + The Acorn Disc Filing System is the standard file system of the + RiscOS operating system which runs on Acorn's ARM-based Risc PC + systems and the Acorn Archimedes range of machines. If you say + `Y' here, Linux will support disk partitions created under ADFS. + +PowerTec partition support +CONFIG_ACORN_PARTITION_POWERTEC + Support reading partition tables created on Acorn machines using + the PowerTec SCSI drive. + +RISCiX partition support +CONFIG_ACORN_PARTITION_RISCIX + Once upon a time, there was a native Unix port for the Acorn series + of machines called RISCiX. If you say 'Y' here, Linux will be able to + read disks partitioned under RISCiX. + +ICS partition support +CONFIG_ACORN_PARTITION_ICS + Say Y here if you would like to use hard disks under Linux which + were partitioned using the ICS interface on Acorn machines. + Alpha OSF partition support CONFIG_OSF_PARTITION Say Y here if you would like to use hard disks under Linux which @@ -11972,6 +12357,16 @@ Say Y here if you would like to use hard disks under Linux which were partitioned on an x86 PC (not necessarily by DOS). +Amiga partition table support +CONFIG_AMIGA_PARTITION + Say Y here if you would like to use hard disks under Linux which + were partitioned under AmigaOS. + +Atari partition table support +CONFIG_ATARI_PARTITION + Say Y here if you would like to use hard disks under Linux which + were partitioned under the Atari OS. + BSD disklabel (FreeBSD partition tables) support CONFIG_BSD_DISKLABEL FreeBSD uses its own hard disk partition scheme on your PC. It @@ -12021,6 +12416,12 @@ partition table format used by DEC (now Compaq) Ultrix machines. Otherwise, say N. +IBM disk label and partition support +CONFIG_IBM_PARTITION + Say Y here if you would like to be able to read the hard disk + partition table format used by IBM DASD disks operating under CMS. + Otherwise, say N. + ADFS file system support (EXPERIMENTAL) CONFIG_ADFS_FS The Acorn Disc Filing System is the standard file system of the @@ -12095,16 +12496,16 @@ works only if the Windows machines use TCP/IP as the underlying transport protocol, and not NetBEUI. For details, read Documentation/filesystems/smbfs.txt and the SMB-HOWTO, available - from http://www.linuxdoc.org/docs.html#howto . + from . Note: if you just want your box to act as an SMB *server* and make files and printing services available to Windows clients (which need to have a TCP/IP stack), you don't need to say Y here; you can use the program samba (available via FTP (user: anonymous) in - ftp://metalab.unc.edu/pub/Linux/system/network/samba ) for that. + ) for that. General information about how to connect Linux, Windows machines and - Macs is on the WWW at http://www.eats.com/linux_mac_win.html . + Macs is on the WWW at . If you want to compile the SMB support as a module ( = code which can be inserted in and removed from the running kernel whenever you @@ -12149,7 +12550,7 @@ *client*. You will need user level code as well, both for the client and server. Servers are currently user level, i.e. they need no kernel support. Please read Documentation/filesystems/coda.txt and - check out the Coda home page http://www.coda.cs.cmu.edu . + check out the Coda home page . If you want to compile the coda client support as a module ( = code which can be inserted in and removed from the running kernel @@ -12164,13 +12565,13 @@ mount NetWare file server volumes and to access them just like any other Unix directory. For details, please read the file Documentation/filesystems/ncpfs.txt in the kernel source and the - IPX-HOWTO from http://www.linuxdoc.org/docs.html#howto . + IPX-HOWTO from . You do not have to say Y here if you want your Linux box to act as a file *server* for Novell NetWare clients. General information about how to connect Linux, Windows machines and - Macs is on the WWW at http://www.eats.com/linux_mac_win.html . + Macs is on the WWW at . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -12226,24 +12627,6 @@ effects by saying Y to "Allow using of Native Language Support" below. -Allow mounting of volume subdirectories -CONFIG_NCPFS_MOUNT_SUBDIR - Allows you to mount not only whole servers or whole volumes, but - also subdirectories from a volume. It can be used to reexport data - and so on. There is no reason to say N, so Y is recommended unless - you count every byte. - - To utilize this feature you must use ncpfs-2.0.12 or newer. - -NDS authentication support -CONFIG_NCPFS_NDS_DOMAINS - This allows storing NDS private keys in kernel space where they - can be used to authenticate another server as interserver NDS - accesses need it. You must use ncpfs-2.0.12.1 or newer to utilize - this feature. Say Y if you are using NDS connections to NetWare - servers. Do not say Y if security is primary for you because root - can read your session key (from /proc/kcore). - Allow using of Native Language Support CONFIG_NCPFS_NLS Allows you to use codepages and I/O charsets for file name @@ -12262,21 +12645,24 @@ To use the new attributes, it is recommended to use the flags '-f 600 -d 755' on the ncpmount command line. -nls default codepage +Default NLS Option CONFIG_NLS_DEFAULT - The default NLS used when mounting file system. Currently, the valid - values are: + The default NLS used when mounting file system. Note, that this is + the NLS used by your console, not the NLS used by a specyfic file + system (if different) to store data (filenames) on a disk. + Currently, the valid values are: big5, cp437, cp737, cp775, cp850, cp852, cp855, cp857, cp860, cp861, cp862, cp863, cp864, cp865, cp866, cp869, cp874, cp932, cp936, - cp949, cp950, euc-jp, euc-kr, gb2312, iso8859-1, iso8859-2, iso8859-3, - iso8859-4, iso8859-5, iso8859-6, iso8859-7, iso8859-8, iso8859-9, - iso8859-14, iso8859-15, koi8-r, sjis - If you specify a wrong value, it will use the built-in NLS; compatible - with iso8859-1. + cp949, cp950, cp1251, cp1255, euc-jp, euc-kr, gb2312, iso8859-1, + iso8859-2, iso8859-3, iso8859-4, iso8859-5, iso8859-6, iso8859-7, + iso8859-8, iso8859-9, iso8859-13, iso8859-14, iso8859-15, + koi8-r, koi8-ru, koi8-u, sjis, tis-620, utf8. + If you specify a wrong value, it will use the built-in NLS; + compatible with iso8859-1. If unsure, specify it as "iso8859-1". -nls codepage 437 +Codepage 437 (United States, Canada) CONFIG_NLS_CODEPAGE_437 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored @@ -12287,7 +12673,7 @@ say Y here if you want to include the DOS codepage that is used in the United States and parts of Canada. This is recommended. -nls codepage 737 +Codepage 737 (Greek) CONFIG_NLS_CODEPAGE_737 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored @@ -12298,7 +12684,7 @@ say Y here if you want to include the DOS codepage that is used for Greek. If unsure, say N. -nls codepage 775 +Codepage 775 (Baltic Rim) CONFIG_NLS_CODEPAGE_775 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored @@ -12307,9 +12693,10 @@ DOS/Windows partitions correctly. This does apply to the filenames only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage that is used - for the Baltic Rim Languages. If unsure, say N. + for the Baltic Rim Languages (Latvian and Lithuanian). If unsure, + say N. -nls codepage 850 +Codepage 850 (Europe) CONFIG_NLS_CODEPAGE_850 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12324,7 +12711,7 @@ If unsure, say Y. -nls codepage 852 +Codepage 852 (Central/Eastern Europe) CONFIG_NLS_CODEPAGE_852 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12338,7 +12725,7 @@ Finnish, Hungarian, Irish, German, Polish, Romanian, Serbian (Latin transcription), Slovak, Slovenian, and Sorbian. -nls codepage 855 +Codepage 855 (Cyrillic) CONFIG_NLS_CODEPAGE_855 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12348,7 +12735,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Cyrillic. -nls codepage 857 +Codepage 857 (Turkish) CONFIG_NLS_CODEPAGE_857 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12358,7 +12745,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Turkish. -nls codepage 860 +Codepage 860 (Portugese) CONFIG_NLS_CODEPAGE_860 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12368,7 +12755,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Portuguese. -nls codepage 861 +Codepage 861 (Icelandic) CONFIG_NLS_CODEPAGE_861 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12378,7 +12765,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Icelandic. -nls codepage 862 +Codepage 862 (Hebrew) CONFIG_NLS_CODEPAGE_862 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12388,7 +12775,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Hebrew. -nls codepage 863 +Codepage 863 (Canadian French) CONFIG_NLS_CODEPAGE_863 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12399,7 +12786,7 @@ say Y here if you want to include the DOS codepage for Canadian French. -nls codepage 864 +Codepage 864 (Arabic) CONFIG_NLS_CODEPAGE_864 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12409,7 +12796,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Arabic. -nls codepage 865 +Codepage 865 (Norwegian, Danish) CONFIG_NLS_CODEPAGE_865 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12420,7 +12807,7 @@ say Y here if you want to include the DOS codepage for the Nordic European countries. -nls codepage 866 +Codepage 866 (Cyrillic/Russian) CONFIG_NLS_CODEPAGE_866 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12431,7 +12818,7 @@ say Y here if you want to include the DOS codepage for Cyrillic/Russian. -nls codepage 869 +Codepage 869 (Greek) CONFIG_NLS_CODEPAGE_869 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12441,7 +12828,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Greek. -nls codepage 874 +Thai charset (CP874, TIS-620) CONFIG_NLS_CODEPAGE_874 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12451,7 +12838,18 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Thai. -nls codepage 932 +Windows CP1251 (Bulgarian, Belarussian) +CONFIG_NLS_CODEPAGE_1251 + The Microsoft FAT file system family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for Russian and + Bulgarian and Belorussian. + +Japanese charsets (Shift-JIS, EUC-JP) CONFIG_NLS_CODEPAGE_932 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12461,9 +12859,9 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for Shift-JIS or EUC-JP. To use EUC-JP, you can use 'euc-jp' as mount option or - NLS Default value during kernel configuration , instead of 'cp932' + NLS Default value during kernel configuration, instead of 'cp932'. -nls codepage 936 +Simplified Chinese charset (CP936, GB2312) CONFIG_NLS_CODEPAGE_936 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12474,7 +12872,7 @@ say Y here if you want to include the DOS codepage for Simplified Chinese(GBK). -nls codepage 949 +Korean charset (CP949, EUC-KR) CONFIG_NLS_CODEPAGE_949 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12484,7 +12882,7 @@ only, not to the file contents. You can include several codepages; say Y here if you want to include the DOS codepage for UHC. -nls codepage 950 +Traditional Chinese charset (Big5) CONFIG_NLS_CODEPAGE_950 The Microsoft FAT file system family can deal with filenames in native language character sets. These character sets are stored in @@ -12495,7 +12893,7 @@ say Y here if you want to include the DOS codepage for Traditional Chinese(Big5). -nls iso8859-1 +NLS ISO 8859-1 (Latin 1; Western European Languages) CONFIG_NLS_ISO8859_1 If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs @@ -12506,7 +12904,7 @@ Galician, Irish, Icelandic, Italian, Norwegian, Portuguese, Spanish, and Swedish. It is also the default for the US. If unsure, say Y. -nls iso8859-2 +NLS ISO 8859-2 (Latin 2; Slavic/Central European Languages) CONFIG_NLS_ISO8859_2 If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs @@ -12516,7 +12914,7 @@ languages: Czech, German, Hungarian, Polish, Rumanian, Croatian, Slovak, Slovene. -nls iso8859-3 +NLS ISO 8859-3 (Latin 3; Esperanto, Galician, Maltese, Turkish) CONFIG_NLS_ISO8859_3 If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs @@ -12525,16 +12923,16 @@ set, which is popular with authors of Esperanto, Galician, Maltese, and Turkish. -nls iso8859-4 +NLS ISO 8859-4 (Latin 4; old Baltic charset) CONFIG_NLS_ISO8859_4 If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs correctly on the screen, you need to include the appropriate input/output character sets. Say Y here for the Latin 4 character set which introduces letters for Estonian, Latvian, and - Lithuanian. It is an incomplete predecessor of Latin 6. + Lithuanian. It is an incomplete predecessor of Latin 7. -nls iso8859-5 +NLS ISO 8859-5 (Cyrillic) CONFIG_NLS_ISO8859_5 If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs @@ -12544,7 +12942,7 @@ Macedonian, Russian, Serbian, and Ukrainian. Note that the charset KOI8-R is preferred in Russia. -nls iso8859-6 +NLS ISO 8859-6 (Arabic) CONFIG_NLS_ISO8859_6 If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs @@ -12552,7 +12950,7 @@ input/output character sets. Say Y here for ISO8859-6, the Arabic character set. -nls iso8859-7 +NLS ISO 8859-7 (Modern Greek) CONFIG_NLS_ISO8859_7 If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs @@ -12560,7 +12958,7 @@ input/output character sets. Say Y here for ISO8859-7, the Modern Greek character set. -nls iso8859-8 +Hebrew charsets (ISO-8859-8, CP1255) CONFIG_NLS_ISO8859_8 If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs @@ -12568,7 +12966,7 @@ input/output character sets. Say Y here for ISO8859-8, the Hebrew character set. -nls iso8859-9 +NLS ISO 8859-9 (Latin 5; Turkish) CONFIG_NLS_ISO8859_9 If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs @@ -12577,7 +12975,7 @@ set, and it replaces the rarely needed Icelandic letters in Latin 1 with the Turkish ones. Useful in Turkey. -nls iso8859-10 +NLS ISO 8859-10 (Latin 6; Nordic) CONFIG_NLS_ISO8859_10 If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs @@ -12587,6 +12985,15 @@ letters that were missing in Latin 4 to cover the entire Nordic area. +NLS ISO 8859-13 (Latin 7; Baltic) +CONFIG_NLS_ISO8859_13 + If you want to display filenames with native language characters + from the Microsoft FAT file system family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the Latin 7 character + set, which supports modern Baltic languages including Latvian + and Lithuanian. + NLS ISO 8859-14 (Latin 8; Celtic) CONFIG_NLS_ISO8859_14 If you want to display filenames with native language characters @@ -12595,9 +13002,9 @@ input/output character sets. Say Y here for the Latin 8 character set, which adds the last accented vowels for Welsh (aka Cymraeg) (and Manx Gaelic) hat were missing in Latin 1. - http://linux.speech.cymru.org/ has further information. + has further information. -nls iso8859-15 +NLS ISO 8859-15 (Latin 9; Western European Languages with Euro) CONFIG_NLS_ISO8859_15 If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs @@ -12609,10 +13016,10 @@ Portuguese, Spanish, and Swedish. Latin 9 is an update to Latin 1 (ISO 8859-1) that removes a handful of rarely used characters and instead adds support for Estonian, corrects the - support for French and Finnish, and adds the new Euro character. If - unsure, say Y. + support for French and Finnish, and adds the new Euro character. + If unsure, say Y. -nls koi8-r +NLS KOI8-R (Russian) CONFIG_NLS_KOI8_R If you want to display filenames with native language characters from the Microsoft FAT file system family or from JOLIET CDROMs @@ -12620,6 +13027,22 @@ input/output character sets. Say Y here for the preferred Russian character set. +NLS KOI8-U/RU (Ukrainian, Belarussian) +CONFIG_NLS_KOI8_U + If you want to display filenames with native language characters + from the Microsoft FAT file system family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the preferred Ukrainian + (koi8-u) and Belarussian (koi8-ru) character sets. + +NLS UTF8 +CONFIG_NLS_UTF8 + If you want to display filenames with native language characters + from the Microsoft FAT file system family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the UTF-8 encoding of + the Unicode/ISO9646 universal character set. + Virtual terminal CONFIG_VT If you say Y here, you will get support for terminal devices with @@ -12667,8 +13090,20 @@ If unsure, say Y. +Apple Desktop Bus (ADB) support +CONFIG_ADB + Include support for peripherals connected via the Apple Desktop Bus, ADB. + +Include CUDA ADB driver +CONFIG_ADB_CUDA + Macintosh PowerPCs feature an intelligent power switch called a Cuda; + its job is to turn system power on and off, manage system resets from + various commands, maintain parameter RAM (PRAM), and manage the + real-time clock. If you say `Y' the Linux kernel will support the + Cuda directly. + Support for PowerMac keyboard -CONFIG_MAC_KEYBOARD +CONFIG_ADB_KEYBOARD This option allows you to use an ADB keyboard attached to your machine. Note that this disables any other (ie. PS/2) keyboard support, even if your machine is physically capable of using both at @@ -12677,6 +13112,13 @@ If you use an ADB keyboard (4 pin connector), say Y here. If you use a PS/2 keyboard (6 pin connector), say N here. +Include IOP (IIfx/Quadra 9x0) ADB driver +CONFIG_ADB_IOP + The I/O Processor (IOP) is an Apple custom IC designed to provide + intelligent support for I/O controllers. It is described at + to enable direct + support for it, say 'Y' here. + Standard/generic serial support CONFIG_SERIAL This selects whether you want to include the driver for the standard @@ -12986,7 +13428,7 @@ box (as opposed to using a serial printer; if the connector at the printer has 9 or 25 holes ["female"], then it's serial), say Y. Also read the Printing-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . It is possible to share one parallel port among several devices (e.g. printer and ZIP drive) and it is safe to compile the @@ -13035,6 +13477,12 @@ If unsure, say N. +iSeries Virtual Console Support +CONFIG_VIOCONS + If you are running Linux on an iSeries system you really want to + say Y here, otherwise you really want to say N. This allows + Linux to route it's console I/O to OS/400. + I2C support CONFIG_I2C I2C (pronounce: I-square-C) is a slow serial bus protocol used in @@ -13135,7 +13583,7 @@ Microsoft mouse (made by Logitech) that plugs into a COM port (rectangular with 9 or 25 pins). These people say N here. If you have something else, read the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto , and say Y here. + , and say Y here. If you have a laptop, you either have to check the documentation or experiment a bit to find out whether the trackball is a serial mouse @@ -13158,7 +13606,7 @@ MouseSystem or Microsoft mouse (made by Logitech) that plugs into a COM port (rectangular with 9 or 25 pins). These people say N here. If you have something else, read the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . This HOWTO contains + . This HOWTO contains information about all non-serial mice, not just bus mice. If you have a laptop, you either have to check the documentation or @@ -13175,7 +13623,7 @@ generally a round connector with 9 pins. Note that the newer mice made by Logitech don't use the Logitech protocol anymore; for those, you don't need this option. You want to read the Busmouse-HOWTO , - available from http://www.linuxdoc.org/docs.html#howto . + available from . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -13195,12 +13643,12 @@ Although PS/2 mice are not technically bus mice, they are explained in detail in the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . When using a PS/2 mouse, you can get problems if you want to use the mouse both on the Linux console and under X. Using the "-R" option of the Linux mouse managing program gpm (available from - ftp://metalab.unc.edu/pub/Linux/system/mouse ) solves this + ) solves this problem, or you can get the "mconv2" utility from the same location. C&T 82C710 mouse port support (as on TI Travelmate) @@ -13208,7 +13656,7 @@ This is a certain kind of PS/2 mouse used on the TI Travelmate. If you are unsure, try first to say N here and come back if the mouse doesn't work. Read the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . PC110 digitizer pad support CONFIG_PC110_PAD @@ -13226,7 +13674,7 @@ These animals (also called Inport mice) are connected to an expansion board using a round connector with 9 pins. If this is what you have, say Y and read the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you are unsure, say N and read the HOWTO nevertheless: it will tell you what you have. Also be aware that several vendors talk @@ -13250,7 +13698,7 @@ CONFIG_ADBMOUSE Say Y here if you have this type of bus mouse (4 pin connector) as is common on Macintoshes. You may want to read the Busmouse-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto . + available from . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -13264,7 +13712,7 @@ most mice by ATI are actually Microsoft busmice; you should say Y to "Microsoft busmouse support" above if you have one of those. Read the Busmouse-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -13282,6 +13730,11 @@ and read Documentation/modules.txt. The module will be called tpqic02.o. +iSeries Virtual Tape Support +CONFIG_VIOTAPE + If you are running Linux on an iSeries system and you want Linux + to read and/or write a tape drive owned by OS/400, say Y here. + Do you want runtime configuration for QIC-02 CONFIG_QIC02_DYNCONF You can either configure this driver once and for all by editing a @@ -13323,7 +13776,7 @@ Note that the Ftape-HOWTO is out of date (sorry) and documents the older version 2.08 of this software but still contains useful information. There is a web page with more recent documentation at - http://www.math1.rwth-aachen.de/~heine/ftape/ . This page + . This page always contains the latest release of the ftape driver and useful information (backup software, ftape related patches and documentation, FAQ). Note that the file system interface has changed @@ -13358,7 +13811,7 @@ file Documentation/ftape.txt contains a short description of the most important changes in the file system interface compared to previous versions of ftape. The ftape home page - http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/ contains + contains further information. IMPORTANT NOTE: zftape can read archives created by previous @@ -13587,7 +14040,7 @@ introduced in XFree86 4.0. If you say Y here, you need to select the module that's right for your graphics card from the list below. These modules provide support for synchronization, security, and - DMA transfers. Please see http://dri.sourceforge.net for more + DMA transfers. Please see for more details. You should also select and configure AGP (/dev/agpgart) support. @@ -13607,6 +14060,14 @@ is selected, the module will be called r128.o. AGP support for this card is strongly suggested (unless you have a PCI version). +ATI Radeon +CONFIG_DRM_RADEON + Choose this option if you have an ATI Radeon graphics card. There + are both PCI and AGP versions. You don't need to choose this to + run the Radeon in plain VGA mode. There is a product page at + . + If M is selected, the module will be called radeon.o. + Intel I810 CONFIG_DRM_I810 Choose this option if you have an Intel I810 graphics card. If M is @@ -13619,6 +14080,12 @@ is selected, the module will be called mga.o. AGP support is required for this driver to work. +Creator/Creator3D/Elite3D +CONFIG_DRM_FFB + Choose this option if you have one of Sun's Creator3D-based graphics + and frame buffer cards. Product page at + . + MTRR control and configuration CONFIG_MTRR On Intel P6 family processors (Pentium Pro, Pentium II and later) @@ -13661,7 +14128,7 @@ Double Talk PC internal speech card support CONFIG_DTLK This driver is for the DoubleTalk PC, a speech synthesizer - manufactured by RC Systems (http://www.rcsys.com/ ). It is also + manufactured by RC Systems (). It is also called the `internal DoubleTalk'. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read @@ -13685,7 +14152,7 @@ This driver provides the kernel-side support for the intelligent fieldbus cards made by Applicom International. More information about these cards can be found on the WWW at the address - http://www.applicom-int.com/ , or by email from David Woodhouse + , or by email from David Woodhouse . To compile this driver as a module ( = code which can be inserted in @@ -13722,15 +14189,15 @@ Power Management is most important for battery powered laptop computers; if you have a laptop, check out the Linux Laptop home page on the WWW at - http://www.cs.utexas.edu/users/kharker/linux-laptop/ and the Battery + and the Battery Powered Linux mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . Note that, even if you say N here, Linux on the x86 architecture will issue the hlt instruction if nothing is to be done, thereby sending the processor to sleep and saving power. -ACPI Support +ACPI support CONFIG_ACPI ACPI/OSPM support for Linux is currently under development. As such, this support is preliminary and EXPERIMENTAL. Configuring ACPI support @@ -13755,10 +14222,20 @@ Component Architecture (ACPI CA). The latest ACPI CA source code, documentation, debug builds, and implementation status information can be downloaded from: - http://developer.intel.com/technology/iapc/acpi/downloads.htm - + + The ACPI mailing list may also be of interest: - http://phobos.fs.tum.de/acpi/index.html + + +Enable ACPI 2.0 with errata 1.3 +CONFIG_ACPI20 + Enable support for the 2.0 version of the ACPI interpreter. See the + help for ACPI for caveats and discussion. + +ACPI kernel configuration manager +CONFIG_ACPI_KERNEL_CONFIG + If you say `Y' here, Linux's ACPI support will use the hardware-level + system descriptions found on IA64 machines. Advanced Power Management BIOS support CONFIG_APM @@ -13778,7 +14255,7 @@ In order to use APM, you will need supporting software. For location and more information, read Documentation/pm.txt and the Battery Powered Linux mini-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . + . This driver does not spin down disk drives (see the hdparm(8) manpage ("man 8 hdparm") for that), and it doesn't turn off @@ -13807,7 +14284,7 @@ 5) pass the "mem=4M" option to the kernel (thereby disabling all but the first 4 MB of RAM) 6) make sure that the CPU is not over clocked. - 7) read the sig11 FAQ at http://www.bitwizard.nl/sig11/ + 7) read the sig11 FAQ at 8) disable the cache from your BIOS settings 9) install a fan for the video card or exchange video RAM 10) install a better fan for the CPU @@ -13906,7 +14383,7 @@ The watchdog is usually used together with the watchdog daemon which is available via FTP (user: anonymous) from - ftp://tsx-11.mit.edu/pub/linux/sources/sbin/ . This daemon can also + . This daemon can also monitor NFS connections and can reboot the machine when the process table is full. @@ -13980,7 +14457,7 @@ and if it does, it reboots your computer after a certain amount of time. This driver is like the WDT501 driver but for different hardware. Please read Documentation/pcwd-watchdog.txt. The PC - watchdog cards can be ordered from http://www.berkprod.com . + watchdog cards can be ordered from . This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -14003,8 +14480,28 @@ module, say M here and read Documentation/modules.txt. Most people will say N. +Advantech SBC Watchdog Timer +CONFIG_ADVANTECH_WDT + If you are configuring a Linux kernel for the Advantech single-board + computer, say `Y' here to support its built-in watchdog timer feature. + See the help for CONFIG_WATCHDOG for discussion. + +W83877F Watchdog Timer +CONFIG_W83877F_WDT + This is the driver for the hardware watchdog on the W83877F chipset + as used in EMACS PC-104 motherboards (and likely others). This + watchdog simply watches your kernel to make sure it doesn't freeze, + and if it does, it reboots your computer after a certain amount of + time. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module is called mixcomwd.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. Most people + will say N. + Mixcom Watchdog -CONFIG_MIXCOMWD +CONFIG_MIXCOMWD This is a driver for the Mixcom hardware watchdog cards. This watchdog simply watches your kernel to make sure it doesn't freeze, and if it does, it reboots your computer after a certain amount of @@ -14037,7 +14534,7 @@ For information on utilities to make use of this driver see the Toshiba Linux utilities web site at: - http://www.buzzard.org.uk/toshiba/ + Say Y if you intend to run this kernel on a Toshiba portable. Say N otherwise. @@ -14052,7 +14549,7 @@ For latest news and information on obtaining all the required ingredients for this driver, check: - http://www.urbanmyth.org/microcode/ + This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -14184,6 +14681,12 @@ The module will be called lightning.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +Crystal SoundFusion gameports +CONFIG_INPUT_CS461X + Say Y here if you have a Cirrus CS461x aka "Crystal SoundFusion" + PCI audio accelerator. A product page for the CS4614 is at + . + Aureal Vortex and Trident 4DWave gameports CONFIG_INPUT_PCIGAME Say Y here if you have a Trident 4DWave DX/NX or Aureal Vortex 1/2 @@ -14350,6 +14853,11 @@ connected to your computer's serial port. For more information on how to use the driver please read Documentation/joystick.txt +Gravis Stinger gamepad +CONFIG_INPUT_STINGER + Say Y here if you have a Gravis Stinger connected to one of your serial + ports. Gravis has a home page at . + I-Force joysticks/wheels CONFIG_INPUT_IFORCE_232 Say Y here if you have an I-Force joystick or steering wheel @@ -14476,7 +14984,7 @@ interrupt and DMA channel), because you will be asked for it. You want to read the Sound-HOWTO, available from - http://www.linuxdoc.org/docs.html#howto . General information + . General information about the modular sound system is contained in the files Documentation/sound/Introduction. The file Documentation/sound/README.OSS contains some slightly outdated but @@ -14484,7 +14992,7 @@ If you have a PnP sound card and you want to configure it at boot time using the ISA PnP tools (read - http://www.roestock.demon.co.uk/isapnptools/ ), then you need to + ), then you need to compile the sound card support as a module ( = code which can be inserted in and removed from the running kernel whenever you want) and load that module after the PnP configuration is finished. To do @@ -14495,7 +15003,7 @@ I'm told that even without a sound card, you can make your computer say more than an occasional beep, by programming the PC speaker. Kernel patches and supporting utilities to do that are in the pcsp - package, available at ftp://ftp.infradead.org/pub/pcsp/ . + package, available at . OSS sound modules CONFIG_SOUND_OSS @@ -14883,24 +15391,37 @@ If unsure, say Y. -ACI mixer (miroPCM12/PCM20) +Yamaha PCI native mode support (EXPERIMENTAL) +CONFIG_SOUND_YMFPCI + This is an experimental driver that uses Yamaha PCI sound cards in + the native mode. You may also want to try another driver, + "Yamaha PCI legacy mode support" under the OSS drivers. + +Yamaha PCI legacy mode support +CONFIG_SOUND_YMPCI + This is a driver for Yamaha PCI sound cards that turns them + to the Sound Blaster compatible mode. You don't need to enable + Sound Blaster support to use it. + +ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio) CONFIG_SOUND_ACI_MIXER ACI (Audio Command Interface) is a protocol used to communicate with - the microcontroller on some sound cards produced by miro, e.g. the - miroSOUND PCM12 and PCM20. The main function of the ACI is to - control the mixer and to get a product identification. - - This Voxware ACI driver currently only supports the ACI functions on - the miroSOUND PCM12 and PCM20 cards. On the PCM20, ACI also controls - the radio tuner. This is supported in the video4linux - radio-miropcm20 driver. + the microcontroller on some sound cards produced by miro and Cardinal + Technologies. The main function of the ACI is to control the mixer + and to get a product identification. + + This Voxware ACI driver currently supports the ACI functions on the + miroSOUND PCM1-pro, PCM12 and PCM20 radio. On the PCM20 radio, ACI + also controls the radio tuner. This is supported in the video4linux + miropcm20 driver (say M or Y here and go back to "Multimedia devices" + -> "Radio Adapters"). SB32/AWE support CONFIG_SOUND_AWE32_SYNTH Say Y here if you have a Sound Blaster SB32, AWE32-PnP, SB AWE64 or similar sound card. See Documentation/sound/README.awe, Documentation/sound/AWE32 and the Soundblaster-AWE mini-HOWTO, - available from http://www.linuxdoc.org/docs.html#howto for more + available from for more info. Gallant's Audio Excel DSP 16 support (SC-6000 and SC-6600) @@ -15132,7 +15653,7 @@ conversations while downloading stuff. It only works if your computer is equipped with an ISDN card and both you and your service provider purchased an ISDN line from the phone company. For details, - read http://alumni.caltech.edu/~dank/isdn/ on the WWW. + read on the WWW. This driver allows you to use an ISDN-card for networking connections and as dialin/out device. The isdn-tty's have a built in @@ -15500,6 +16021,16 @@ This enables HiSax support for the AMD7930 chips on some SPARCs. This code is not finished yet. +ELSA PCMCIA MicroLink cards +CONFIG_HISAX_ELSA_CS + This enables the PCMCIA client driver for the Elsa PCMCIA MicroLink + card. + +Sedlbauer PCMCIA cards +CONFIG_HISAX_SEDLBAUER_CS + This enables the PCMCIA client driver for the Sedlbauer Speed Star + and Speed Star II cards. + PCBIT-D support CONFIG_ISDN_DRV_PCBIT This enables support for the PCBIT ISDN-card. This card is @@ -15521,7 +16052,7 @@ can be inserted in and removed from the running kernel whenever you want, details in Documentation/modules.txt); the module will be called sc.o. See Documentation/isdn/README.sc and - http://www.spellcast.com for more information. + for more information. Eicon active card support CONFIG_ISDN_DRV_EICON @@ -15531,11 +16062,16 @@ latest isdn4k-utils package. Please read the file Documentation/isdn/README.eicon for more information. +Eicon legacy driver +CONFIG_ISDN_DRV_EICON_OLD + Say Y here to use your Eicon active ISDN card with ISDN4Linux + isdn module. + Eicon Diva Server card support CONFIG_ISDN_DRV_EICON_PCI Say Y here if you have an Eicon Diva Server (BRI/PRI/4BRI) ISDN card. Please read Documentation/isdn/README.eicon for more information. - + Eicon old-type card support CONFIG_ISDN_DRV_EICON_ISA Say Y here if you have an old-type Eicon active ISDN card. In order @@ -15545,7 +16081,7 @@ Documentation/isdn/README.eicon for more information. Eicon driver type standalone -CONFIG_ISDN_DRV_EICON_STANDALONE +CONFIG_ISDN_DRV_EICON_DIVAS Enable this option if you want the eicon driver as standalone version with no interface to the ISDN4Linux isdn module. If you say Y here, the eicon module only supports the Diva Server PCI @@ -15564,7 +16100,7 @@ CONFIG_ISDN_CAPI This provides the CAPI (Common ISDN Application Programming Interface, a standard making it easy for programs to access ISDN - hardware, see http://www.capi.org/ . This is needed for AVM's set of + hardware, see . This is needed for AVM's set of active ISDN controllers like B1, T1, M1. This code is also available as a module ( = code which can be @@ -15573,6 +16109,33 @@ compile it as a module, say M here and read Documentation/modules.txt. +CAPI2.0 /dev/capi20 support +CONFIG_ISDN_CAPI_CAPI20 + This option will provide the CAPI 2.0 interface to userspace + applications via /dev/capi20. Applications should use the standardized + libcapi20 to access this functionality. You should say Y/M here. + +CAPI2.0 Middleware support +CONFIG_ISDN_CAPI_MIDDLEWARE + This option will enhance the capabilities of the /dev/capi20 interface. + It will provide a means of moving a data connection, established + via the usual /dev/capi20 interface to a special tty device. If you want + to use pppd with pppdcapiplugin to dial up to your ISP, say Y here. + +CAPI2.0 filesystem support +CONFIG_ISDN_CAPI_CAPIFS_BOOL + This option provides a special file system, similar to /dev/pts with + device nodes for the special ttys established by using the middleware + extension above. If you want to use pppd with pppdcapiplugin to dial up + to your ISP, say Y here. + +CAPI2.0 capidrv interface support +CONFIG_ISDN_CAPI_CAPIDRV + This option provides the glue code to hook up CAPI driven cards to + the legacy isdn4linux link layer. If you have a card which is supported + by a CAPI driver, but still want to use old features like ippp + interfaces or ttyI emulation, say Y/M here. + AVM B1 ISA support CONFIG_ISDN_DRV_AVMB1_B1ISA Enable support for the ISA version of the AVM B1 card. @@ -15594,6 +16157,11 @@ CONFIG_ISDN_DRV_AVMB1_B1PCMCIA Enable support for the PCMCIA version of the AVM B1 card. +AVM B1/M1/M2 PCMCIA cs module +CONFIG_ISDN_DRV_AVMB1_AVM_CS + Enable the PCMCIA client driver for the AVM B1/M1/M2 + PCMCIA cards. + AVM T1/T1-B PCI support CONFIG_ISDN_DRV_AVMB1_T1PCI Enable support for the AVM T1 T1B card. @@ -15698,7 +16266,7 @@ CONFIG_SUNOS_EMUL This allows you to run most SunOS binaries. If you want to do this, say Y here and place appropriate files in /usr/gnemul/sunos. See - http://www.ultralinux.org/faq.html for more information. If you want + for more information. If you want to run SunOS binaries on an Ultra you must also say Y to "Kernel support for 32-bit a.out binaries" above. @@ -15735,7 +16303,7 @@ This driver provides support for the build-in sound devices on most Sun machines. If you want to be able to use this, select this option and one or more of the lowlevel drivers below. See - http://www.dementia.org/~shadow/sparcaudio.html for more + for more information. AMD7930 Lowlevel Driver @@ -15775,6 +16343,16 @@ you plan to use this kernel on an Amiga, say Y here and browse the material available in Documentation/m68k; otherwise say N. +A4000T SCSI support +CONFIG_A4000T_SCSI + Support for the NCR53C710 SCSI controller on the Amiga 4000T. + +A4091 SCSI support +CONFIG_A4091_SCSI + Support for the NCR53C710 chip on the Amiga 4091 Z3 SCSI2 controller + (1993). Very obscure -- the 4091 was part of an Amiga 4000 upgrade plan + at the time the Amiga business was sold to DKB. + Atari support CONFIG_ATARI This option enables support for the 68000-based Atari series of @@ -15811,6 +16389,12 @@ If you don't want to compile a kernel for a Sun 3x, say N. +Sun3x builtin serial support +CONFIG_SUN3X_ZS + ZS refers to a type of asynchronous serial port built in to the Sun3 + and Sun3x workstations; if you have a Sun 3, you probably have these. + Say 'Y' to support ZS ports directly. + 68020 support CONFIG_M68020 If you anticipate running this kernel on a computer with a MC68020 @@ -16036,6 +16620,12 @@ If you have the Phase5 Fastlane Z3 SCSI controller, or plan to use one in the near future, say Y to this question. Otherwise, say N. +BSC Oktagon SCSI support +OKTAGON_SCSI + If you have the BSC Oktagon SCSI disk controller for the Amiga, say Y to + this question. If you're in doubt about whether you have one, see the + picture at . + Atari native SCSI support CONFIG_ATARI_SCSI If you have an Atari with built-in NCR5380 SCSI controller (TT, @@ -16056,6 +16646,12 @@ use a Toshiba CD-ROM drive; otherwise, the option is not needed and would impact performance a bit, so say N. +Reset SCSI-devices at boottime +CONFIG_ATARI_SCSI_RESET_BOOT + Reset the devices on your Atari whenever it boots. This makes the boot + process fractionally longer but may assist recovery from errors that + leave the devices with SCSI operations partway completed. + Hades SCSI DMA emulator CONFIG_TT_DMA_EMUL This option enables code which emulates the TT SCSI DMA chip on the @@ -16218,6 +16814,16 @@ If you want to use a GVP IO-Extender serial card in Linux, say Y. Otherwise, say N. +GVP IO-Extender parallel printer support +CONFIG_GVPIOEXT_LP + Say Y to enable driving a printer from the parallel port on your + GVP IO-Extender card, N otherwise + +GVP IO-Extender PLIP support +CONFIG_GVPIOEXT_PLIP + Say Y to enable doing IP over the parallel port on your GVP IO-Extender + card, N otherwise + Multiface Card III serial support CONFIG_MULTIFACE_III_TTY If you want to use a Multiface III card's serial port in Linux, @@ -16227,6 +16833,16 @@ 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. + +Commodore A2232 serial support +CONFIG_A2232 + If you want to enable support for Commodore A2232 seven-port + serial boards, answer Y. + + This driver can be built as a module; but then "generic_serial.o" + will also be built as a module. This has to be loaded before + "ser_a2232.o". If you want to do this, answer M here and read + "Documentation/modules.txt". Atari DMA sound support CONFIG_DMASOUND_ATARI @@ -16339,7 +16955,7 @@ must get the power management daemon, pmud, to make it work and you must have the /dev/pmu device (see the pmud README). - Get pmud from ftp://linuxcare.com.au/pub/ppclinux/pmud/ + Get pmud from . If you have a PowerBook, you should say Y. @@ -16351,7 +16967,7 @@ CONFIG_MOL This option enables low-level support for Mac-on-Linux. MOL lets you run MacOS and Linux simultaneously. Please - visit for more information. + visit <>for more information. If unsure, say Y. ADB raw keycode support @@ -16473,7 +17089,7 @@ The driver will default to AAUI on ANS anyway, and if you use it as a module, you can provide the port_aaui=0|1 to force the driver setting. - + BMAC (G3 ethernet) support CONFIG_BMAC Say Y for support of BMAC Ethernet interfaces. These are used on G3 @@ -16494,16 +17110,6 @@ whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. -Symbios 53c885 (Synergy ethernet) support -CONFIG_NCR885E - This is and Ethernet driver for the dual-function NCR 53C885 - SCSI/Ethernet controller. - - This driver is also available as a module called ncr885e.o ( = 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. - National DP83902AV (Oak ethernet) support CONFIG_OAKNET Say Y if your machine has this type of Ethernet network card. @@ -16518,11 +17124,11 @@ Support for audio/video capture and overlay devices and FM radio cards. The exact capabilities of each device vary. User tools for this are available from - ftp://ftp.uk.linux.org/pub/linux/video4linux . + . If you are interested in writing a driver for such an audio/video device or user software interacting with such a driver, please read - the file Documentation/video4linux/API.html. + the file Documentation/video4linux/API.html . This driver is also available as a module called videodev.o ( = code which can be inserted in and removed from the running kernel @@ -16554,7 +17160,7 @@ In order to control your radio card, you will need to use programs that are compatible with the Video for Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . More information + . More information is contained in the file Documentation/video4linux/radiotrack.txt. If you want to compile this driver as a module ( = code which can be @@ -16575,7 +17181,7 @@ In order to control your radio card, you will need to use programs that are compatible with the Video for Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -16595,7 +17201,7 @@ In order to control your radio card, you will need to use programs that are compatible with the Video for Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -16616,10 +17222,10 @@ In order to control your radio card, you will need to use programs that are compatible with the Video for Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . Further documentation on this driver can be found on the WWW at - http://linux.blackhawke.net/cadet.html . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -16634,7 +17240,7 @@ In order to control your radio card, you will need to use programs that are compatible with the Video for Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -16653,7 +17259,7 @@ In order to control your radio card, you will need to use programs that are compatible with the Video for Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -16689,7 +17295,7 @@ In order to control your radio card, you will need to use programs that are compatible with the Video for Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -16711,21 +17317,36 @@ say M here and read Documentation/modules.txt. The module will be called i2c-parport.o. -Miro PCM20 Radio +miroSOUND PCM20 radio CONFIG_RADIO_MIROPCM20 Choose Y here if you have this FM radio card. You also need to say Y - to "ACI mixer (miroPCM12/PCM20)" (in "additional low level sound - drivers") for this to work. + to "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio)" (in "Sound") + for this to work. + + In order to control your radio card, you will need to use programs + that are compatible with the Video for Linux API. Information on + this API and pointers to "v4l" programs may be found on the WWW at + . + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. The module will be + called miropcm20.o + +Guillemot MAXI Radio FM 2000 Radio Card +CONFIG_RADIO_MAXIRADIO + Choose Y here if you have this radio card. This card may also be + found as Gemtek PCI FM. In order to control your radio card, you will need to use programs that are compatible with the Video for Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. The module will be - called radio-miropcm20.o + called radio-maxiradio.o GemTek Radio Card CONFIG_RADIO_GEMTEK @@ -16735,7 +17356,7 @@ In order to control your radio card, you will need to use programs that are compatible with the Video for Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -16754,7 +17375,7 @@ PlanB is the V4L driver for the PowerMac 7x00/8x00 series video input hardware. If you want to experiment with this, say Y. Otherwise, or if you don't understand a word, say N. - See http://www.cpu.lu/~mlan/planb.html for more info. + See for more info. Saying M will compile this driver as a module (planb.o). @@ -16772,7 +17393,7 @@ In order to control your radio card, you will need to use programs that are compatible with the Video for Linux API. Information on this API and pointers to "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . + . If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), @@ -16784,13 +17405,6 @@ Fill in the i/o port of your TerraTec FM radio card. If unsure, go with the default. -### Add these -# Zoran ZR36057/36060 support -# CONFIG_VIDEO_ZORAN - -# Include support for Iomega Buz -# CONFIG_VIDEO_BUZ - Trust FM radio card CONFIG_RADIO_TRUST This is a driver for the Trust FM radio cards. Say Y if you have @@ -16820,6 +17434,17 @@ whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. +Stradis 4:2:2 MPEG-2 video driver +CONFIG_VIDEO_STRADIS + Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video driver + for PCI. There is a product page at . + +Zoran ZR36057/36060 Video For Linux +CONFIG_VIDEO_ZORAN + Say Y here to include support for video cards based on the the Zoran + ZR36057/36060 encoder/decoder chip (including the Iomega Buz and the + Miro DC10 and DC30 video capture cards). + ZR36120/36125 Video for Linux CONFIG_VIDEO_ZR36120 Support for ZR36120/ZR36125 based frame grabber/overlay boards. @@ -16860,6 +17485,17 @@ monochrome Quickcam, Quickcam VC or QuickClip. It is also available as a module (c-qcam.o). Read Documentation/video4linux/CQcam.txt for more information. + +Winbond W9966CF Webcam Video For Linux +CONFIG_VIDEO_W9966 + Video4linux driver for Winbond's w9966 based Webcams. + Currently tested with the LifeView FlyCam Supra. + If you have one of these cameras, say Y here + otherwise say N. + This driver is also available as a module (w9966.o) + + Checkout Documentation/video4linux/w9966.txt and w9966.c + for more information. CPiA Video For Linux CONFIG_VIDEO_CPIA @@ -16959,6 +17595,35 @@ CONFIG_DASD_FBA FBA devices are currently unsupported. +Support for 3215 line mode terminal +CONFIG_3215 + Include support for IBM 3215 line-mode terminals. Can't be used + if 3270 console support is chosen. + +Support for console on 3215 line mode terminal +CONFIG_3215_CONSOLE + Include support for using an IBM 3215 line-mode terminal as the Linux + system console. Can't be used if 3270 console support is chosen. + +Support for 3270 line mode terminal +CONFIG_3270 + Include support for IBM 3270 line-mode terminals. + +Support for console on 3270 line mode terminal +CONFIG_3270_CONSOLE + Include support for using an IBM 3270 line-mode terminal as the Linux + system console. Excludes using 3215s. Available only if 3270 + support is compiled in statically. + +Support for HWC line mode terminal +CONFIG_HWC + Include support for IBM HWC line-mode terminals. + +Support for console on HWC line mode terminal +CONFIG_HWC_CONSOLE + Include support for using an IBM HWC line-mode terminal as the Linux + system console. + SAB3036 tuner support CONFIG_TUNER_3036 Say Y here to include support for Philips SAB3036 compatible tuners. @@ -17007,7 +17672,7 @@ L7200 Software Development Board which uses an ARM720T processor. Information on this board can be obtained at: - http://www.linkupsys.com/ + If you have any questions or comments about the Linux kernel port to this board, send e-mail to sjhill@cotw.com @@ -17017,7 +17682,7 @@ Say Y here if you intend to run this kernel on the Rebel.COM NetWinder. Information about this machine can be found at: - http://www.netwinder.org/ + Saying N will reduce the size of the Footbridge kernel. @@ -17032,8 +17697,8 @@ There are no product plans beyond the current research prototypes at this time. Information is available at: - http://crl.research.compaq.com/projects/personalserver - + + If you have any questions or comments about the Compaq Personal Server, send e-mail to skiff@crl.dec.com @@ -17054,8 +17719,8 @@ H3600 handheld computer. Information about this machine and the Linux port to this machine can be found at: - http://www.handhelds.org/Compaq/index.html#iPAQ_H3600 - http://www.compaq.com/products/handhelds/pocketpc/ + + Include support for Brutus CONFIG_SA1100_BRUTUS @@ -17065,20 +17730,20 @@ Include support for LART CONFIG_SA1100_LART Say Y here if you are using the Linux Advanced Radio Terminal - (also known as the LART). See http://www.lart.tudelft.nl/ for + (also known as the LART). See for information on the LART. Include support for GraphicsClient CONFIG_SA1100_GRAPHICSCLIENT Say Y here if you are using an Applied Data Systems Intel(R) StrongARM(R) SA-1100 based Graphics Client SBC. See - http://www.flatpanels.com/ for information on this system. + for information on this system. Include support for Victor CONFIG_SA1100_VICTOR Say Y here if you are using a Visu Aide Intel(R) StrongARM(R) SA-1100 based Victor Digital Talking Book Reader. See - http://www.visuaide.com/pagevictor.en.html for information on + for information on this system. Load kernel using Angel Debug Monitor @@ -17172,18 +17837,6 @@ Say Y here if you want the low-level print routines to direct their output to the serial port in the DC21285 (Footbridge). -Split initialisation functions into discardable section -CONFIG_TEXT_SECTIONS - If you say Y here, kernel code that is only used during - initialisation is collected into a special area of the kernel so - that it can be discarded and the memory reclaimed when - initialisation is complete. In addition, if the kernel you wish to - build is able to run on multiple architectures, it allows the unused - code to be discarded. Some versions of binutils, however, have a bug - that causes the kernel to crash during startup when this option is - enabled. Say Y unless you experience problems that you suspect may - be caused by this. - Disable pgtable cache (EXPERIMENTAL) CONFIG_NO_PGT_CACHE Normally the kernel maintains a `quicklist' of preallocated @@ -17230,44 +17883,68 @@ SA1100 serial port support CONFIG_SERIAL_SA1100 + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainer listed in MAINTAINERS * If you have a machine based on a SA1100/SA1110 StrongARM CPU you can enable its onboard serial port by enabling this option. Please read Documentation/arm/SA1100/serial_UART for further info. Console on SA1100 serial port CONFIG_SERIAL_SA1100_CONSOLE + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainer listed in MAINTAINERS * If you have enabled the serial port on the SA1100/SA1110 StrongARM CPU you can make it the console by answering Y to this option. L7200 serial port support CONFIG_SERIAL_L7200 + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainer listed in MAINTAINERS * If you have a LinkUp Systems L7200 board you can enable its two onboard serial ports by enabling this option. The device numbers are major ID 4 with minor 64 and 65 respectively. Console on L7200 serial port CONFIG_SERIAL_L7200_CONSOLE + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainer listed in MAINTAINERS * If you have enabled the serial ports on the L7200 development board you can make the first serial port the console by answering Y to this option. L7200 SDB keyboard support CONFIG_KEYBOARD_L7200 + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainer listed in MAINTAINERS * Enable this option if you would like to be able to use a keyboard on a LinkUp Systems L7200 board. L7200 SDB Fujitsu keyboard support CONFIG_KEYBOARD_L7200_NORM + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainer listed in MAINTAINERS * Select the Fujitsu keyboard if you want a normal QWERTY style keyboard on the LinkUp SDB. L7200 SDB Prototype keyboard support CONFIG_KEYBOARD_L7200_DEMO + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainer listed in MAINTAINERS * Select the prototype keyboard if you want to play with the LCD/keyboard combination on the LinkUp SDB. Footbridge Mode CONFIG_HOST_FOOTBRIDGE + * Orphaned entry retained 20 April 2001 by Russell King * + * If you read this note from the configurator, please contact * + * the Configure.help maintainer listed in MAINTAINERS * The 21285 Footbridge chip can operate in either `host mode' or `add-in' mode. Say Y if your 21285 is in host mode, and therefore is the configuration master, otherwise say N. This must not be @@ -17338,12 +18015,17 @@ To use Linux support for the IrDA (tm) protocols, you will also need some user-space utilities like irattach. For more information, see the file Documentation/networking/irda.txt. You also want to read the - IR-HOWTO, available at http://www.linuxdoc.org/docs.html#howto . + IR-HOWTO, available at . This support is also available as a module called irda.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +Ultra (connectionless) protocol +CONFIG_IRDA_ULTRA + Say Y here to support the connectionless Ultra IRDA protocol, also + called IrOBEX. + IrDA protocol options CONFIG_IRDA_OPTIONS Say Y here if you want to configure any of the following IrDA options. @@ -17461,6 +18143,19 @@ If unsure, say Y. +USB IrDA FIR Dongle Device Driver +CONFIG_USB_IRDA + Say Y here if you want to build support for the USB IrDA FIR Dongle + device driver. If you want to compile it as a module (irda-usb.o), + say M here and read Documentation/modules.txt. IrDA-USB support the + various IrDA USB dongles available and most of their pecularities. + Those dongles plug in the USB port of your computer, are plug and + play, and support SIR and FIR (4Mbps) speeds. On the other hand, + those dongles tend to be less efficient than a FIR chipset. + + Please note that the driver is still experimental. And of course, + you will need both USB and IrDA support in your kernel... + Winbond W83977AF IrDA Device Driver CONFIG_WINBOND_FIR Say Y here if you want to build IrDA support for the Winbond @@ -17666,6 +18361,390 @@ another UltraSPARC-IIi-cEngine boardset with hardware watchdog, you should say N to this option. +ETRAX Memory configuration +CONFIG_ETRAX_DRAM_SIZE + Size of DRAM (decimal in MB) typically 2, 8 or 16. + +LED configuration +CONFIG_ETRAX_PA_LEDS + The Etrax network driver is responsible for flashing LED's when + packets arrive and are sent. It uses macros defined in asm/io.h, + and those macros are defined after what YOU choose in this option. + The actual bits used are configured separately. + Some products put the leds on a memory-mapped latch instead. This + is mapped at 0x90000000. + +LED bit configuration +CONFIG_ETRAX_LED1G + Bit to use for the first green LED. + Most Axis products use bit 2 here. + +LED bit configuration +CONFIG_ETRAX_LED1R + Bit to use for the first red LED. + Most Axis products use bit 3 here. + For products with only one controllable LED, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +LED bit configuration +CONFIG_ETRAX_LED2G + Bit to use for the second green LED. The "Active" LED. + Most Axis products use bit 4 here. + For products with only one controllable LED, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +LED bit configuration +CONFIG_ETRAX_LED2R + Bit to use for the second red LED. + Most Axis products use bit 5 here. + For products with only one controllable LED, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +LED bit configuration +CONFIG_ETRAX_LED3G + Bit to use for the third green LED. The "Drive" LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +LED bit configuration +CONFIG_ETRAX_LED3R + Bit to use for the third red LED. + For products with only one ortwo controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +Flash LED off during activity +CONFIG_ETRAX_LED_OFF_DURING_ACTIVITY + This option allows you to decide whether the network LED (and + Bluetooth LED in case you use Bluetooth) will be on or off when + the network is connected, and whether it should flash off or on + when there is activity. If you say y to this option the network + LED will be lit when there is a connection, and will flash off + when there is activity. + +Button configuration +CONFIG_ETRAX_PA_BUTTON_BITMASK + This is a bitmask with information about what bits on PA that + are used for buttons. + Most products has a so called TEST button on PA1, if that's true + use 02 here. + Use 00 if there are no buttons on PA. + If the bitmask is <> 00 a button driver will be included in the gpio + driver. Etrax general I/O support must be enabled. + +PA changeable direction bits +CONFIG_ETRAX_PA_CHANGEABLE_DIR + This is a bitmask with information of what bits in PA that a user + can change direction on using ioctl's. + Bit set = changeable. + You probably want 00 here. + +PA changeable data bits +CONFIG_ETRAX_PA_CHANGEABLE_BITS + This is a bitmask with information of what bits in PA that a user + can change change the value on using ioctl's. + Bit set = changeable. + You probably want 00 here. + +PA changeable direction bits +CONFIG_ETRAX_PB_CHANGEABLE_DIR + This is a bitmask with information of what bits in PB that a user + can change direction on using ioctl's. + Bit set = changeable. + You probably want 00 here. + +CONFIG_ETRAX_PB_CHANGEABLE_BITS + This is a bitmask with information of what bits in PB that a user + can change the value on using ioctl's. + Bit set = changeable. + You probably want 00 here. + +Kernel debugger (kgdb) +CONFIG_ETRAX_KGDB + The CRIS version of gdb can be used to remotely debug a running Linux + kernel via the serial debug port. Provided you have gdb-cris installed, + run gdb-cris vmlinux, then type + (gdb) set remotebaud 115200 <- kgdb uses 115200 as default + (gdb) target remote /dev/ttyS0 <- maybe you use another port + This should connect you to your booted kernel (or boot it now if you + didn't before). The kernel halts when it boots, waiting for gdb if + this option is turned on! + +Etrax bus waitstates +CONFIG_ETRAX_DEF_R_WAITSTATES + Waitstates for SRAM, Flash and peripherials (not DRAM). 95f8 is a + good choice for most Axis products... + +Etrax bus configuration +CONFIG_ETRAX_DEF_R_BUS_CONFIG + Assorted bits controlling write mode, DMA burst length etc. 104 is a + good choice for most Axis products... + +Etrax DRAM configuration +CONFIG_ETRAX_DEF_R_DRAM_CONFIG + The R_DRAM_CONFIG register specifies everything on how the DRAM chips + in the system are connected to the Etrax CPU. This is different + depending on the manufacturer, chip type and number of chips. + So this value often needs to be different for each Axis + product. + +Etrax DRAM timing +CONFIG_ETRAX_DEF_R_DRAM_TIMING + Different DRAM chips have different speeds. Current Axis products use + 50ns DRAM chips which can use the timing: 5611. + +Etrax SDRAM configuration +CONFIG_ETRAX_DEF_R_SDRAM_CONFIG + The R_SDRAM_CONFIG register specifies everything on how the SDRAM chips + in the system are connected to the Etrax CPU. This is different + depending on the manufacturer, chip type and number of chips. + So this value often needs to be different for each Axis + product. + +Etrax SDRAM timing +CONFIG_ETRAX_DEF_R_SDRAM_TIMING + Different SDRAM chips have different timing. + +Etrax General port A direction +CONFIG_ETRAX_DEF_R_PORT_PA_DIR + Configures the direction of general port A bits. 1 is out, 0 is in. + This is often totally different depending on the product used. There + are some guidelines though - if you know that only LED's are connected + to port PA, then they are usually connected to bits 2-4 and you can + therefore use 1c. On other boards which don't have the LED's + at the general ports, these bits are used for all kinds of stuff. + If you don't know what to use, it is always safe to put all as inputs, + although floating inputs isn't good. + +Etrax General port A data +CONFIG_ETRAX_DEF_R_PORT_PA_DATA + Configures the initial data for the general port A bits. Most products + should use 00 here. + +Etrax General port B config +CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG + Configures the type of the general port B bits. 1 is chip select, + 0 is port. Most products should use 00 here. + +Etrax General port B direction +CONFIG_ETRAX_DEF_R_PORT_PB_DIR + Configures the direction of general port B bits. 1 is out, 0 is in. + This is often totally different depending on the product used. Bits + 0 and 1 on port PB are usually used for I2C communication, but the + kernel I2C driver sets the appropriate directions itself so you don't + need to take that into consideration when setting this option. + If you don't know what to use, it is always safe to put all as inputs. + +Etrax General port B data +CONFIG_ETRAX_DEF_R_PORT_PB_DATA + Configures the initial data for the general port A bits. Most products + should use FF here. + +Etrax General port device +CONFIG_ETRAX_GPIO + Enables the Etrax general port device (major 120, minors 0 and 1). + You can use this driver to access the general port bits. It supports + these ioctl's: + #include + fd = open("/dev/gpioa", O_RDWR); // or /dev/gpiob + ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), bits_to_set); + ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), bits_to_clear); + val = ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_READBITS), NULL); + Remember that you need to setup the port directions appropriately in + the General configuration. + +Etrax parallel data support +CONFIG_ETRAX_PARDATA + Adds support for writing data to the parallel port par0 of the ETRAX 100. + If you create a character special file with major number 126, you can + write to the data bits of par0. + Note: you need to disable Etrax100 parallel port support. + +Etrax parallel LCD (HD44780) Driver +CONFIG_ETRAX_LCD_HD44780 + Adds support for a HD44780 controlled LCD connected to the parallel + port par0 of the Etrax. + +Etrax Serial port ser0 support +CONFIG_ETRAX_SERIAL + Enables the ETRAX 100 serial driver for ser0 (ttyS0) + You probably want this enabled. + +/proc/serial entry +CONFIG_ETRAX_SERIAL_PROC_ENTRY + Enables /proc/serial entry where errors and statistics can be viewed. + CONFIG_PROC_FS must also be set for this to work. + +Etrax Serial port fast flush of DMA using fast timer API +CONFIG_ETRAX_SERIAL_FAST_TIMER + Select this to have the serial DMAs flushed at a higher rate than normally, + possible by using the fast timer API, the timeout is approx. 4 character + times. + If unsure, say N. + +Etrax Serial port fast flush of DMA +CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST + Select this to have the serial DMAs flushed at a higher rate than normally + possible through a fast timer interrupt (currently at 15360 Hz). + If unsure, say N. + +Etrax Serial port receive flush timeout +CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS + Number of timer ticks between flush of receive fifo (1 tick = 10ms). + Try 0-3 for low latency applications. Approx 5 for high load + applications (e.g. PPP). Maybe this should be more adaptive some day... + +Etrax Serial port ser0 DTR, RI, DSR and CD support on PB +CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB + Enables the status and control signals DTR, RI, DSR and CD on PB for ser0 + +Etrax Serial port ser1 support +CONFIG_ETRAX_SERIAL_PORT1 + Enables the ETRAX 100 serial driver for ser1 (ttyS1) + +Etrax Serial port ser1 DTR, RI, DSR and CD support on PB +CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB + Enables the status and control signals DTR, RI, DSR and CD on PB for ser1 + + +Etrax Serial port ser2 support +CONFIG_ETRAX_SERIAL_PORT2 + Enables the ETRAX 100 serial driver for ser2 (ttyS2) + +Etrax Serial port ser2 DTR, RI, DSR and CD support on PA +CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA + Enables the status and control signals DTR, RI, DSR and CD on PA for ser + +Etrax100 Serial port ser3 support +CONFIG_ETRAX_SERIAL_PORT3 + Enables the ETRAX 100 serial driver for ser3 (ttyS3) + +Etrax100 RS-485 Support +CONFIG_ETRAX_RS485 + Enables support for RS-485 serial communication + +Etrax100 RS-485 mode on PA +CONFIG_ETRAX_RS485_ON_PA + Control Driver Output Eanble on RS485 tranceiver using a pin on PA port: + Axis 2400/2401 uses PA 3. + +Etrax100 RS-485 disable receiver +CONFIG_ETRAX_RS485_DISABLE_RECEIVER + It's necessary to disable the serial receiver to avoid serial loopback. + Not all products are able to do this in software only. Axis 2400/2401 + must disable receiver. + +Etrax100 I2C Support +CONFIG_ETRAX_I2C + Enables an I2C driver on PB0 and PB1 on ETRAX100. + EXAMPLE usage: + i2c_arg = I2C_WRITEARG(STA013_WRITE_ADDR, reg, val); + ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_WRITEREG), i2c_arg); + i2c_arg = I2C_READARG(STA013_READ_ADDR, reg); + val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg); + +Etrax100 I2C EEPROM (NVRAM) support +CONFIG_ETRAX_I2C_EEPROM + Enables I2C EEPROM (non-volatile RAM) on PB0 and PB1 using the I2C driver. + +Etrax100 I2C EEPROM (NVRAM) size/probe +CONFIG_ETRAX_I2C_EEPROM_PROBE + Specifies size or auto probe of the EEPROM size. + Options: Probed, 2k, 8k, 16k + (Probing works for 2k and 8k but no that well for 16k) + +Etrax DS1302 RTC driver +CONFIG_ETRAX_DS1302 + Enables the driver for the DS1302 Real-Time Clock battery-backed + chip on some products. The kernel reads the time when booting, and + the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a + rtc_time struct (see asm/rtc.h) on the /dev/rtc device, major 121. + You can check the time with cat /proc/rtc, but normal time reading + should be done using libc function time and friends. + +Etrax DS1302 RST on the Generic Port +CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT + If your product has the RST signal line for the DS1302 RTC on the + Generic Port then say Y here, otherwise leave it as N in which + case the RST signal line is assumed to be connected to Port PB + (just like the SCL and SDA lines). + +Etrax DS1302 RST bit number +CONFIG_ETRAX_DS1302_RSTBIT + This is the bit number for the RST signal line of the DS1302 RTC on + the selected port. If you have selected the generic port then it + should be bit 27, otherwise your best bet is bit 5. + +Etrax DS1302 SCL bit number +CONFIG_ETRAX_DS1302_SCLBIT + This is the bit number for the SCL signal line of the DS1302 RTC on + Port PB. This is probably best left at 3. + +Etrax DS1302 SDA bit number +CONFIG_ETRAX_DS1302_SDABIT + This is the bit number for the SDA signal line of the DS1302 RTC on + Port PB. This is probably best left at 2. + +Etrax 100 IDE Reset +CONFIG_ETRAX_IDE_PB7_RESET + Configures the pin used to reset the IDE bus. + +ETRAX 100LX USB 1.1 Host +CONFIG_ETRAX_USB_HOST + This option enables the host functionality of the ETRAX 100LX + built-in USB controller. In host mode the controller is designed + for CTRL and BULK traffic only, INTR traffic may work as well + however (depending on the requirements of timeliness). + +ETRAX 100LX USB 1.1 Host port 1 enable +CONFIG_ETRAX_USB_HOST_PORT1 + This option enables port 1 of the ETRAX 100LX USB root hub (RH). + +ETRAX 100LX USB 1.1 Host port 2 enable +CONFIG_ETRAX_USB_HOST_PORT2 + This option enables port 2 of the ETRAX 100LX USB root hub (RH). + +ETRAX 100LX 10/100Mbit Ethernet controller +CONFIG_ETRAX_ETHERNET + This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet + controller. + +ETRAX 100LX Synchronous serial ports +CONFIG_ETRAX_SYNCHRONOUS_SERIAL + This option enables support for the ETRAX 100LX built-in + synchronous serial ports. These ports are used for continuous + streamed data like audio. The default setting is compatible + with the STA 013 MP3 decoder, but can easily be tuned to fit + any other audio encoder/decoder and SPI. + +ETRAX 100LX Synchronous serial port 0 enabled +CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0 + Enables the ETRAX 100LX synchronous serial port 0 (syncser0). + +ETRAX 100LX Synchronous serial port 0 uses DMA +CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA + Makes synchronous serial port 0 use DMA. + +ETRAX 100LX Synchronous serial port 1 enabled +CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1 + Enables the ETRAX 100LX synchronous serial port 1 (syncser1). + +ETRAX 100LX Synchronous serial port 1 uses DMA +CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA + Makes synchronous serial port 1 use DMA. + +MTD flash map support +CONFIG_ETRAX_AXISFLASHMAP + This option enables MTD mapping of flash devices. Needed to + use flash memories. If unsure, say yes. + +Ptable sector offset +CONFIG_ETRAX_PTABLE_SECTOR + Byte-offset of the partition table in the first flash chip. + The default value is 64kB and should not be changed unless + you know exactly what you are doing. The only valid reason + for changing this is when the flash block size is bigger + than 64kB (e.g. when using two parallel 16 bit flashes). + IA-64 system type CONFIG_IA64_GENERIC This selects the system type of your hardware. A "generic" kernel @@ -17673,11 +18752,11 @@ a kernel for your specific system, it will be faster and smaller. To find out what type of IA-64 system you have, you may want to - check the IA-64 Linux web site at http://www.linux-ia64.org/. + check the IA-64 Linux web site at . As of the time of this writing, most hardware is DIG compliant, so the "DIG-compliant" option is usually the right choice. - HP-simulator For the HP simulator (http://software.hp.com/ia64linux/). + HP-simulator For the HP simulator (). SN1-simulator For the SGI SN1 simulator. DIG-compliant For DIG ("Developer's Interface Guide") compliant system. @@ -17724,33 +18803,6 @@ redirect interrupts to different CPUs. Select N here if you're unsure. -Enable use of global TLB purge instruction (ptc.g) -CONFIG_ITANIUM_PTCG - Say Y here if you want the kernel to use the IA-64 "ptc.g" - instruction to flush the TLB on all CPUs. Select N here if - you're unsure. - -Enable SoftSDV hacks -CONFIG_IA64_SOFTSDV_HACKS - Say Y here to enable hacks to make the kernel work on the Intel - SoftSDV simulator. Select N here if you're unsure. - -Enable AzusA hacks -CONFIG_IA64_AZUSA_HACKS - Say Y here to enable hacks to make the kernel work on the NEC - AzusA platform. Select N here if you're unsure. - -Force socket buffers below 4GB? -CONFIG_SKB_BELOW_4GB - Most of today's network interface cards (NICs) support DMA to - the low 32 bits of the address space only. On machines with - more then 4GB of memory, this can cause the system to slow - down if there is no I/O TLB hardware. Turning this option on - avoids the slow-down by forcing socket buffers to be allocated - from memory below 4GB. The downside is that your system could - run out of memory below 4GB before all memory has been used up. - If you're unsure how to answer this question, answer Y. - Enable IA-64 Machine Check Abort CONFIG_IA64_MCA Say Y here to enable machine check support for IA-64. If you're @@ -17771,7 +18823,47 @@ and the PAL firmware version in use. To use this option, you have to check that the "/proc file system - support" (CONFIG_PROC_FS) is enabled, too. + support" (CONFIG_PROC_FS) is enabled too. + +Directly Connected Compact Flash support +CONFIG_CF_ENABLER + Compact Flash is a small, removable mass storage device introduced + in 1994 originally as a PCMCIA device. If you say `Y' here, you + compile in support for Compact Flash devices directly connected to + a SuperH processor. A Compact Flash FAQ is available at + . + +Kernel debugging +CONFIG_DEBUG_KERNEL + Say Y here if you are developing drivers or trying to debug and identify + kernel problems. + +Debug memory allocations +CONFIG_DEBUG_SLAB + Say Y here to have the kernel do limited verification on memory + allocation as well as poisoning memory on free to catch use of freed + memory. + +Memory mapped I/O debug support +CONFIG_DEBUG_IOVIRT + Say Y here to get warned whenever an attempt is made to do I/O on + obviously invalid addresses such as those generated when ioremap() + calls are forgotten. Memory mapped I/O will go through an extra check to + catch access to unmapped ISA addresses, an access method that can still + be used by old drivers that are being ported from 2.0/2.2. + +Spinlock debugging +CONFIG_DEBUG_SPINLOCK + Say Y here and build SMP to catch missing spinlock initialisation and + certain other kinds of spinlock errors commonly made. This is best used + in conjunction with the NMI watchdog so that spinlock deadlocks are also + debuggable + +Verbose BUG() reporting (adds 70K) +CONFIG_DEBUG_BUGVERBOSE + Say Y here to make BUG() panics output the file name and line number of + the BUG call as well as the EIP and oops trace. This aids debugging but + costs about 70-100K of memory. # # A couple of things I keep forgetting: @@ -17795,7 +18887,7 @@ # LocalWords: netscape gcc LD CC toplevel MODVERSIONS insmod rmmod modprobe IP # LocalWords: genksyms INET loopback gatewaying ethernet PPP ARP Arp MEMSIZE # LocalWords: howto multicasting MULTICAST MBONE firewalling ipfw ACCT resp ip -# LocalWords: proc acct IPIP encapsulator decapsulator klogd PCTCP RARP EXT PS +# LocalWords: proc acct IPIP encapsulator decapsulator klogd RARP EXT PS # LocalWords: telnetting subnetted NAGLE rlogin NOSR ttyS TGA techinfo mbone nl # LocalWords: Mb SKB IPX Novell dosemu DDP ATALK vmalloc visar ehome # LocalWords: SD CHR scsi thingy SG CD LUNs LUN jukebox Adaptec BusLogic EATA diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/README.nsp_cs.eng linux.ac/Documentation/README.nsp_cs.eng --- linux.vanilla/Documentation/README.nsp_cs.eng Thu Jan 1 01:00:00 1970 +++ linux.ac/Documentation/README.nsp_cs.eng Tue Apr 3 17:54:28 2001 @@ -0,0 +1,127 @@ + + WorkBiT NinjaSCSI-3/32Bi driver for Linux + +1. Comment + This is Workbit corp.'s(http://www.workbit.co.jp/) NinjaSCSI-3 +(http://www.workbit.co.jp/ts/z_nj3r.html) and NinjaSCSI-32Bi +(http://www.workbit.co.jp/ts/z_njsc32bi.html) PCMCIA card driver module +for Linux. + +2. My Linux environment +Linux kernel: 2.4.0 / 2.2.18 +pcmcia-cs: 3.1.24 +gcc: gcc-2.95.2 +PC card: I-O data PCSC-F (NinjaSCSI-3) + I-O data CBSC-II in 16 bit mode (NinjaSCSI-32Bi) +SCSI device: I-O data CDPS-PX24 (CD-ROM drive) + Media Intelligent MMO-640GT (Optical disk drive) + +3. Install +[1] Check your PC card is true "NinjaSCSI-3" card. + If you installed pcmcia-cs already, pcmcia reports your card as UNKNOWN + card, and write ["WBT", "NinjaSCSI-3", "R1.0"] or some other string to + your console or log file. + You can also use "cardctl" program (this program is in pcmcia-cs source + code) to get more info. + +# cat /var/log/messgaes +... +Jan 2 03:45:06 lindberg cardmgr[78]: unsupported card in socket 1 +Jan 2 03:45:06 lindberg cardmgr[78]: product info: "WBT", "NinjaSCSI-3", "R1.0" +... +# cardctl ident +Socket 0: + no product info available +Socket 1: + product info: "IO DATA", "CBSC16 ", "1" + + +[2] Get Linux kernel source, and extract it to /usr/src. + Because NinjaSCSI driver requiers some SCSI header files in Linux kernel + source. + I recomend rebuilding your kernel. This eliminate some versioning problem. +$ cd /usr/src +$ tar -zxvf linux-x.x.x.tar.gz +$ cd linux +$ make config +... + +[3] If you use this driver with Kernel 2.2, Unpack pcmcia-cs in some directory + and make & install. This driver requies pcmcia-cs header file. +$ cd /usr/src +$ tar zxvf cs-pcmcia-cs-3.x.x.tar.gz +... + +[4] Extract this driver's archive somewhere, and edit Makefile, then do make. +$ tar -zxvf nsp_cs-x.x.tar.gz +$ cd nsp_cs-x.x +$ make + +[5] Copy nsp_cs.o to suitable plase, like /lib/modules//pcmcia/ . + +[6] Add these lines to /etc/pcmcia/config . + If you yse pcmcia-cs-3.1.8 or later, we can use "nsp_cs.conf" file. + So, you don't need to edit file. Just copy to /etc/pcmcia/ . + +------------------------------------- +device "nsp_cs" + class "scsi" module "nsp_cs" + +card "WorkBit NinjaSCSI-3" + version "WBT", "NinjaSCSI-3", "R1.0" + bind "nsp_cs" + +card "WorkBit NinjaSCSI-32Bi (16bit)" + version "WORKBIT", "UltraNinja-16", "1" + bind "nsp_cs" + +# OEM +card "WorkBit NinjaSCSI-32Bi (16bit) / IO-DATA" + version "IO DATA", "CBSC16 ", "1" + bind "nsp_cs" + +# OEM +card "WorkBit NinjaSCSI-32Bi (16bit) / KME-1" + version "KME ", "SCSI-CARD-001", "1" + bind "nsp_cs" +card "WorkBit NinjaSCSI-32Bi (16bit) / KME-2" + version "KME ", "SCSI-CARD-002", "1" + bind "nsp_cs" +card "WorkBit NinjaSCSI-32Bi (16bit) / KME-3" + version "KME ", "SCSI-CARD-003", "1" + bind "nsp_cs" +card "WorkBit NinjaSCSI-32Bi (16bit) / KME-4" + version "KME ", "SCSI-CARD-004", "1" + bind "nsp_cs" +------------------------------------- + +[7] Boot (or reboot) pcmcia-cs. +# /etc/rc.d/rc.pcmcia start (BSD style) +or +# /etc/init.d/pcmcia start (SYSV style) + + +4. History +See README.nin_cs . + +5. Caution + If you eject card when doing some operation for your SCSI device or suspend +your computer, you encount some *BAD* error like disk crash. + It works good when I using this driver right way. But I'm not guarantee +your data. Please backup your data when you use this driver. + +6. Known Bugs + Some write error occurs when you use slow device. + +7. Testing + Please send me some reports(bug reports etc..) of this software. +When you send report, please tell me these or more. + card name + kernel version + your SCSI device name(hard drive, CD-ROM, etc...) + +8. Copyright + See GPL. + + +2001/02/01 yokota@netlab.is.tsukuba.ac.jp diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/devices.txt linux.ac/Documentation/devices.txt --- linux.vanilla/Documentation/devices.txt Sat May 26 16:52:52 2001 +++ linux.ac/Documentation/devices.txt Mon May 21 00:04:10 2001 @@ -2125,6 +2125,9 @@ ... 63 = /dev/usb/scanner15 16th USB scanner 64 = /dev/usb/rio500 Diamond Rio 500 + 96 = /dev/usb/hiddev0 First raw HID event interface + ... + 111 = /dev/usb/hiddev15 16th raw HID event interface 181 char Conrad Electronic parallel port radio clocks 0 = /dev/pcfclock0 First Conrad radio clock diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/filesystems/ntfs.txt linux.ac/Documentation/filesystems/ntfs.txt --- linux.vanilla/Documentation/filesystems/ntfs.txt Fri Feb 16 23:53:08 2001 +++ linux.ac/Documentation/filesystems/ntfs.txt Sat May 26 00:18:05 2001 @@ -9,9 +9,18 @@ distribution from Sourceforge at http://sourceforge.net/projects/linux-ntfs/ and always run the included ntfsfix utility after performing a write to an NTFS partition from Linux to fix some of the damage done by the Linux NTFS -driver. You should run ntfsfix _after_ unmounting the partition in Linux but -_before_ rebooting into Windows. During the next reboot into Windows, chkdsk -will be run automatically to fix the remaining damage. +driver and to schedule an automatic chkdsk when Windows reboots. You should +run ntfsfix _after_ unmounting the partition in Linux but _before_ rebooting +into Windows. During the next reboot into Windows, chkdsk will be run +automatically fixing the remaining damage. If no errors are found it is a +good indication that the driver + ntfsfix together worked to full +satisfaction. (-; + +Please note that the experimental write support is limited to Windows NT4 and +earlier versions at the moment. + +If you think you have discovered a bug please have look at the "Known bugs" +section below to see whether it isn't known already. For ftdisk support, limited success was reported with volume sets on top of the md driver, although mirror and stripe sets should work as well - if the @@ -19,25 +28,84 @@ using the md driver will fail if any of your NTFS partitions have an odd number of sectors. -Please note that the experimental write support is limited to -Windows NT4 and earlier versions at the moment. +Supported mount options +======================= + +iocharset=name Character set to use when returning file names. + Unlike VFAT, NTFS suppresses names that contain + unconvertible characters + +utf8= Use UTF-8 for converting file names + +uni_xlate=,2 Use the VFAT-style encoding for file names outside + the current character set. A boolean value will + enable the feature, a value of 2 will enable the + encoding as documented in vfat.txt: + ':', (u & 0x3f), ((u>>6) & 0x3f), (u>>12), -The ntfs driver supports the following mount options: -iocharset=name Character set to use when returning file names. - Unlike VFAT, NTFS suppresses names that contain - unconvertible characters -utf8= Use UTF-8 for converting file names -uni_xlate=,2 Use the VFAT-style encoding for file names outside - the current character set. A boolean value will - enable the feature, a value of 2 will enable the - encoding as documented in vfat.txt: - ':', (u & 0x3f), ((u>>6) & 0x3f), (u>>12), uid= gid= -umask= These options work as documented in mount(8). - By default, the files are owned by root and - not readable by somebody else. -posix= If enabled, the file system distinguishes between - upper and lower case. The 8.3 alias names are presented - as hard links instead of being suppressed. +umask= These options work as documented in mount(8). + By default, the files are owned by root and + not readable by anyone else. + +posix= If enabled, the file system distinguishes between + upper and lower case. The 8.3 alias names are presented + as hard links instead of being suppressed. + +show_sys_files= If enabled, show all system files as normal files. Note + that $MFT does not appear unless specifically + requested. For example in bash, use: "ls -l \$MFT". + Be careful not to write anything to them or you could + crash the kernel and/or corrupt your file system! + +Known bugs and (mis-)features +============================= + +- Do not use the driver for writing as it corrupts the file system. If you do + use it, get the Linux-NTFS tools and use the ntfsfix utility after + dismounting a partition you wrote to. + +- Use "ls -l" instead of just "ls", otherwise the last entry in the directory + is not displayed for some directories. (?!?) + +- Use the show_sys_files mount option which should make things work generally + better. (It results in both the short and long file names being shown as well + as the sytem files.) + +- Special characters are not treated correctly. For example if the file name + contains an apostrophe you will not be able to open it. + +- Writing of extension records is not supported properly. + +Please send bug reports/comments/feed back/abuse to the Linux-NTFS development +list at sourceforge: linux-ntfs-dev@lists.sourceforge.net + +ChangeLog +========= + +NTFS 1.1.15 (changes since kernel 2.4.4's NTFS driver): + + - New mount option show_sys_files= to show all system files as + normal files. + - Support for files and in general any attributes up to the full 2TiB + size supported by the NTFS filesystem. Note we only support up to + 32-bits worth of inodes/clusters at this point. + - Support for more than 128kiB sized runlists (using vmalloc_32() + instead of kmalloc()). + - Fixed races in allocation of clusters and mft records. + - Fixed major bugs in attribute handling / searching / collation. + - Fixed major bugs in compressing a run list into a mapping pairs array. + - Fixed major bugs in inode allocation. Especially file create and + mkdir. + - Fixed memory leaks. + - Fixed major bug in inode layout assignment of sequence numbers. + - Lots of other bug fixes I can't think of right now... + - Fixed NULL bug found by the Stanford checker in ntfs_dupuni2map(). + - Convert large stack variable to dynamically allocated one in + ntfs_get_free_cluster_count() (found by Stanford checker). + +Kernel 2.4.4: + + - Started ChangeLog. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/filesystems/ramfs.txt linux.ac/Documentation/filesystems/ramfs.txt --- linux.vanilla/Documentation/filesystems/ramfs.txt Thu Jan 1 01:00:00 1970 +++ linux.ac/Documentation/filesystems/ramfs.txt Tue Apr 3 17:54:28 2001 @@ -0,0 +1,47 @@ + ramfs - An automatically resizing memory based filesystem + + + Ramfs is a file system which keeps all files in RAM. It allows read + and write access. In contrast to RAM disks, which get allocated a + fixed amount of RAM, ramfs grows and shrinks to accommodate the + files it contains. + + You can mount the ramfs with: + mount -t ramfs none /mnt/wherever + + Then just create and use files. When the filesystem is unmounted, all + its contents are lost. + + NOTE! This filesystem is probably most useful not as a real + filesystem, but as an example of how virtual filesystems can be + written. + +Resource limits: + +By default a ramfs will be limited to using half of (physical) memory +for storing file contents, a bit over that when the metadata is +included. The resource usage limits of ramfs can be controlled with +the following mount options: + + maxsize=NNN + Sets the maximum allowed memory usage of the +filesystem to NNN kilobytes. This will be rounded down to a multiple +of the page size. The default is half of physical memory. NB. unlike +most of the other limits, setting this to zero does *not* mean no +limit, but will actually limit the size of the filesystem data to zero +pages. There might be a use for this in some perverse situation. + + maxfilesize=NNN + Sets the maximum size of a single file on the +filesystem to NNN kilobytes. This will be rounded down to a multiple +of the page size. If NNN=0 there is no limit. The default is no limit. + + maxdentries=NNN + Sets the maximum number of directory entries (hard +links) on the filesystem to NNN. If NNN=0 there is no limit. By +default this is set to maxsize/4. + + maxinodes=NNN + Sets the maximum number of inodes (i.e. distinct +files) on the filesystem to NNN. If NNN=0 there is no limit. The +default is no limit (but there can never be more inodes than dentries). diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/memory.txt linux.ac/Documentation/memory.txt --- linux.vanilla/Documentation/memory.txt Tue Dec 22 16:31:07 1998 +++ linux.ac/Documentation/memory.txt Tue Apr 3 17:54:28 2001 @@ -37,7 +37,7 @@ * Not overclocking your CPU. * Having the memory tested in a memory tester or exchanged - with the vendor. + with the vendor. Consider testing it with memtest86 yourself. * Exchanging your CPU, cache, or motherboard for one that works. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/nmi_watchdog.txt linux.ac/Documentation/nmi_watchdog.txt --- linux.vanilla/Documentation/nmi_watchdog.txt Mon Aug 21 16:57:35 2000 +++ linux.ac/Documentation/nmi_watchdog.txt Tue Apr 3 17:54:28 2001 @@ -1,19 +1,27 @@ -Is your SMP system locking up unpredictably? No keyboard activity, just +Is your ix86 system locking up unpredictably? No keyboard activity, just a frustrating complete hard lockup? Do you want to help us debugging such lockups? If all yes then this document is definitely for you. -on Intel SMP hardware there is a feature that enables us to generate -'watchdog NMI interrupts'. (NMI: Non Maskable Interrupt - these get -executed even if the system is otherwise locked up hard) This can be -used to debug hard kernel lockups. By executing periodic NMI interrupts, -the kernel can monitor whether any CPU has locked up, and print out -debugging messages if so. You can enable/disable the NMI watchdog at boot -time with the 'nmi_watchdog=1' boot parameter. Eg. the relevant -lilo.conf entry: +On Intel and similar ix86 type hardware there is a feature that enables +us to generate 'watchdog NMI interrupts'. (NMI: Non Maskable Interrupt +which get executed even if the system is otherwise locked up hard). +This can be used to debug hard kernel lockups. By executing periodic +NMI interrupts, the kernel can monitor whether any CPU has locked up, +and print out debugging messages if so. You must enable the NMI +watchdog at boot time with the 'nmi_watchdog=n' boot parameter. Eg. +the relevant lilo.conf entry: append="nmi_watchdog=1" +For SMP machines and UP machines with an IO-APIC use nmi_watchdog=1. +For UP machines without an IO-APIC use nmi_watchdog=2, this only works +for some processor types. If in doubt, boot with nmi_watchdog=1 and +check the NMI count in /proc/interrupts; if the count is zero then +reboot with nmi_watchdog=2 and check the NMI count. If it is still +zero then log a problem, you probably have a processor that needs to be +added to the nmi code. + A 'lockup' is the following scenario: if any CPU in the system does not execute the period local timer interrupt for more than 5 seconds, then the NMI handler generates an oops and kills the process. This @@ -24,8 +32,9 @@ cannot even accept NMI interrupts, or the crash has made the kernel unable to print messages. -NOTE: currently the NMI-oopser is enabled unconditionally on x86 SMP -boxes. +NOTE: starting with 2.4.2-ac18 the NMI-oopser is disabled by default, +you have to enable it with a boot time parameter. Prior to 2.4.2-ac18 +the NMI-oopser is enabled unconditionally on x86 SMP boxes. [ feel free to send bug reports, suggestions and patches to Ingo Molnar or the Linux SMP mailing diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sound/CMI8338 linux.ac/Documentation/sound/CMI8338 --- linux.vanilla/Documentation/sound/CMI8338 Fri Jul 28 20:50:52 2000 +++ linux.ac/Documentation/sound/CMI8338 Sun Feb 25 00:46:15 2001 @@ -8,59 +8,80 @@ On-board C-Media chips -WHAT'S NEW +STEPS TO BUILD DRIVER ================================================================================ - 1. Support modem interface for 8738. (select in kernel configuration) - 2. Enable S/PDIF-in to S/PDIF-out (S/PDIF loop). - 3. Enable 4 channels analog duplicate mode on 3 jack or 4 jack - configuration. + 1. Backup the Config.in and Makefile in the sound driver directory + (/usr/src/linux/driver/sound). + The Configure.help provide help when you config driver in step + 4, please backup the original one (/usr/src/linux/Document) and + copy this file. + The cmpci is document for the driver in detail, please copy it + to /usr/src/linux/Document/sound so you can refer it. Backup if + there is already one. + 2. Extract the tar file by 'tar xvzf cmpci-xx.tar.gz' in the above + directory. - Be aware: C-Media Electronics Inc. is basically an IC design house, - and whose development of software drivers is mainly for use by its OEM - customers in their products. C-Media Electronics Inc. itself does not - manufacture end-user products, such as PC or sound cards, so it can - not fully control the drivers provided to consumers. Drivers provided - at this site, therefore, MAY NOT BE APPLICABLE to all sound cards. - Drivers you download from this site may function well at certain - situation, but C-Media Electronics Inc. does not give any guarantee or - assurances. Please be aware that these drivers might cause some - technical difficulties when installed + 3. Change directory to /usr/src/linux + 4. Config cm8338 driver by 'make menuconfig', 'make config' or + 'make xconfig' command. -1. Config cm8338 driver by 'make menuconfig' or 'make config' command. + 5. Please select Sound Card (CONFIG_SOUND=m) support and CMPCI + driver (CONFIG_SOUND_CMPCI=m) as modules. Resident mode not tested. + For driver option, please refer 'DRIVER PARAMETER' -2. Please select Sound Card (CONFIG_SOUND=m) support and CMPCI driver (CONFIG_SOUND_CMPCI=m) as modules. Resident mode not tested. + 6. Compile the kernel if necessary. -3. Compile the kernel if necessary. + 7. Compile the modules by 'make modules'. -4. Compile the modules by 'make modules'. + 8. Install the modules by 'make modules_install' -5. Install the modules by 'make modules_install' -6. Before first time to run the driver, create module dependency by 'depmod -a' +INSTALL DRIVER +================================================================================ + + 1. Before first time to run the driver, create module dependency by + 'depmod -a' + + 2. To install the driver manually, enter 'modprobe cmpci'. + + 3. Driver installation for various distributions: -7. To install the driver, enter 'modprobe cmpci'. + a. Slackware 4.0 + Add the 'modprobe cmpci' command in your /etc/rc.d/rc.modules + file.so you can start the driver automatically each time booting. + b. Caldera OpenLinux 2.2 + Use LISA to load the cmpci module. -DRIVER PARAMETERS + c. RedHat 6.0 and S.u.S.E. 6.1 + Add following command in /etc/conf.modules: + + alias sound cmpci + + also visit http://www.cmedia.com.tw for installation instruction. + +DRIVER PARAMETER ================================================================================ Some functions for the cm8738 can be configured in Kernel Configuration or modules parameters. Set these parameters to 1 to enable. + mpu_io: I/O ports base for MPU-401, 0 if disabled. + fm_io: I/O ports base for OPL-3, 0 if disabled. + spdif_inverse:Inverse the S/PDIF-in signal, this depends on your + CD-ROM or DVD-ROM. spdif_loop: Enable S/PDIF loop, this route S/PDIF-in to S/PDIF-out directly. - four_ch: Enable 4 channels mode, rear-out or line-in will output - the same as line-out. - rear_out: Enable this if you have independent rear-out jacket on - your sound card, otherwise line-in will be used as + speakers: Number of speakers used. + use_line_as_rear:Enable this if you want to use line-in as rear-out. + use_line_as_bass:Enable this if you want to use line-in as + bass-out. modem: You will need to set this parameter if you want to use the HSP modem. You need install the pctel.o, the modem driver itself. - - (You will need to get the pctel driver (binary only) and the support for - this option from the CMI site. It is not included in the Linux kernel - proper as it is non-free). + joystick: Enable joystick. You will need to install Linux joystick + driver. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sysctl/vm.txt linux.ac/Documentation/sysctl/vm.txt --- linux.vanilla/Documentation/sysctl/vm.txt Tue Apr 3 17:31:51 2001 +++ linux.ac/Documentation/sysctl/vm.txt Tue Apr 3 17:54:29 2001 @@ -21,6 +21,7 @@ - buffermem - freepages - kswapd +- max_map_count - overcommit_memory - page-cluster - pagecache @@ -171,6 +172,19 @@ and don't use much of it. Look at: mm/mmap.c::vm_enough_memory() for more information. + +============================================================== + +max_map_count: + +This file contains the maximum number of memory map areas a +process may have. Memory map areas are used as a side-effect +of calling malloc, directly by mmap and mprotect, and also +when loading shared libraries. + +While most applications need less than a thousand maps, +certain programs, particularly malloc debuggers, may consume +lots of them, e.g. up to one or two maps per allocation. ============================================================== diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/sysrq.txt linux.ac/Documentation/sysrq.txt --- linux.vanilla/Documentation/sysrq.txt Sat May 26 16:52:52 2001 +++ linux.ac/Documentation/sysrq.txt Sat May 26 16:59:08 2001 @@ -1,26 +1,27 @@ +Linux Magic System Request Key Hacks +Documentation for sysrq.c version 1.15 +Last update: $Date: 2001/01/28 10:15:59 $ - MAGIC SYSRQ KEY DOCUMENTATION v1.32 - ------------------------------------ - [Sat Apr 8 22:15:03 CEST 2000] - -* What is the magic SysRQ key? +* What is the magic SysRq key? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It is a 'magical' key combo you can hit which kernel will respond to +It is a 'magical' key combo you can hit which the kernel will respond to regardless of whatever else it is doing, unless it is completely locked up. -* How do I enable the magic SysRQ key? +* How do I enable the magic SysRq key? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You need to say "yes" to 'Magic SysRq key (CONFIG_MAGIC_SYSRQ)' when -configuring the kernel. This option is only available in 2.1.x or later -kernels. Once you boot the new kernel, you need to enable it manually -using following command: +configuring the kernel. When running on a kernel with SysRq compiled in, it +may be DISABLED at run-time using following command: + + echo "0" > /proc/sys/kernel/sysrq - echo "1" > /proc/sys/kernel/sysrq +Note that previous versions disabled sysrq by default, and you were required +to specifically enable it at run-time. That is not the case any longer. -* How do I use the magic SysRQ key? +* How do I use the magic SysRq key? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -On x86 - You press the key combo 'ALT-SysRQ-'. Note - Some - (older?) may not have a key labeled 'SysRQ'. The 'SysRQ' key is +On x86 - You press the key combo 'ALT-SysRq-'. Note - Some + keyboards may not have a key labeled 'SysRq'. The 'SysRq' key is also known as the 'Print Screen' key. On SPARC - You press 'ALT-STOP-', I believe. @@ -33,14 +34,14 @@ Print Screen (or F13) - may suffice. On other - If you know of the key combos for other architectures, please - let me know so I can add them to this section. + let me know so I can add them to this section. * What are the 'command' keys? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 'r' - Turns off keyboard raw mode and sets it to XLATE. -'k' - Secure Access Key (SAK) Kills all programs on the current virtual - console. NOTE: See important comments below in SAK section. +'k' - Secure Access Key (SAK) Kills all programs on the current virtual + console. NOTE: See important comments below in SAK section. 'b' - Will immediately reboot the system without syncing or unmounting your disks. @@ -70,8 +71,8 @@ 'l' - Send a SIGKILL to all processes, INCLUDING init. (Your system will be non-functional after this.) -'h' - Will display help ( actually any other key than those listed - above will display help. but 'h' is easy to remember :-) +'h' - Will display help ( actually any other key than those listed + above will display help. but 'h' is easy to remember :-) * Okay, so what can I use them for? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -83,8 +84,8 @@ and thus letting you make sure that the login prompt you see is actually the one from init, not some trojan program. IMPORTANT:In its true form it is not a true SAK like the one in :IMPORTANT -IMPORTATN:c2 compliant systems, and it should be mistook as such. :IMPORTANT - It seems other find it useful as (System Attention Key) which is +IMPORTANT:c2 compliant systems, and it should be mistook as such. :IMPORTANT + It seems other find it useful as (System Attention Key) which is useful when you want to exit a program that will not let you switch consoles. (For example, X or a svgalib program.) @@ -93,7 +94,7 @@ 'S'ync is great when your system is locked up, it allows you to sync your disks and will certainly lessen the chance of data loss and fscking. Note -that the sync hasn't taken place until you see the "OK" and "Done" appear +that the sync hasn't taken place until you see the "OK" and "Done" appear on the screen. (If the kernel is really in strife, you may not ever get the OK or Done message...) @@ -111,30 +112,72 @@ are unable to kill any other way, especially if it's spawning other processes. -* Sometimes SysRQ seems to get 'stuck' after using it, what can I do? +* Sometimes SysRq seems to get 'stuck' after using it, what can I do? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ That happens to me, also. I've found that tapping shift, alt, and control on both sides of the keyboard, and hitting an invalid sysrq sequence again will fix the problem. (ie, something like alt-sysrq-z). Switching to another virtual console (ALT+Fn) and then back again should also help. -* I hit SysRQ, but nothing seems to happen, what's wrong? +* I hit SysRq, but nothing seems to happen, what's wrong? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -There are some keyboards that send different scancodes for SysRQ than the -pre-defined 0x54. So if SysRQ doesn't work out of the box for a certain -keyboard, run 'showkey -s' to find out the proper scancode sequence. Then -use 'setkeycodes 84' to define this sequence to the usual SysRQ +There are some keyboards that send different scancodes for SysRq than the +pre-defined 0x54. So if SysRq doesn't work out of the box for a certain +keyboard, run 'showkey -s' to find out the proper scancode sequence. Then +use 'setkeycodes 84' to define this sequence to the usual SysRq code (84 is decimal for 0x54). It's probably best to put this command in a -boot script. Oh, and by the way, you exit 'showkey' by not typing anything +boot script. Oh, and by the way, you exit 'showkey' by not typing anything for ten seconds. +* I want to add SysRQ key events to a module, how does it work? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In order to register a basic function with the table, you must first include +the header 'include/linux/sysrq.h', this will define everything else you need. +Next, you must create a sysrq_key_op struct, and populate it with A) the key +handler function you will use, B) a help_msg string, that will print when SysRQ +prints help, and C) an action_msg string, that will print right before your +handler is called. Your handler must conform to the protoype in 'sysrq.h'. + +After the sysrq_key_op is created, you can call the macro +register_sysrq_key(int key, struct sysrq_key_op *op_p) that is defined in +sysrq.h, this will register the operation pointed to by 'op_p' at table +key 'key', if that slot in the table is blank. At module unload time, you must +call the macro unregister_sysrq_key(int key, struct sysrq_key_op *op_p), which +will remove the key op pointed to by 'op_p' from the key 'key', if and only if +it is currently registered in that slot. This is in case the slot has been +overwritten since you registered it. + +The Magic SysRQ system works by registering key operations against a key op +lookup table, which is defined in 'drivers/char/sysrq.c'. This key table has +a number of operations registered into it at compile time, but is mutable, +and 4 functions are exported for interface to it: __sysrq_lock_table, +__sysrq_unlock_table, __sysrq_get_key_op, and __sysrq_put_key_op. The +functions __sysrq_swap_key_ops and __sysrq_swap_key_ops_nolock are defined +in the header itself, and the REGISTER and UNREGISTER macros are built fromi +these. More complex (and dangerous!) manipulations of the table are possible +using these functions, but you must be careful to always lock the table before +you read or write from it, and to unlock it again when you are done. (And of +course, to never ever leave an invalid pointer in the table). Null pointers in +the table are always safe :) + +If for some reason you feel the need to call the handle_sysrq function from +within a function called by handle_sysrq, you must be aware that you are in +a lock (you are also in an interupt handler, which means don't sleep!), so +you must call __handle_sysrq_nolock instead. + * I have more questions, who can I ask? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You may feel free to send email to myrdraal@deathsdoor.com, and I will -respond as soon as possible. +respond as soon as possible. -Myrdraal +And I'll answer any questions about the registration system you got, also +responding as soon as possible. + -Crutcher + * Credits ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Written by Mydraal Updated by Adam Sulmicki +Updated by Jeremy M. Dolan 2001/01/28 10:15:59 +Added to by Crutcher Dunnavant diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/hiddev.txt linux.ac/Documentation/usb/hiddev.txt --- linux.vanilla/Documentation/usb/hiddev.txt Thu Jan 1 01:00:00 1970 +++ linux.ac/Documentation/usb/hiddev.txt Thu May 17 20:27:16 2001 @@ -0,0 +1,162 @@ +Care and feeding of your Human Interface Devices + +INTRODUCTION + +In addition to the normal input type HID devices, USB also uses the +human interface device protocols for things that are not really human +interfaces, but have similar sorts of communication needs. The two big +examples for this are power devices (especially uninterruptable power +supplies) and monitor control on higher end monitors. + +To support these disparite requirements, the Linux USB system provides +HID events to two seperate interfaces: +* the input subsystem, which converts HID events into normal input +device interfaces (such as keyboard, mouse and joystick) and a +normalised event interface - see Documentation/usb/input.txt +* the hiddev interface, which provides fairly raw HID events + +The data flow for a HID event produced by a device is something like +the following : + + usb.c ---> hid.c ----> input.c ----> [keyboard/mouse/joystick/event] + | + | + --> hiddev.c ----> POWER / MONITOR CONTROL + +In addition, other subsystems (apart from USB) can potentially feed +events into the input subsystem, but these have no effect on the hid +device interface. + +USING THE HID DEVICE INTERFACE + +The hiddev interface is a char interface using the normal USB major, +with the minor numbers starting at 96 and finishing at 111. Therefore, +you need the following commands: +mknod /dev/usb/hiddev0 c 180 96 +mknod /dev/usb/hiddev1 c 180 97 +mknod /dev/usb/hiddev2 c 180 98 +mknod /dev/usb/hiddev3 c 180 99 +mknod /dev/usb/hiddev4 c 180 100 +mknod /dev/usb/hiddev5 c 180 101 +mknod /dev/usb/hiddev6 c 180 102 +mknod /dev/usb/hiddev7 c 180 103 +mknod /dev/usb/hiddev8 c 180 104 +mknod /dev/usb/hiddev9 c 180 105 +mknod /dev/usb/hiddev10 c 180 106 +mknod /dev/usb/hiddev11 c 180 107 +mknod /dev/usb/hiddev12 c 180 108 +mknod /dev/usb/hiddev13 c 180 109 +mknod /dev/usb/hiddev14 c 180 110 +mknod /dev/usb/hiddev15 c 180 111 + +So you point your hiddev compliant user-space program at the correct +interface for your device, and it all just works. + +Assuming that you have a hiddev compliant user-space program, of +course. If you need to write one, read on. + + +THE HIDDEV API +This description should be read in conjunction with the HID +specification, freely available from http://www.usb.org, and +conveniently linked of http://www.linux-usb.org. + +The hiddev API uses a read() interface, and a set of ioctl() calls. + + +read(): +This is the event interface. When the HID device performs an +interrupt transfer, indicating a change of state, data will be made +available at the associated hiddev device with the content of a struct +hiddev_event: + + struct hiddev_event { + unsigned hid; + signed int value; + }; + +containing the HID usage identifier for the status that changed, and +the value that it was changed to. Note that the structure is defined +within , along with some other useful #defines and +structures. + + +ioctl(): +This is the control interface. There are a number of controls: + +HIDIOCGVERSION - int (read) +Gets the version code out of the hiddev driver. + +HIDIOCAPPLICATION - (none) +This ioctl call returns the HID application usage associated with the +hid device. The third argument to ioctl() specifies which application +index to get. This is useful when the device has more than one +application collection. If the index is invalid (greater or equal to +the number of application collections this device has) the ioctl +returns -1. You can find out beforehand how many application +collections the device has from the num_applications field from the +hiddev_devinfo structure. + +HIDIOCGDEVINFO - struct hiddev_devinfo (read) +Gets a hiddev_devinfo structure which describes the device. + +HIDIOCGSTRING - struct struct hiddev_string_descriptor (read/write) +Gets a string descriptor from the device. The caller must fill in the +"index" field to indicate which descriptor should be returned. + +HIDIOCINITREPORT - (none) +Instructs the kernel to retrieve all input and feature report values +from the device. At this point, all the usage structures will contain +current values for the device, and will maintain it as the device +changes. + +HIDIOCGNAME - string (variable length) +Gets the device name + +HIDIOCGREPORT - struct hiddev_report_info (write) +Instructs the kernel to get a feature or input report from the device, +in order to selectively update the usage structures (in contrast to +INITREPORT). + +HIDIOCSREPORT - struct hiddev_report_info (write) +Instructs the kernel to send a report to the device. This report can +be filled in by the user through HIDIOCSUSAGE calls (below) to fill in +individual usage values in the report beforesending the report in full +to the device. + +HIDIOCGREPORTINFO - struct hiddev_report_info (read/write) +Fills in a hiddev_report_info structure for the user. The report is +looked up by type (input, output or feature) and id, so these fields +must be filled in by the user. The ID can be absolute -- the actual +report id as reported by the device -- or relative -- +HID_REPORT_ID_FIRST for the first report, and (HID_REPORT_ID_NEXT | +report_id) for the next report after report_id. Without a-priori +information about report ids, the right way to use this ioctl is to +use the relative IDs above to enumerate the valid IDs. The ioctl +returns non-zero when there is no more next ID. The real report ID is +filled into the returned hiddev_report_info structure. + +HIDIOCGFIELDINFO - struct hiddev_field_info (read/write) +Returns the field information associated with a report in a +hiddev_field_info structure. The user must fill in report_id and +report_type in this structure, as above. The field_index should also +be filled in, which should be a number from 0 and maxfield-1, as +returned from a previous HIDIOCGREPORTINFO call. + +HIDIOCGUCODE - struct hiddev_usage_ref (read/write) +Returns the usage_code in a hiddev_usage_ref structure, given that +given its report type, report id, field index, and index within the +field have already been filled into the structure. + +HIDIOCGUSAGE - struct hiddev_usage_ref (read/write) +Returns the value of a usage in a hiddev_usage_ref structure. The +usage to be retrieved can be specified as above, or the user can +choose to fill in the report_type field and specify the report_id as +HID_REPORT_ID_UNKNOWN. In this case, the hiddev_usage_ref will be +filled in with the report and field infomation associated with this +usage if it is found. + +HIDIOCSUSAGE - struct hiddev_usage_ref (write) +Sets the value of a usage in an output report. + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/usb/se401.txt linux.ac/Documentation/usb/se401.txt --- linux.vanilla/Documentation/usb/se401.txt Thu Jan 1 01:00:00 1970 +++ linux.ac/Documentation/usb/se401.txt Tue May 1 14:38:33 2001 @@ -0,0 +1,54 @@ +Linux driver for SE401 based USB cameras + +Copyright, 2001, Jeroen Vreeken + + +INTRODUCTION: + +The SE401 chip is the used in low-cost usb webcams. +It is produced by Endpoints Inc. (www.endpoints.com). +It interfaces directly to a cmos image sensor and USB. The only other major +part in a se401 based camera is a dram chip. + +The following cameras are known to work with this driver: + +Aox se401 (non-branded) cameras +Philips PVCV665 USB VGA webcam 'Vesta Fun' +Kensington VideoCAM PC Camera Model 67014 +Kensington VideoCAM PC Camera Model 67015 +Kensington VideoCAM PC Camera Model 67016 +Kensington VideoCAM PC Camera Model 67017 + + +WHAT YOU NEED: + +- USB support +- VIDEO4LINUX support + +More information about USB support for linux can be found at: +http://www.linux-usb.org + + +MODULE OPTIONS: + +When the driver is compiled as a module you can also use the 'flickerless' +option. With it exposure is limited to values that do not interfere with the +net frequency. Valid options for this option are 0, 50 and 60. (0=disable, +50=50hz, 60=60hz) + + +KNOWN PROBLEMS: + +The driver works fine with the usb-ohci and uhci host controller drivers, +the default settings also work with usb-uhci. But sending more then one bulk +transfer at a time with usb-uhci doesn't work yet. +Users of usb-ohci and uhci can safely enlarge SE401_NUMSBUF in se401.h in +order to increase the throughput (and thus framerate). + + +HELP: + +The latest info on this driver can be found at: +http://www.chello.nl/~j.vreeken/se401/ +And questions to me can be send to: +pe1rxq@amsat.org diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/video4linux/Zoran linux.ac/Documentation/video4linux/Zoran --- linux.vanilla/Documentation/video4linux/Zoran Thu Jan 1 01:00:00 1970 +++ linux.ac/Documentation/video4linux/Zoran Tue Apr 3 17:54:29 2001 @@ -0,0 +1,517 @@ +DC10/DC10plus/LML33/Buz Driver for Linux +========================================= + +by Rainer Johanni (for Iomega Buz Driver) + +Adapted for DC10/DC10plus by Wolfgang Scherr + +Further changes for DC10/DC10plus and LML33 cards by +Serguei Miridonov + +Current homepage: http://www.cicese.mx/~mirsev/Linux/DC10plus/ +Current maintainer: Serguei Miridonov + + This is a driver for DC10plus capture cards from Pinnacle Systems + Inc., LML33 cards from Linux Media Labs and Buz from Iomega. + It also works with many old Miro DC10 cards with SAA7110A TV decoder + and ADV7176 TV encoder (please, make sure that your card has these + chips, otherwise the driver will not work). + + The driver is Video4Linux compliant and contains extensions to + provide hardware support for full motion MJPEG compression and + decompression. Since this driver is a derivative from the driver for + Buz Iomega cards written by Dr. Rainer Johanni, + http://www.johanni.de/munich-vision/buz/ they both have compatible + API. I hope that this API will become a part of V4L standard. + +Copyright: This driver is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License. Please, +check http://www.gnu.org/ for details. + +No warranty: This software is provided on AN "AS-IS" basis WITHOUT +WARRANTY OF ANY KIND. YOU USE IT AT YOUR OWN RISK. + + + +CONTENTS +~~~~~~~~ + +Supported Formats +Hardware compression +Compiling and Loading the Driver +Driver Options +Tested applications +Programming interface +Features for testing +Mailing lists +Bug Reports + + +Supported Formats +================= + +Card: DC10/DC10plus LML33/Buz + +TV standard: NTSC/PAL/SECAM(*) NTSC/PAL + +Format: Square pixel CCIR.601 + 640x480 NTSC 720x480 NTSC + 768x576 PAL/SECAM(*) 720x576 PAL + +Frame rates: 30 frames/60 fields per second NTSC + 25 frames/50 fields per second PAL/SECAM(*) + +(*) - SECAM is supported for input only in DC10/DC10plus cards. The +output of the recorded SECAM video stream will be in PAL standard. +Also, please, note that monitoring of the SECAM input signal at the +DC10/DC10plus analog output may not be available. Please, use +appropriate application like XawTV to watch full color SECAM video at +the card input. + +Hardware compression +==================== + +Since the card provides hardware compression, even low end machines can +be successfully used for movie capture and playback. I'm testing the +driver with with 2.2.16 kernel running on 233 MHz Pentium MMX with 64M +RAM on 430TX motherboard and with 10GB IDE drive from Western Digital +Corp. + +On one test run with DC10plus card I've got 0 frames dropped during +about 20 minutes of full motion NTSC (I live in Mexico) video capture +with fully synchronized audio. The command was + + lavrec -fa -in -d1 -l -1 -q30 -w /dos/g/capture/Linux/test%03d.avi + +for recording, and + + lavplay -n128 /dos/g/capture/Linux/test*.avi + +for playback. (See lavtools distribution for more information). + +Typical run of similar test can provide as few as 6-8 dropped frames per +half of an hour. You mileage may vary, though. + +Compiling and Loading the Driver +================================ + +You should run a 2.2.x kernel in order to use this driver. The driver +was also tested with 2.4-test6 kernel, so hopefully it will work +with 2.4 kernels too. + +I would recommend to use only official kernels from www.kernel.org and +its mirrors. Kernels supplied with some Linux distributions may be +patched in some way to meet specific needs of particular Linux +distributor and could be incompatible with this driver. As a driver +maintainer, I am not able to follow every unofficial kernel release, +and no unofficial kernels will be supported. + +Besides the files in this directory, the driver needs the 'videodev' +and the 'i2c' module from the Linux kernel (i2c-old for 2.4 kernels). +In order to get these modules available, enable module support for +VIDEODEV and BTTV (which implies i2c) in your 2.2.x kernel +configuration. You will find these devices in the menu "Character +Devices" in your Kernel Configuration. + +In newer kernels (2.4) instead of BTTV you should enable support for +Iomega Buz cards and for Zoran 36060/36067 chipset. This will include +i2c or i2c-old modules and Buz/LML33 driver. However, instead of +modules for Buz/LML33 driver from the kernel, use modules from _this_ +driver. + +To compile the driver, just type make. + +Before you load the driver you must have a video device at major device +node 81. If you don't have it yet, do the following (as root!): + +cd /dev +mknod video0 c 81 0 +ln -s video0 video + +If you have more than one card, add more nodes in /dev directory: + +mknod video1 c 81 1 +mknod video2 c 81 2 +... + +The driver should operate properly with several cards. It was tested +with one DC10plus and one LML33 cards installed together and the driver +correctly identifies both cards and works with both of them. + +Currently the driver does not support LML33 and Buz cards installed +together in the same system. This will be fixed in future versions. + +Edit the 'update' script if you want to give the driver special options +(see below for options descriptions) and then type (as root) + +./update + +to insert all necessary modules into the kernel. is a list of +cards installed in your system separated by white space. Supported cards +are dc10, dc10plus, lml33, and buz. For example, if you have both dc10plus +and lml33 cards, please type + +./update dc10 lml33 + +If you want to make full use of the Video for Linux _uncompressed_ +grabbing facilities, you must either + +- obtain and install the "big_physarea patch" for your kernel and + set aside the necessary memory during boot time. There seem to be + several versions of this patch against various kernel versions + floating around in the net, you may obtain one e.g. from: + http://www.polyware.nl/~middelin/patch/bigphysarea-2.2.1.tar.gz You + also have to compile your driver AFTER installing that patch in order + to get it working + + or + +- start your kernel with the mem=xxx option, where xxx is your + real memory minus the memory needed for the buffers. + For doing this add an entry in lilo.conf (if you use lilo): + append "mem=xxxM" + or add a line in your linux.par file (if you use loadlin): + mem=xxxM + +The second method is by far easier, however it is dangerous if more +than one driver at a time has the idea to use the memory leftover by +setting the mem=xxx parameter below the actual memory size. + +Read also below how to use this memory! + + + If you use only MJPEG compressed capture provided by the driver, you + should not need large memory areas for DMA. In this case, you will be + able to capture and playback movies with lavtools, however you will + not be able to use capture features of XawTV and other similar + programs (you can still watch video on the screen). + + + +Driver Options +============== + +You are able to customize the behavior of the driver by giving +it some options at start time. + +default_input, default_norm +--------------------------- + +As soon as the driver is loaded, the Buz samples video signals +from one of its input ports and displays it on its output. +The driver uses the Composite Input and the video norm PAL for this. +If you want to change this default behavior, set default_input=1 +(for S-VHS input) or default_norm=1 for NTSC or default_norm=2 +for SECAM (DC10/DC10plus only). + +lock_norm +--------- + +This option was introduced to disable norm (TV standard) change by some +not well behaving programs. For example, if you have some application +which was written by somebody who lives in a country with PAL standard, +this program may not have NTSC option and may always try to set the +driver to PAL. In this case, you may load the driver with +default_norm=1 and lock_norm=1 and the card will be forced to work in +NTSC standard only. + + Options: + + lock_norm=0 default, TV standard change is enabled; + lock_norm=1 TV standard change is disabled but the driver + will not notify the application about any error; + lock_norm=2 TV standard change is disabled and the driver + will notify the program that TV standards other + than set by default_norm=X option are not + supported. + +pass_through +------------ + +When the driver is not in use (device is not opened by any program) and +pass_through=0 (default) the driver will set the TV encoder to produce +color bar signal at the output. If the driver was loaded with +pass_through=1, the color bar will be disabled and input signal will be +sent to the output even if the driver not in use. If you have LML33 card +and wish the color bar signal at the output, you will also need to set +lml33dpath=1 (please, see next section). + +lml33dpath +---------- + +LML33 card normally (lml33dpath=0) connects its output to the input +using analog switch. Additionally, it also allows real-time monitoring +of digitized video using TV monitor connected to the output. This +"digital path" option can be enabled setting lml33dpath=1. In this +mode, the input is connected only to the TV decoder, digital video data +is sent via internal video bus to the TV encoder and resulting analog +signal is sent to the output. This mode could be very useful for testing and +picture adjustment while watching video at the TV monitor connected to +the output. However, because of lack of 75 ohm terminating resistors at +TV decoder input, the signal will suffer serious distortions. + +# These distortions could be eliminated by soldering two 75 ohm resistors +# in LML33 card: in parallel to capacitors C73 and C82 (see schematics of +# H33 board available at www.linuxmedialabs.com and www.zoran.com). Be +# aware, however, that doing so will void card warranty and the card, +# after this change, must always be used with loading option lml33dpath=1. +# +# WARNING: I DID NOT TRY THIS CARD CHANGE YET, THIS IS JUST AN ASSUMPTION +# AND I WILL NOT BE RESPONSIBLE FOR ANY DAMAGE ASSOCIATED WITH THIS +# CHANGE. IF YOU WISH TO TRY IT, DO IT AT YOUR OWN RISK. + +Please, note that DC10/DC10plus cards always use "digital path" for +signal monitoring. Its input and output are both properly terminated +and the digitized signal quality does not depend on the connection of +the output load. + + +v4l_nbufs, v4l_bufsize +---------------------- + +In order to make to make full use of the Video for Linux uncompressed +picture grabbing facilities of the driver (which are needed by many +Video for Linux applications), the driver needs a set of physically +contiguous buffers for grabbing. These parameters determine how many +buffers of which size the driver will allocate at open (the open will +fail if it is unable to do so!). + +These values do not affect the MJPEG grabbing facilities of the driver, +they are needed for uncompressed image grabbing only!!! + +v4l_nbufs is the number of buffers to allocate, a value of 2 (the default) +should be sufficient in almost all cases. Only special applications +(streaming captures) will need more buffers and then mostly the +MJPEG capturing features of the Buz will be more appropriate. +So leave this parameter at it's default unless you know what you do. + +The things for v4l_bufsize are more complicated: v4l_bufsize is set by +default to 128 [KB] which is the maximum amount of physically +contiguous memory Linux is able to allocate without kernel changes. +This is sufficient for grabbing 24 bit color images up to sizes of +approx. 240x180 pixels (240*180*3 = 129600, 128 KB = 131072). + +In order to be able to capture bigger images you have either to +- obtain and install the "big_physarea patch" and set aside + the necessary memory during boot time or +- start your kernel with the mem=xxx option, where xxx is your + real memory minus the memory needed for the buffers. +In that case, useful settings for v4l_bufsize are +- 1296 [Kb] for grabbing 24 bit images of max size 768*576 +- 1728 [Kb] for 32bit images of same size (4*768*576 = 1728 Kb!) +You may reduce these numbers accordingly if you know you are only +grabbing 720 pixels wide images or NTSC images (max height 480). + +In some cases it may happen that Linux isn't even able to obtain +the default 128 KB buffers. If you don't need uncompressed image +grabbing at all, set v4l_bufsize to an arbitrary small value (e.g. 4) +in order to be able to open the video device. + +triton, natoma +-------------- + +The driver tries to detect if you have a triton or natoma chipset +in order to take special measures for these chipsets. +If this detection fails but you are sure you have such a chipset, +set the corresponding variable to 1. +This is a very special option and may go away in the future. + + +Tested applications +=================== + + XawTV to watch video on your computer monitor. + + kwintv the same (you might need to use option lock_norm=1). + + lavtools To record and playback AVI or Quicktime files. Note: you + will need patched version, lavtools-1.2p2 to support new + features of this driver. Please visit driver homepage for + more info. + + Broadcast2000 reportedly (I didn't try that) can accept movies recorded + by lavrec in Quicktime format for editing and then edited + movie can be played back by lavplay program. + + MainActor 3.5x also can accept movies recorded by lavrec for editing. + + +The driver can to be used by two programs at the same time +(please, see warning note below regarding this feature). Using XawTV +you can watch what you are recording or playing back with lavtools. +I've tested the following sequence and it worked for me: + +* start xawtv and switch inputs, TV standards, and adjust video + (contrast, saturation, etc.). You may also run your favorite + audio mixer application to adjust audio inputs. + +* run lavrec with options: + + -i (to choose proper input + and TV standard) + + -l -1 (to use audio mixer settings) + + Other lavrec option can be added at your choice. + +* watch the movie in xawtv window while recording it as AVI or + Quicktime file. + +* when recording is finished, run lavplay or xlav and watch your + clip in xawtv window. + +* Note: you should not quit xawtv during recording or playing back. + If you quit xawtv during recording or playback, another lavtools + program will stop and may even crash. + +I'm not sure that the same will work for you. You can try but, +please, be careful. + +WARNING! This is an experimental feature and I'm not sure if it will be +supported in the future. The original driver was not designed to be +used like this and it has no protection against any interference +between two running programs. THEREFORE, IT IS POTENTIALLY DANGEROUS +AND SINCE THE DRIVER OPERATES IN KERNEL SPACE, USING THIS FEATURE MAY +CRASH YOUR ENTIRE SYSTEM. + + +Programming interface +===================== + +This driver should be fully compliant to Video for Linux, so all +tools working with Video for Linux should work with (hopefully) +no problems. + +A description of the Video for Linux programming interface can be found at: +http://roadrunner.swansea.linux.org.uk/v4lapi.shtml + +Besides the Video for Linux interface, the driver has a "proprietary" +interface for accessing the Buz's MJPEG capture and playback facilities. + +For a full description of all members and ioctls see "zoran.h" (used to +be buz.h or dc10.h in previous versions, so, please, update your +programs accordingly). + +The ioctls for that interface are as follows: + +BUZIOC_G_PARAMS +BUZIOC_S_PARAMS + +Get and set the parameters of the buz. The user should always do a +BUZIOC_G_PARAMS (with a struct buz_params) to obtain the default +settings, change what he likes and then make a BUZIOC_S_PARAMS call. + +BUZIOC_REQBUFS + +Before being able to capture/playback, the user has to request +the buffers he is wanting to use. Fill the structure +zoran_requestbuffers with the size (recommended: 256*1024) and +the number (recommended 32 up to 256). There are no such restrictions +as for the Video for Linux buffers, you should LEAVE SUFFICIENT +MEMORY for your system however, else strange things will happen .... +On return, the zoran_requestbuffers structure contains number and +size of the actually allocated buffers. +You should use these numbers for doing a mmap of the buffers +into the user space. +The BUZIOC_REQBUFS ioctl also makes it happen, that the next mmap +maps the MJPEG buffer instead of the V4L buffers. + +BUZIOC_QBUF_CAPT +BUZIOC_QBUF_PLAY + +Queue a buffer for capture or playback. The first call also starts +streaming capture. When streaming capture is going on, you may +only queue further buffers or issue syncs until streaming +capture is switched off again with a argument of -1 to +a BUZIOC_QBUF_CAPT/BUZIOC_QBUF_PLAY ioctl. + +BUZIOC_SYNC + +Issue this ioctl when all buffers are queued. This ioctl will +block until the first buffer becomes free for saving its +data to disk (after BUZIOC_QBUF_CAPT) or for reuse (after BUZIOC_QBUF_PLAY). + +BUZIOC_G_STATUS + +Get the status of the input lines (video source connected/norm). +This ioctl may be subject to change. + +For programming example, please, look at lavrec.c and lavplay.c code in +lavtools-1.2p2 package (URL: http://www.cicese.mx/~mirsev/DC10plus/) +and the 'examples' directory in the original Buz driver distribution. + +Additional notes for software developers: + + The driver returns maxwidth and maxheight parameters according to + the current TV standard (norm). Therefore, the software which + communicates with the driver and "asks" for these parameters should + first set the correct norm. Well, it seems logically correct: TV + standard is "more constant" for current country than geometry + settings of a variety of TV capture cards which may work in ITU or + square pixel format. Remember that users now can lock the norm to + avoid any ambiguity. + +Features for testing +==================== + +When loaded, the driver creates a /proc/zoranX entry for each card: +using 'cat /proc/zoran0' for your first card you can see the contents +of ZR36057/67 chip registers. It is also possible to modify the +contents of some registers directly. WARNING: modified contents is not +stored in the driver memory, if you restart any program which uses this +driver or even change position or cause redraw of a window of xawtv or +other program, the original registers contents will be restored by the +driver. However, it can be used to change ZR36067 registers on the fly +for fine tuning and then to include these changes into driver code. +This feature is very limited and still requires some documentation. +However, if you are impatient, look at zoran_procfs.c code and +(IMPORTANT!) read ZR36057/67 manual. To set TopField bit, for example, +you need to type as root: + +echo TopField=1 > /proc/zoranX # change X to 0 for your first card, + # 1 for second and so on... + +If you use this feature and have found some interesting result, please, let +me know. + +Mailing lists +============= + +There are two mailing lists available to discuss application issues and +suggest driver improvements: + +1. A mailing list buz-linux was set up to discuss Iomega Buz driver. +Since this driver is derivative of that driver, you can also post your +questions and suggestions there. Subscribe with a message (with +"subscribe" in the subject) to buz-linux-subscribe@webmages.com. +Unsubscribe with a message (with "unsubscribe" in the subject) to +buz-linux-unsubscribe@webmages.com. The mailing list archive can be +found at http://buz.webmages.com/list/. + +2. Video4Linux mailing list is set for more general discussions related +to uncompressed video capture, V4L and V4L2 API, many Video4Linux +applications, etc. to subscribe to this mailing list, please, visit +https://listman.redhat.com/mailman/listinfo/video4linux-list + +Bug Reports +=========== + +If you have found a bug, please, do the following: + +1. Edit first line of zoran.c file and set DEBUGLEVEL to 3; +2. Recompile the driver and install it running update script + in the driver directory; +3. Run the application(s) which you used when you had found a + suspisious behavior; +4. When application stops, look at you /var/log/messages file + (or whatever file you use to log kernel messages) and copy + all lines related to the driver activity to a separate file + in the same order of their appearence in your log file. +5. Mail a message to with a subject + "Linux DC10(plus)/LML33/Buz driver bug report" with a detailed + description of your problem, kernel version, application name and + attach that file with kernel messages as plain text (please, don't + attach it using base64, uuencode, or any other encoding). + + If you have a Buz card, please, also mail the same message to + Wolfgang Scherr diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Documentation/video4linux/w9966.txt linux.ac/Documentation/video4linux/w9966.txt --- linux.vanilla/Documentation/video4linux/w9966.txt Thu Jan 1 01:00:00 1970 +++ linux.ac/Documentation/video4linux/w9966.txt Tue Apr 24 17:55:10 2001 @@ -0,0 +1,37 @@ + +W9966 Camera driver, written by Jakob Kemi (jakob.kemi@post.utfors.se) + +Ok, after a lot of work in softice, wdasm, reading pdf-files +and trial-and-error work I've finally got everything to work. +Since I needed some vision for a robotics project I borrowed +this camera from a friend and started hacking. Anyway I've +converted my original code from the AVR 8bit RISC C/asm +into a working linux driver. I would really appreciate _any_ +kind of feedback regarding this driver. + +To get it working quickly configure your kernel +to support parport, ieee1284, video4linux, experimental drivers +and w9966 + +If w9966 is statically linked it will perform aggressive probing +for the camera. If built as a module you'll have more configuration options. + +Options: +modprobe w9966.o pardev=parport0(or whatever) parmode=0 (0=auto, 1=ecp, 2=epp) + +voila! + +you can also type 'modinfo -p w9966.o' for option usage +(or checkout w9966.c) + +I've only tested it with custom built testprograms +(http://hem.fyristorg.com/mogul/w9966.html) and with gqcam. +(you'll need to tweak the code to qcam a bit to make it work, +dimensions and such) + +The slow framerate is due to missing DMA ECP read support in the +parport drivers. I might add working EPP support later. + +Good luck! + + /Jakob diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/MAINTAINERS linux.ac/MAINTAINERS --- linux.vanilla/MAINTAINERS Sat May 26 16:52:52 2001 +++ linux.ac/MAINTAINERS Sat May 26 00:24:53 2001 @@ -81,6 +81,12 @@ L: linux-net@vger.kernel.org S: Maintained +53C700 AND 53C700-66 SCSI DRIVER +P: James E.J. Bottomley +M: James.Bottomley@HansenPartnership.com +L: linux-scsi@vger.kernel.org +S: Maintained + 6PACK NETWORK DRIVER FOR AX.25 P: Andreas Koensgen M: ajk@iehk.rwth-aachen.de @@ -106,6 +112,13 @@ L: linux-net@vger.kernel.org S: Maintained +A2232 SERIAL BOARD DRIVER +P: Enver Haase +M: ehaase@inf.fu-berlin.de +M: A2232@gmx.net +L: linux-m68k@lists.linux-m68k.org +S: Maintained + ACI MIXER DRIVER P: Robert Siemer M: Robert.Siemer@gmx.de @@ -231,7 +244,7 @@ CIRRUS LOGIC GENERIC FBDEV DRIVER P: Jeff Garzik M: jgarzik@mandrakesoft.com -L: linux-fbdev@vuser.vu.union.edu +L: linux-fbdev-devel@lists.sourceforge.net S: Odd Fixes CIRRUS LOGIC CS4280/CS461x SOUNDDRIVER @@ -608,6 +621,52 @@ W: http://www.uni-mainz.de/~langm000/linux.html S: Maintained +IBM ISERIES LINUX +P: Dave Boutcher +P: Jeff Haumont +M: ilinux@us.ibm.com +M: haumont@us.ibm.com +M: boutcher@us.ibm.com +S: Maintained + +IBM ISERIES VIRTUAL CONSOLE +P: Ryan Arnold +P: Dave Boutcher +M: ilinux@us.ibm.com +M: ryanarn@us.ibm.com +M: boutcher@us.ibm.com +S: Maintained + +IBM ISERIES VIRTUAL CD-ROM +P: Ryan Arnold +P: Dave Boutcher +M: ilinux@us.ibm.com +M: ryanarn@us.ibm.com +M: boutcher@us.ibm.com +S: Maintained + +IBM ISERIES VIRTUAL DISK +P: Ryan Arnold +P: Dave Boutcher +M: ilinux@us.ibm.com +M: ryanarn@us.ibm.com +M: boutcher@us.ibm.com +S: Maintained + +IBM ISERIES VIRTUAL ETHERNET +P: Kyle Lucke +M: ilinux@us.ibm.com +M: klucke@raleigh.ibm.com +S: Maintained + +IBM ISERIES VIRTUAL TAPE +P: Ryan Arnold +P: Dave Boutcher +M: ilinux@us.ibm.com +M: ryanarn@us.ibm.com +M: boutcher@us.ibm.com +S: Maintained + IBM ServeRAID RAID DRIVER P: Keith Mitchell M: ipslinux@us.ibm.com @@ -740,6 +799,13 @@ W: http://www.developer.axis.com/software/jffs/ S: Maintained +JOURNALLING FLASH FILE SYSTEM V2 (JFFS2) +P: David Woodhouse +M: dwmw2@infradead.org +L: jffs-dev@axis.com +W: http://sources.redhat.com/jffs2/ +S: Maintained + JOYSTICK DRIVER P: Vojtech Pavlik M: vojtech@suse.cz @@ -764,8 +830,8 @@ M: kaos@ocs.com.au P: Michael Elizabeth Chastain M: mec@shout.net -L: linux-kbuild@torque.net -W: http://www.kernel.org/pub/linux/kernel/projects/kbuild/ +L: kbuild-devel@lists.sourceforge.net +W: http://kbuild.sourceforge.net S: Maintained KERNEL NFSD @@ -895,6 +961,12 @@ L: linware@sh.cvut.cz S: Maintained +NCR DUAL 700 SCSI DRIVER (MICROCHANNEL) +P: James E.J. Bottomley +M: James.Bottomley@HansenPartnership.com +L: linux-scsi@vger.kernel.org +S: Maintained + NETFILTER P: Rusty Russell M: rusty@rustcorp.com.au @@ -1087,9 +1159,9 @@ S: Maintained RAGE128 FRAMEBUFFER DISPLAY DRIVER -P: Brad Douglas -M: brad@neruo.com -L: linux-fbdev@vuser.vu.union.edu +P: Ani Joshi +M: ajoshi@shell.unixbox.com +L: linux-fbdev-devel@lists.sourceforge.net S: Maintained RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER @@ -1303,10 +1375,11 @@ S: Maintained TOKEN-RING NETWORK DRIVER -P: Paul Norton -M: pnorton@ieee.org +P: Mike Phillips +M: mikep@linuxtr.net L: linux-net@vger.kernel.org L: linux-tr@linuxtr.net +W: http://www.linuxtr.net S: Maintained TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE @@ -1389,6 +1462,13 @@ S: Maintained W: http://www.kroah.com/linux-usb/ +USB CDC ETHERNET DRIVER +P: Brad Hards +M: bhards@bigpond.net.au +L: linux-usb-users@lists.sourceforge.net +L: linux-usb-devel@lists.sourceforge.net +S: Maintained + USB HID/HIDBP/INPUT DRIVERS P: Vojtech Pavlik M: vojtech@suse.cz @@ -1405,6 +1485,13 @@ L: linux-usb-devel@lists.sourceforge.net S: Maintained +USB KAWASAKI LSI DRIVER +P: Brad Hards +M: bhards@bigpond.net.au +L: linux-usb-users@lists.sourceforge.net +L: linux-usb-devel@lists.sourceforge.net +S: Maintained + USB OHCI DRIVER P: Roman Weissgaerber M: weissg@vienna.at @@ -1487,6 +1574,14 @@ L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net W: http://usb.in.tum.de +S: Maintained + +USER-MODE PORT +P: Jeff Dike +M: jdike@karaya.com +L: user-mode-linux-devel@lists.sourceforge.net +L: user-mode-linux-user@lists.sourceforge.net +W: http://user-mode-linux.sourceforge.net S: Maintained VFAT FILESYSTEM: diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/Makefile linux.ac/Makefile --- linux.vanilla/Makefile Sat May 26 16:52:52 2001 +++ linux.ac/Makefile Sat May 26 18:50:57 2001 @@ -1,11 +1,18 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 5 -EXTRAVERSION = +EXTRAVERSION = -ac1 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) -ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) +# SUBARCH tells the usermode build what the underlying arch is. That is set +# first, and if a usermode build is happening, the "ARCH=um" on the command +# line overrides the setting of ARCH below. If a native build is happening, +# then ARCH is assigned, getting whatever value it gets normally, and +# SUBARCH is subsequently ignored. + +SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) +ARCH := $(SUBARCH) CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ @@ -145,6 +152,7 @@ DRIVERS-$(CONFIG_ATM) += drivers/atm/atm.o DRIVERS-$(CONFIG_IDE) += drivers/ide/idedriver.o DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsidrv.o +DRIVERS-$(CONFIG_FUSION_BOOT) += drivers/message/fusion/fusion.o DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394drv.o ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),) @@ -163,7 +171,7 @@ DRIVERS-$(CONFIG_FC4) += drivers/fc4/fc4.a DRIVERS-$(CONFIG_ALL_PPC) += drivers/macintosh/macintosh.o DRIVERS-$(CONFIG_MAC) += drivers/macintosh/macintosh.o -DRIVERS-$(CONFIG_ISAPNP) += drivers/pnp/pnp.o +DRIVERS-$(CONFIG_PNP) += drivers/pnp/pnp.o DRIVERS-$(CONFIG_SGI_IP22) += drivers/sgi/sgi.a DRIVERS-$(CONFIG_VT) += drivers/video/video.o DRIVERS-$(CONFIG_PARIDE) += drivers/block/paride/paride.a @@ -171,7 +179,7 @@ DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o DRIVERS-$(CONFIG_INPUT) += drivers/input/inputdrv.o -DRIVERS-$(CONFIG_I2O) += drivers/i2o/i2o.o +DRIVERS-$(CONFIG_I2O) += drivers/message/i2o/i2o.o DRIVERS-$(CONFIG_IRDA) += drivers/net/irda/irda.o DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.o DRIVERS-$(CONFIG_PHONE) += drivers/telephony/telephony.o @@ -330,14 +338,14 @@ TAGS: dummy etags `find include/asm-$(ARCH) -name '*.h'` find include -type d \( -name "asm-*" -o -name config \) -prune -o -name '*.h' -print | xargs etags -a - find $(SUBDIRS) init -name '*.c' | xargs etags -a + find $(SUBDIRS) init -name '*.[ch]' | xargs etags -a # Exuberant ctags works better with -I tags: dummy CTAGSF=`ctags --version | grep -i exuberant >/dev/null && echo "-I __initdata,__exitdata,EXPORT_SYMBOL,EXPORT_SYMBOL_NOVERS"`; \ ctags $$CTAGSF `find include/asm-$(ARCH) -name '*.h'` && \ find include -type d \( -name "asm-*" -o -name config \) -prune -o -name '*.h' -print | xargs ctags $$CTAGSF -a && \ - find $(SUBDIRS) init -name '*.c' | xargs ctags $$CTAGSF -a + find $(SUBDIRS) init -name '*.[ch]' | xargs ctags $$CTAGSF -a ifdef CONFIG_MODULES ifdef CONFIG_MODVERSIONS @@ -414,7 +422,8 @@ $(MAKE) -C Documentation/DocBook mrproper distclean: mrproper - rm -f core `find . \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ + rm -f core `find . \( -not -type d \) -and \ + \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ -o -name '.*.rej' -o -name '.SUMS' -o -size 0 \) -type f -print` TAGS tags diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/kernel/sys_alcor.c linux.ac/arch/alpha/kernel/sys_alcor.c --- linux.vanilla/arch/alpha/kernel/sys_alcor.c Fri Oct 27 18:55:01 2000 +++ linux.ac/arch/alpha/kernel/sys_alcor.c Sat May 26 00:21:50 2001 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -219,11 +220,21 @@ static void alcor_kill_arch(int mode) { - /* Who said DEC engineer's have no sense of humor? ;-) */ - if (alpha_using_srm) { - *(vuip) GRU_RESET = 0x0000dead; - mb(); + switch(mode) { + case LINUX_REBOOT_CMD_RESTART: + /* Who said DEC engineer's have no sense of humor? ;-) */ + if (alpha_using_srm) { + *(vuip) GRU_RESET = 0x0000dead; + mb(); + } + break; + case LINUX_REBOOT_CMD_HALT: + break; + case LINUX_REBOOT_CMD_POWER_OFF: + break; } + + halt(); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/mm/fault.c linux.ac/arch/alpha/mm/fault.c --- linux.vanilla/arch/alpha/mm/fault.c Tue Apr 3 17:31:52 2001 +++ linux.ac/arch/alpha/mm/fault.c Tue Apr 3 17:54:29 2001 @@ -24,10 +24,37 @@ #include #include #include +#include #include #include +extern spinlock_t timerlist_lock; +void bust_spinlocks(int yes) +{ + spin_lock_init(&timerlist_lock); + if (yes) { + oops_in_progress = 1; +#ifdef CONFIG_SMP + spin_lock_init(&global_irq_lock); /* Many serial drivers do __global_cli() */ +#endif + } else { + int loglevel_save = console_loglevel; + unblank_screen(); + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; + } +} + + + extern void die_if_kernel(char *,struct pt_regs *,long, unsigned long *); @@ -223,7 +250,7 @@ pgd = current->active_mm->pgd + offset; pgd_k = swapper_pg_dir + offset; - if (!pgd_present(*pgd) && pgd_present(*pgd_k)) { + if (pgd_present(*pgd_k)) { pgd_val(*pgd) = pgd_val(*pgd_k); return; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/alpha/mm/init.c linux.ac/arch/alpha/mm/init.c --- linux.vanilla/arch/alpha/mm/init.c Sat May 26 16:52:53 2001 +++ linux.ac/arch/alpha/mm/init.c Sat May 26 17:01:07 2001 @@ -32,6 +32,9 @@ #include #include #include +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; unsigned long totalram_pages; @@ -400,7 +403,7 @@ si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/arm/mm/init.c linux.ac/arch/arm/mm/init.c --- linux.vanilla/arch/arm/mm/init.c Mon Apr 30 15:13:08 2001 +++ linux.ac/arch/arm/mm/init.c Wed May 23 23:52:06 2001 @@ -33,6 +33,9 @@ #include #include +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; #ifndef CONFIG_DISCONTIGMEM #define NR_NODES 1 @@ -647,7 +650,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/cris/drivers/ide.c linux.ac/arch/cris/drivers/ide.c --- linux.vanilla/arch/cris/drivers/ide.c Mon Apr 30 15:13:08 2001 +++ linux.ac/arch/cris/drivers/ide.c Thu Apr 19 14:13:36 2001 @@ -1,4 +1,4 @@ -/* $Id: ide.c,v 1.9 2001/03/01 13:11:18 bjornw Exp $ +/* $Id: ide.c,v 1.16 2001/04/05 08:30:07 matsfg Exp $ * * Etrax specific IDE functions, like init and PIO-mode setting etc. * Almost the entire ide.c is used for the rest of the Etrax ATA driver. @@ -8,6 +8,28 @@ * Mikael Starvik (pio setup stuff) * * $Log: ide.c,v $ + * Revision 1.16 2001/04/05 08:30:07 matsfg + * Corrected cse1 and csp0 reset. + * + * Revision 1.15 2001/04/04 14:34:06 bjornw + * Re-instated code that mysteriously disappeared during review updates. + * + * Revision 1.14 2001/04/04 13:45:12 matsfg + * Calls REG_SHADOW_SET for cse1 reset so only the resetbit is affected + * + * Revision 1.13 2001/04/04 13:26:40 matsfg + * memmapping is done in init.c + * + * Revision 1.12 2001/04/04 11:37:56 markusl + * Updated according to review remarks + * + * Revision 1.11 2001/03/29 12:49:14 matsfg + * Changed check for ata_tot_size from >= to >. + * Sets sw_len to 0 if size is exactly 65536. + * + * Revision 1.10 2001/03/16 09:39:30 matsfg + * Support for reset on port CSP0 + * * Revision 1.9 2001/03/01 13:11:18 bjornw * 100 -> HZ * @@ -158,6 +180,10 @@ #define ATA_PIO0_STROBE 19 #define ATA_PIO0_HOLD 4 +static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive); +static void e100_ideproc (ide_ide_action_t func, ide_drive_t *drive, + void *buffer, unsigned int length); + /* * good_dma_drives() lists the model names (from "hdparm -i") * of drives which do not support mword2 DMA but which are @@ -174,7 +200,7 @@ unsigned long flags; pio = 4; - //pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + /* pio = ide_get_best_pio_mode(drive, pio, 4, NULL); */ save_flags(flags); cli(); @@ -226,10 +252,6 @@ restore_flags(flags); } -static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive); /* defined below */ -static void e100_ideproc (ide_ide_action_t func, ide_drive_t *drive, - void *buffer, unsigned int length); /* defined below */ - void __init init_e100_ide (void) { @@ -277,26 +299,23 @@ *R_GEN_CONFIG = genconfig_shadow; #ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET -#ifndef CONFIG_CRIS_LOW_MAP - /* remap the I/O-mapped reset-bit from CSE1 to something inside our kernel space */ - reset_addr = (unsigned long *)ioremap((unsigned long)(MEM_CSE1_START | - MEM_NON_CACHEABLE), 16); - *reset_addr = 0; -#else - /* LOW_MAP, can't do the ioremap, but it's already mapped straight over */ - reset_addr = (unsigned long *)(MEM_CSE1_START | MEM_NON_CACHEABLE); - *reset_addr = 0; + init_ioremap(); + REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 0); #endif + +#ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET + init_ioremap(); + REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 0); #endif /* wait some */ - - dummy = 1; - dummy = 2; - dummy = 3; + udelay(25); #ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET - *reset_addr = 1 << 16; + REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 1); +#endif +#ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET + REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 1); #endif #ifdef CONFIG_ETRAX_IDE_G27_RESET *R_PORT_G_DATA = 0; /* de-assert bus-reset */ @@ -349,7 +368,6 @@ e100_atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) { ide_ioreg_t data_reg = IDE_DATA_REG; - unsigned long status; D(printk("atapi_input_bytes, dreg 0x%x, buffer 0x%x, count %d\n", data_reg, buffer, bytecount)); @@ -376,7 +394,7 @@ /* initiate a multi word dma read using PIO handshaking */ - *R_ATA_TRANSFER_CNT = bytecount >> 1; + *R_ATA_TRANSFER_CNT = IO_FIELD(R_ATA_TRANSFER_CNT, count, bytecount >> 1); *R_ATA_CTRL_DATA = data_reg | IO_STATE(R_ATA_CTRL_DATA, rw, read) | @@ -390,35 +408,38 @@ LED_DISK_READ(1); WAIT_DMA(3); LED_DISK_READ(0); - + #if 0 - /* old polled transfer code */ - - /* initiate a multi word read */ - - *R_ATA_TRANSFER_CNT = wcount << 1; - - *R_ATA_CTRL_DATA = data_reg | - IO_STATE(R_ATA_CTRL_DATA, rw, read) | - IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | - IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | - IO_STATE(R_ATA_CTRL_DATA, multi, on) | - IO_STATE(R_ATA_CTRL_DATA, dma_size, word); - - /* svinto has a latency until the busy bit actually is set */ - - nop(); nop(); - nop(); nop(); - nop(); nop(); - nop(); nop(); - nop(); nop(); - - /* unit should be busy during multi transfer */ - while((status = *R_ATA_STATUS_DATA) & IO_MASK(R_ATA_STATUS_DATA, busy)) { - while(!(status & IO_MASK(R_ATA_STATUS_DATA, dav))) - status = *R_ATA_STATUS_DATA; - *ptr++ = (unsigned short)(status & 0xffff); - } + /* old polled transfer code + * this should be moved into a new function that can do polled + * transfers if DMA is not available + */ + + /* initiate a multi word read */ + + *R_ATA_TRANSFER_CNT = wcount << 1; + + *R_ATA_CTRL_DATA = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, read) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + /* svinto has a latency until the busy bit actually is set */ + + nop(); nop(); + nop(); nop(); + nop(); nop(); + nop(); nop(); + nop(); nop(); + + /* unit should be busy during multi transfer */ + while((status = *R_ATA_STATUS_DATA) & IO_MASK(R_ATA_STATUS_DATA, busy)) { + while(!(status & IO_MASK(R_ATA_STATUS_DATA, dav))) + status = *R_ATA_STATUS_DATA; + *ptr++ = (unsigned short)(status & 0xffff); + } #endif } @@ -426,8 +447,6 @@ e100_atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) { ide_ioreg_t data_reg = IDE_DATA_REG; - unsigned short *ptr = (unsigned short *)buffer; - unsigned long ctrl; D(printk("atapi_output_bytes, dreg 0x%x, buffer 0x%x, count %d\n", data_reg, buffer, bytecount)); @@ -454,7 +473,7 @@ /* initiate a multi word dma write using PIO handshaking */ - *R_ATA_TRANSFER_CNT = bytecount >> 1; + *R_ATA_TRANSFER_CNT = IO_FIELD(R_ATA_TRANSFER_CNT, count, bytecount >> 1); *R_ATA_CTRL_DATA = data_reg | IO_STATE(R_ATA_CTRL_DATA, rw, write) | @@ -470,40 +489,42 @@ LED_DISK_WRITE(0); #if 0 - /* old polled write code */ + /* old polled write code - see comment in input_bytes */ - while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag */ + /* wait for busy flag */ + while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); - /* initiate a multi word write */ + /* initiate a multi word write */ - *R_ATA_TRANSFER_CNT = bytecount >> 1; + *R_ATA_TRANSFER_CNT = bytecount >> 1; - ctrl = data_reg | - IO_STATE(R_ATA_CTRL_DATA, rw, write) | - IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | - IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | - IO_STATE(R_ATA_CTRL_DATA, multi, on) | - IO_STATE(R_ATA_CTRL_DATA, dma_size, word); - - LED_DISK_WRITE(1); - - /* Etrax will set busy = 1 until the multi pio transfer has finished - * and tr_rdy = 1 after each succesful word transfer. - * When the last byte has been transferred Etrax will first set tr_tdy = 1 - * and then busy = 0 (not in the same cycle). If we read busy before it - * has been set to 0 we will think that we should transfer more bytes - * and then tr_rdy would be 0 forever. This is solved by checking busy - * in the inner loop. - */ - - do { - *R_ATA_CTRL_DATA = ctrl | *ptr++; - while(!(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, tr_rdy)) && - (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))); - } while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); + ctrl = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, write) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + LED_DISK_WRITE(1); + + /* Etrax will set busy = 1 until the multi pio transfer has finished + * and tr_rdy = 1 after each succesful word transfer. + * When the last byte has been transferred Etrax will first set tr_tdy = 1 + * and then busy = 0 (not in the same cycle). If we read busy before it + * has been set to 0 we will think that we should transfer more bytes + * and then tr_rdy would be 0 forever. This is solved by checking busy + * in the inner loop. + */ + + do { + *R_ATA_CTRL_DATA = ctrl | *ptr++; + while(!(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, tr_rdy)) && + (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))); + } while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); + + LED_DISK_WRITE(0); +#endif - LED_DISK_WRITE(0); -#endif } /* @@ -604,7 +625,7 @@ those blocks that were actually set-up for transfer. */ - if(ata_tot_size + size >= 131072) { + if(ata_tot_size + size > 131072) { printk("too large total ATA DMA request, %d + %d!\n", ata_tot_size, size); return 1; } @@ -625,7 +646,12 @@ addr += 65536; } /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */ - ata_descrs[count].sw_len = size; + if(size == 65536) { + ata_descrs[count].sw_len = 0; /* 0 means 65536, this is a 16-bit field */ + } + else { + ata_descrs[count].sw_len = size; + } ata_descrs[count].ctrl = 0; ata_descrs[count].buf = addr; ata_descrs[count].next = virt_to_phys(&ata_descrs[count + 1]); @@ -793,9 +819,11 @@ /* initiate a multi word dma read using DMA handshaking */ - *R_ATA_TRANSFER_CNT = ata_tot_size >> 1; + *R_ATA_TRANSFER_CNT = + IO_FIELD(R_ATA_TRANSFER_CNT, count, ata_tot_size >> 1); - *R_ATA_CTRL_DATA = IDE_DATA_REG | + *R_ATA_CTRL_DATA = + IO_FIELD(R_ATA_CTRL_DATA, data, IDE_DATA_REG) | IO_STATE(R_ATA_CTRL_DATA, rw, read) | IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | @@ -834,9 +862,11 @@ /* initiate a multi word dma write using DMA handshaking */ - *R_ATA_TRANSFER_CNT = ata_tot_size >> 1; + *R_ATA_TRANSFER_CNT = + IO_FIELD(R_ATA_TRANSFER_CNT, count, ata_tot_size >> 1); - *R_ATA_CTRL_DATA = IDE_DATA_REG | + *R_ATA_CTRL_DATA = + IO_FIELD(R_ATA_CTRL_DATA, data, IDE_DATA_REG) | IO_STATE(R_ATA_CTRL_DATA, rw, write) | IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/cris/mm/init.c linux.ac/arch/cris/mm/init.c --- linux.vanilla/arch/cris/mm/init.c Sat May 26 16:52:54 2001 +++ linux.ac/arch/cris/mm/init.c Wed May 23 23:52:06 2001 @@ -442,7 +442,7 @@ i = max_mapnr; val->totalram = 0; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); while (i-- > 0) { Binary files linux.vanilla/arch/i386/boot/bbootsect and linux.ac/arch/i386/boot/bbootsect differ Binary files linux.vanilla/arch/i386/boot/bsetup and linux.ac/arch/i386/boot/bsetup differ Binary files linux.vanilla/arch/i386/boot/compressed/bvmlinux and linux.ac/arch/i386/boot/compressed/bvmlinux differ Binary files linux.vanilla/arch/i386/boot/compressed/bvmlinux.out and linux.ac/arch/i386/boot/compressed/bvmlinux.out differ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/boot/install.sh linux.ac/arch/i386/boot/install.sh --- linux.vanilla/arch/i386/boot/install.sh Tue Jan 3 11:57:26 1995 +++ linux.ac/arch/i386/boot/install.sh Tue Apr 3 17:54:29 2001 @@ -21,6 +21,7 @@ # User may have a custom install script +if [ -x ~/bin/installkernel ]; then exec ~/bin/installkernel "$@"; fi if [ -x /sbin/installkernel ]; then exec /sbin/installkernel "$@"; fi # Default install - same as make zlilo diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/boot/setup.S linux.ac/arch/i386/boot/setup.S --- linux.vanilla/arch/i386/boot/setup.S Sat May 26 16:52:54 2001 +++ linux.ac/arch/i386/boot/setup.S Wed May 2 14:06:09 2001 @@ -233,8 +233,8 @@ # Move rest of setup code/data to here movw $2048, %di # four sectors loaded by LILO subw %si, %si - movw %cs, %ax # aka SETUPSEG - movw %ax, %es + pushw %cs + popw %es movw $SYSSEG, %ax movw %ax, %ds rep Binary files linux.vanilla/arch/i386/boot/tools/build and linux.ac/arch/i386/boot/tools/build differ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/config.in linux.ac/arch/i386/config.in --- linux.vanilla/arch/i386/config.in Sat May 26 16:52:54 2001 +++ linux.ac/arch/i386/config.in Sat May 26 17:01:18 2001 @@ -135,18 +135,21 @@ define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y + define_bool CONFIG_X86_OOSTORE y fi if [ "$CONFIG_MWINCHIP2" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_TSC y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y + define_bool CONFIG_X86_OOSTORE y fi if [ "$CONFIG_MWINCHIP3D" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_TSC y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y + define_bool CONFIG_X86_OOSTORE y fi tristate 'Toshiba Laptop support' CONFIG_TOSHIBA @@ -170,10 +173,13 @@ bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR bool 'Symmetric multi-processing support' CONFIG_SMP if [ "$CONFIG_SMP" != "y" ]; then - bool 'APIC and IO-APIC support on uniprocessors' CONFIG_X86_UP_IOAPIC + bool 'APIC support on uniprocessors' CONFIG_X86_UP_APIC + dep_bool 'IO-APIC support on uniprocessors' CONFIG_X86_UP_IOAPIC $CONFIG_X86_UP_APIC + if [ "$CONFIG_X86_UP_APIC" = "y" ]; then + define_bool CONFIG_X86_LOCAL_APIC y + fi if [ "$CONFIG_X86_UP_IOAPIC" = "y" ]; then define_bool CONFIG_X86_IO_APIC y - define_bool CONFIG_X86_LOCAL_APIC y fi fi @@ -299,9 +305,11 @@ fi endmenu +source drivers/message/fusion/Config.in + source drivers/ieee1394/Config.in -source drivers/i2o/Config.in +source drivers/message/i2o/Config.in if [ "$CONFIG_NET" = "y" ]; then mainmenu_option next_comment @@ -378,6 +386,16 @@ mainmenu_option next_comment comment 'Kernel hacking' -#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC -bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +bool 'Kernel debugging' CONFIG_DEBUG_KERNEL + +if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then + + +bool ' Debug memory allocations' CONFIG_DEBUG_SLAB +bool ' Memory mapped I/O debugging' CONFIG_DEBUG_IOVIRT +bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ +bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK +bool ' Verbose BUG() reporting (adds 70K)' CONFIG_DEBUG_BUGVERBOSE +fi + endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/defconfig linux.ac/arch/i386/defconfig --- linux.vanilla/arch/i386/defconfig Sat May 26 16:52:54 2001 +++ linux.ac/arch/i386/defconfig Wed May 23 00:03:58 2001 @@ -112,6 +112,7 @@ # CONFIG_PNP=y CONFIG_ISAPNP=y +CONFIG_PNPBIOS=y # # Block devices @@ -731,4 +732,5 @@ # # Kernel hacking # +CONFIG_DEBUG_IOVIRT=y # CONFIG_MAGIC_SYSRQ is not set diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/Makefile linux.ac/arch/i386/kernel/Makefile --- linux.vanilla/arch/i386/kernel/Makefile Fri Dec 29 22:35:47 2000 +++ linux.ac/arch/i386/kernel/Makefile Tue Apr 3 17:54:29 2001 @@ -30,15 +30,20 @@ endif endif -obj-$(CONFIG_MCA) += mca.o -obj-$(CONFIG_MTRR) += mtrr.o -obj-$(CONFIG_X86_MSR) += msr.o -obj-$(CONFIG_X86_CPUID) += cpuid.o -obj-$(CONFIG_MICROCODE) += microcode.o -obj-$(CONFIG_APM) += apm.o -obj-$(CONFIG_SMP) += smp.o smpboot.o trampoline.o -obj-$(CONFIG_X86_LOCAL_APIC) += apic.o -obj-$(CONFIG_X86_IO_APIC) += io_apic.o mpparse.o +obj-$(CONFIG_MCA) += mca.o +obj-$(CONFIG_MTRR) += mtrr.o +obj-$(CONFIG_X86_MSR) += msr.o +obj-$(CONFIG_X86_CPUID) += cpuid.o +obj-$(CONFIG_MICROCODE) += microcode.o +obj-$(CONFIG_APM) += apm.o +obj-$(CONFIG_SMP) += smp.o smpboot.o trampoline.o +obj-$(CONFIG_X86_LOCAL_APIC) += mpparse.o apic.o nmi.o +obj-$(CONFIG_X86_IO_APIC) += io_apic.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o + +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/apic.c linux.ac/arch/i386/kernel/apic.c --- linux.vanilla/arch/i386/kernel/apic.c Tue Dec 5 20:43:48 2000 +++ linux.ac/arch/i386/kernel/apic.c Tue Apr 3 17:54:30 2001 @@ -9,6 +9,7 @@ * and Rolf G. Tews * for testing these extensively. * Maciej W. Rozycki : Various updates and fixes. + * Mikael Pettersson : Power Management for UP-APIC. */ #include @@ -23,11 +24,15 @@ #include #include +#include #include #include #include #include +/* Using APIC to generate smp_local_timer_interrupt? */ +int using_apic_timer = 0; + int prof_multiplier[NR_CPUS] = { 1, }; int prof_old_multiplier[NR_CPUS] = { 1, }; int prof_counter[NR_CPUS] = { 1, }; @@ -89,10 +94,10 @@ */ clear_local_APIC(); /* - * PIC mode, enable symmetric IO mode in the IMCR, - * i.e. connect BSP's local APIC to INT and NMI lines. + * PIC mode, enable APIC mode in the IMCR, i.e. + * connect BSP's local APIC to INT and NMI lines. */ - printk("leaving PIC mode, enabling symmetric IO mode.\n"); + printk("leaving PIC mode, enabling APIC mode.\n"); outb(0x70, 0x22); outb(0x01, 0x23); } @@ -107,7 +112,7 @@ * interrupts, including IPIs, won't work beyond * this point! The only exception are INIT IPIs. */ - printk("disabling symmetric IO mode, entering PIC mode.\n"); + printk("disabling APIC mode, entering PIC mode.\n"); outb(0x70, 0x22); outb(0x00, 0x23); } @@ -124,7 +129,7 @@ * for 82489DX!). */ value = apic_read(APIC_SPIV); - value &= ~(1<<8); + value &= ~APIC_SPIV_APIC_ENABLED; apic_write_around(APIC_SPIV, value); } @@ -203,6 +208,48 @@ extern void __error_in_apic_c (void); +/* + * An initial setup of the virtual wire mode. + */ +void __init init_bsp_APIC(void) +{ + unsigned long value, ver; + + /* + * Don't do the setup now if we have a SMP BIOS as the + * through-I/O-APIC virtual wire mode might be active. + */ + if (smp_found_config || !cpu_has_apic) + return; + + value = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(value); + + /* + * Do not trust the local APIC being empty at bootup. + */ + clear_local_APIC(); + + /* + * Enable APIC. + */ + value = apic_read(APIC_SPIV); + value &= ~APIC_VECTOR_MASK; + value |= APIC_SPIV_APIC_ENABLED; + value |= APIC_SPIV_FOCUS_DISABLED; + value |= SPURIOUS_APIC_VECTOR; + apic_write_around(APIC_SPIV, value); + + /* + * Set up the virtual wire mode. + */ + apic_write_around(APIC_LVT0, APIC_DM_EXTINT); + value = APIC_DM_NMI; + if (!APIC_INTEGRATED(ver)) /* 82489DX */ + value |= APIC_LVT_LEVEL_TRIGGER; + apic_write_around(APIC_LVT1, value); +} + void __init setup_local_APIC (void) { unsigned long value, ver, maxlvt; @@ -255,7 +302,7 @@ /* * Enable APIC */ - value |= (1<<8); + value |= APIC_SPIV_APIC_ENABLED; /* * Some unknown Intel IO/APIC (or APIC) errata is biting us with @@ -270,12 +317,18 @@ * PCI Ne2000 networking cards and PII/PIII processors, dual * BX chipset. ] */ -#if 0 + /* + * Actually disabling the focus CPU check just makes the hang less + * frequent as it makes the interrupt distributon model be more + * like LRU than MRU (the short-term load is more even across CPUs). + * See also the comment in end_level_ioapic_irq(). --macro + */ +#if 1 /* Enable focus processor (bit==0) */ - value &= ~(1<<9); + value &= ~APIC_SPIV_FOCUS_DISABLED; #else /* Disable focus processor (bit==1) */ - value |= (1<<9); + value |= APIC_SPIV_FOCUS_DISABLED; #endif /* * Set spurious IRQ vector @@ -332,24 +385,250 @@ printk("ESR value after enabling vector: %08lx\n", value); } else printk("No ESR for 82489DX.\n"); + + if (nmi_watchdog == NMI_LOCAL_APIC) + setup_apic_nmi_watchdog(); } -void __init init_apic_mappings(void) +#ifdef CONFIG_PM + +#include +#include + +static struct { + /* 'active' is true if the local APIC was enabled by us and + not the BIOS; this signifies that we are also responsible + for disabling it before entering apm/acpi suspend */ + int active; + /* 'perfctr_pmdev' is here because the current (2.4.1) PM + callback system doesn't handle hierarchical dependencies */ + struct pm_dev *perfctr_pmdev; + /* r/w apic fields */ + unsigned int apic_id; + unsigned int apic_taskpri; + unsigned int apic_ldr; + unsigned int apic_dfr; + unsigned int apic_spiv; + unsigned int apic_lvtt; + unsigned int apic_lvtpc; + unsigned int apic_lvt0; + unsigned int apic_lvt1; + unsigned int apic_lvterr; + unsigned int apic_tmict; + unsigned int apic_tdcr; +} apic_pm_state; + +static void apic_pm_suspend(void *data) { - unsigned long apic_phys; + unsigned int l, h; + unsigned long flags; - if (smp_found_config) { - apic_phys = mp_lapic_addr; - } else { + if (apic_pm_state.perfctr_pmdev) + pm_send(apic_pm_state.perfctr_pmdev, PM_SUSPEND, data); + apic_pm_state.apic_id = apic_read(APIC_ID); + apic_pm_state.apic_taskpri = apic_read(APIC_TASKPRI); + apic_pm_state.apic_ldr = apic_read(APIC_LDR); + apic_pm_state.apic_dfr = apic_read(APIC_DFR); + apic_pm_state.apic_spiv = apic_read(APIC_SPIV); + apic_pm_state.apic_lvtt = apic_read(APIC_LVTT); + apic_pm_state.apic_lvtpc = apic_read(APIC_LVTPC); + apic_pm_state.apic_lvt0 = apic_read(APIC_LVT0); + apic_pm_state.apic_lvt1 = apic_read(APIC_LVT1); + apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR); + apic_pm_state.apic_tmict = apic_read(APIC_TMICT); + apic_pm_state.apic_tdcr = apic_read(APIC_TDCR); + __save_flags(flags); + __cli(); + disable_local_APIC(); + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_ENABLE; + wrmsr(MSR_IA32_APICBASE, l, h); + __restore_flags(flags); +} + +static void apic_pm_resume(void *data) +{ + unsigned int l, h; + unsigned long flags; + + __save_flags(flags); + __cli(); + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE; + wrmsr(MSR_IA32_APICBASE, l, h); + apic_write(APIC_ID, apic_pm_state.apic_id); + apic_write(APIC_DFR, apic_pm_state.apic_dfr); + apic_write(APIC_LDR, apic_pm_state.apic_ldr); + apic_write(APIC_TASKPRI, apic_pm_state.apic_taskpri); + apic_write(APIC_SPIV, apic_pm_state.apic_spiv); + apic_write(APIC_LVT0, apic_pm_state.apic_lvt0); + apic_write(APIC_LVT1, apic_pm_state.apic_lvt1); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc); + apic_write(APIC_LVTT, apic_pm_state.apic_lvtt); + apic_write(APIC_TDCR, apic_pm_state.apic_tdcr); + apic_write(APIC_TMICT, apic_pm_state.apic_tmict); + __restore_flags(flags); + if (apic_pm_state.perfctr_pmdev) + pm_send(apic_pm_state.perfctr_pmdev, PM_RESUME, data); +} + +static int apic_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + switch (rqst) { + case PM_SUSPEND: + apic_pm_suspend(data); + break; + case PM_RESUME: + apic_pm_resume(data); + break; + } + return 0; +} + +/* perfctr driver should call this instead of pm_register() */ +struct pm_dev *apic_pm_register(pm_dev_t type, + unsigned long id, + pm_callback callback) +{ + struct pm_dev *dev; + + if (!apic_pm_state.active) + return pm_register(type, id, callback); + if (apic_pm_state.perfctr_pmdev) + return NULL; /* we're busy */ + dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL); + if (dev) { + memset(dev, 0, sizeof(*dev)); + dev->type = type; + dev->id = id; + dev->callback = callback; + apic_pm_state.perfctr_pmdev = dev; + } + return dev; +} + +/* perfctr driver should call this instead of pm_unregister() */ +void apic_pm_unregister(struct pm_dev *dev) +{ + if (!apic_pm_state.active) { + pm_unregister(dev); + } else if (dev == apic_pm_state.perfctr_pmdev) { + apic_pm_state.perfctr_pmdev = NULL; + kfree(dev); + } +} + +static void __init apic_pm_init1(void) +{ + /* can't pm_register() at this early stage in the boot process + (causes an immediate reboot), so just set the flag */ + apic_pm_state.active = 1; +} + +static void __init apic_pm_init2(void) +{ + if (apic_pm_state.active) + pm_register(PM_SYS_DEV, 0, apic_pm_callback); +} + +#else /* CONFIG_PM */ + +static inline void apic_pm_init1(void) { } +static inline void apic_pm_init2(void) { } + +#endif /* CONFIG_PM */ + +/* + * Detect and enable local APICs on non-SMP boards. + * Original code written by Keir Fraser. + */ + +static int __init detect_init_APIC (void) +{ + u32 h, l, features; + int needs_pm = 0; + extern void get_cpu_vendor(struct cpuinfo_x86*); + + /* Workaround for us being called before identify_cpu(). */ + get_cpu_vendor(&boot_cpu_data); + + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model > 1) + break; + goto no_apic; + case X86_VENDOR_INTEL: + if (boot_cpu_data.x86 == 6 || + (boot_cpu_data.x86 == 5 && cpu_has_apic)) + break; + goto no_apic; + default: + goto no_apic; + } + + if (!cpu_has_apic) { /* - * set up a fake all zeroes page to simulate the - * local APIC and another one for the IO-APIC. We - * could use the real zero-page, but it's safer - * this way if some buggy code writes to this page ... + * Some BIOSes disable the local APIC in the + * APIC_BASE MSR. This can only be done in + * software for Intel P6 and AMD K7 (Model > 1). */ + rdmsr(MSR_IA32_APICBASE, l, h); + if (!(l & MSR_IA32_APICBASE_ENABLE)) { + printk("Local APIC disabled by BIOS -- reenabling.\n"); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE; + wrmsr(MSR_IA32_APICBASE, l, h); + needs_pm = 1; + } + } + /* + * The APIC feature bit should now be enabled + * in `cpuid' + */ + features = cpuid_edx(1); + if (!(features & (1 << X86_FEATURE_APIC))) { + printk("Could not enable APIC!\n"); + return -1; + } + set_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability); + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; + boot_cpu_id = 0; + if (nmi_watchdog != NMI_NONE) + nmi_watchdog = NMI_LOCAL_APIC; + + printk("Found and enabled local APIC!\n"); + + if (needs_pm) + apic_pm_init1(); + + return 0; + +no_apic: + printk("No local APIC present or hardware disabled\n"); + return -1; +} + +void __init init_apic_mappings(void) +{ + unsigned long apic_phys; + + /* + * If no local APIC can be found then set up a fake all + * zeroes page to simulate the local APIC and another + * one for the IO-APIC. + */ + if (!smp_found_config && detect_init_APIC()) { apic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE); apic_phys = __pa(apic_phys); - } + } else + apic_phys = mp_lapic_addr; + set_fixmap_nocache(FIX_APIC_BASE, apic_phys); Dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys); @@ -596,6 +875,9 @@ void __init setup_APIC_clocks (void) { + printk("Using local APIC timer interrupts.\n"); + using_apic_timer = 1; + __cli(); calibration_result = calibrate_APIC_clock(); @@ -764,7 +1046,7 @@ apic_write(APIC_ESR, 0); v1 = apic_read(APIC_ESR); ack_APIC_irq(); - irq_err_count++; + atomic_inc(&irq_err_count); /* Here is what the APIC error bits mean: 0: Send CS error @@ -780,3 +1062,43 @@ smp_processor_id(), v , v1); } +/* + * This initializes the IO-APIC and APIC hardware if this is + * a UP kernel. + */ +int __init APIC_init_uniprocessor (void) +{ + if (!smp_found_config && !cpu_has_apic) + return -1; + + /* + * Complain if the BIOS pretends there is one. + */ + if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_id])) { + printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n", + boot_cpu_id); + return -1; + } + + verify_local_APIC(); + + connect_bsp_APIC(); + + phys_cpu_present_map = 1; + apic_write_around(APIC_ID, boot_cpu_id); + + apic_pm_init2(); + + setup_local_APIC(); + + if (nmi_watchdog == NMI_LOCAL_APIC) + check_nmi_watchdog(); +#ifdef CONFIG_X86_IO_APIC + if (smp_found_config) + if (!skip_ioapic_setup && nr_ioapics) + setup_IO_APIC(); +#endif + setup_APIC_clocks(); + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/apm.c linux.ac/arch/i386/kernel/apm.c --- linux.vanilla/arch/i386/kernel/apm.c Mon Apr 30 15:13:08 2001 +++ linux.ac/arch/i386/kernel/apm.c Tue May 1 14:47:38 2001 @@ -166,6 +166,11 @@ * * [This document is available from Microsoft at: * http://www.microsoft.com/hwdev/busbios/amp_12.htm] + * + * (c) 2000 Crutcher Dunnavant + * changed to use the sysrq-register hack for registering + * the power off function called by magic sysrq + * based upon discusions in irc://irc.openprojects.net/#kernelnewbies */ #include @@ -191,12 +196,11 @@ #include #include +#include + 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 @@ -347,6 +351,12 @@ #endif static int exit_kapmd; static int kapmd_running; +#ifdef CONFIG_APM_ALLOW_INTS +static int allow_ints = 1; +#else +static int allow_ints; +#endif +static int broken_psr; static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); @@ -413,11 +423,12 @@ * Also, we KNOW that for the non error case of apm_bios_call, there * is no useful data returned in the low order 8 bits of eax. */ -#ifndef CONFIG_APM_ALLOW_INTS -# define APM_DO_CLI __cli() -#else -# define APM_DO_CLI __sti() -#endif +#define APM_DO_CLI \ + if (apm_info.allow_ints) \ + __sti(); \ + else \ + __cli(); + #ifdef APM_ZERO_SEGS # define APM_DECL_SEGS \ unsigned int saved_fs; unsigned int saved_gs; @@ -671,6 +682,23 @@ #endif } +#ifdef CONFIG_MAGIC_SYSRQ +/* + * Magic sysrq key and handler for the power off function + */ + +void handle_poweroff (int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + apm_power_off(); +} +struct sysrq_key_op sysrq_poweroff_op = { + handler: handle_poweroff, + help_msg: "Off", + action_msg: "Power Off\n" +}; +#endif + + #ifdef CONFIG_APM_DO_ENABLE static int apm_enable_power_management(int enable) { @@ -1525,9 +1553,8 @@ /* Install our power off handler.. */ if (power_off) pm_power_off = apm_power_off; -#ifdef CONFIG_MAGIC_SYSRQ - sysrq_power_off = apm_power_off; -#endif + register_sysrq_key('o',&sysrq_poweroff_op); + if (smp_num_cpus == 1) { #if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT) console_blank_hook = apm_console_blank; @@ -1552,6 +1579,9 @@ apm_disabled = 1; if (strncmp(str, "on", 2) == 0) apm_disabled = 0; + if ((strncmp(str, "allow-ints", 10) == 0) || + (strncmp(str, "allow_ints", 10) == 0)) + apm_info.allow_ints = 1; if ((strncmp(str, "broken-psr", 10) == 0) || (strncmp(str, "broken_psr", 10) == 0)) apm_info.get_power_status_broken = 1; @@ -1620,6 +1650,11 @@ return -ENODEV; } + if (allow_ints) + apm_info.allow_ints = 1; + if (broken_psr) + apm_info.get_power_status_broken = 1; + /* * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1 * but is reportedly a 1.0 BIOS. @@ -1725,9 +1760,7 @@ } misc_deregister(&apm_device); remove_proc_entry("apm", NULL); -#ifdef CONFIG_MAGIC_SYSRQ - sysrq_power_off = NULL; -#endif + unregister_sysrq_key('o',&sysrq_poweroff_op); if (power_off) pm_power_off = NULL; exit_kapmd = 1; @@ -1747,5 +1780,9 @@ MODULE_PARM_DESC(power_off, "Enable power off"); MODULE_PARM(bounce_interval, "i"); MODULE_PARM_DESC(bounce_interval, "Set the number of ticks to ignore suspend bounces"); +MODULE_PARM(allow_ints, "i"); +MODULE_PARM_DESC(allow_ints, "Allow interrupts during BIOS calls"); +MODULE_PARM(broken_psr, "i"); +MODULE_PARM_DESC(broken_psr, "BIOS has a broken GetPowerStatus call"); EXPORT_NO_SYMBOLS; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/bluesmoke.c linux.ac/arch/i386/kernel/bluesmoke.c --- linux.vanilla/arch/i386/kernel/bluesmoke.c Sat May 26 16:52:54 2001 +++ linux.ac/arch/i386/kernel/bluesmoke.c Sat May 26 00:26:48 2001 @@ -182,7 +182,7 @@ * Set up machine check reporting on the Winchip C6 series */ -static void winchip_mcheck_init(struct cpuinfo_x86 *c) +static void __init winchip_mcheck_init(struct cpuinfo_x86 *c) { u32 lo, hi; /* Not supported on C3 */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/dmi_scan.c linux.ac/arch/i386/kernel/dmi_scan.c --- linux.vanilla/arch/i386/kernel/dmi_scan.c Tue Feb 13 22:13:43 2001 +++ linux.ac/arch/i386/kernel/dmi_scan.c Thu May 17 20:34:55 2001 @@ -1,8 +1,10 @@ +#include #include #include #include #include #include +#include #include struct dmi_header @@ -13,6 +15,7 @@ }; #define dmi_printk(x) +//#define dmi_printk(x) printk(x) static char * __init dmi_string(struct dmi_header *dm, u8 s) { @@ -90,9 +93,210 @@ } +enum +{ + DMI_BIOS_VENDOR, + DMI_BIOS_VERSION, + DMI_BIOS_DATE, + DMI_SYS_VENDOR, + DMI_PRODUCT_NAME, + DMI_PRODUCT_VERSION, + DMI_BOARD_VENDOR, + DMI_BOARD_NAME, + DMI_BOARD_VERSION, + DMI_STRING_MAX +}; + +static char *dmi_ident[DMI_STRING_MAX]; + +/* + * Save a DMI string + */ + +static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string) +{ + char *d = (char*)dm; + char *p = dmi_string(dm, d[string]); + if(p==NULL || *p == 0) + return; + if (dmi_ident[slot]) + return; + dmi_ident[slot] = kmalloc(strlen(p)+1, GFP_KERNEL); + if(dmi_ident[slot]) + strcpy(dmi_ident[slot], p); + else + printk(KERN_ERR "dmi_save_ident: out of memory.\n"); +} + +/* + * DMI callbacks for problem boards + */ + +struct dmi_strmatch +{ + u8 slot; + char *substr; +}; + +#define NONE 255 + +struct dmi_blacklist +{ + int (*callback)(struct dmi_blacklist *); + char *ident; + struct dmi_strmatch matches[4]; +}; + +#define NO_MATCH { NONE, NULL} +#define MATCH(a,b) { a, b } + +/* + * We have problems with IDE DMA on some platforms. In paticular the + * KT7 series. On these it seems the newer BIOS has fixed them. The + * rule needs to be improved to match specific BIOS revisions with + * corruption problems + */ + +static __init int disable_ide_dma(struct dmi_blacklist *d) +{ +#ifdef CONFIG_BLK_DEV_IDE + extern int noautodma; + if(noautodma == 0) + { + noautodma = 1; + printk(KERN_INFO "%s series board detected. Disabling IDE DMA.\n", d->ident); + } +#endif + return 0; +} + +/* + * Some machines require the "reboot=b" commandline option, this quirk makes that automatic. + */ +static __init int set_bios_reboot(struct dmi_blacklist *d) +{ + extern int reboot_thru_bios; + if (reboot_thru_bios == 0) + { + reboot_thru_bios = 1; + printk(KERN_INFO "%s series board detected. Selecting BIOS-method for reboots.\n", d->ident); + } + return 0; +} + +/* + * Some laptops require interrupts to be enabled during APM calls + */ + +static __init int set_apm_ints(struct dmi_blacklist *d) +{ + if (apm_info.allow_ints == 0) + { + apm_info.allow_ints = 1; + printk(KERN_INFO "%s machine detected. Enabling interrupts during APM calls.\n", d->ident); + } + return 0; +} + + +/* + * Check for clue free BIOS implementations who use + * the following QA technique + * + * [ Write BIOS Code ]<------ + * | ^ + * < Does it Compile >----N-- + * |Y ^ + * < Does it Boot Win98 >-N-- + * |Y + * [Ship It] + * + * Phoenix A04 08/24/2000 is known bad (Dell Inspiron 5000e) + * Phoenix A07 09/29/2000 is known good (Dell Inspiron 5000) + */ + +static __init int broken_apm_power(struct dmi_blacklist *d) +{ + apm_info.get_power_status_broken = 1; + printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n"); + return 0; +} + +/* + * Process the DMI blacklists + */ + + +/* + * This will be expanded over time to force things like the APM + * interrupt mask settings according to the laptop + */ + +static __initdata struct dmi_blacklist dmi_blacklist[]={ +#if 0 + { disable_ide_dma, "KT7", { /* Overbroad right now - kill DMA on problem KT7 boards */ + MATCH(DMI_PRODUCT_NAME, "KT7-RAID"), + NO_MATCH, NO_MATCH, NO_MATCH + } }, +#endif + { broken_apm_power, "Dell Inspiron 5000e", { /* Handle problems with APM on Inspiron 5000e */ + MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"), + MATCH(DMI_BIOS_VERSION, "A04"), + MATCH(DMI_BIOS_DATE, "08/24/2000"), NO_MATCH + } }, + { set_bios_reboot, "PowerEdge 1300/500", { /* Handle problems with rebooting on Dell 1300's */ + MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/500"), + NO_MATCH, NO_MATCH + } }, + { set_bios_reboot, "PowerEdge 1300/550", { /* Handle problems with rebooting on Dell 1300's */ + MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/550"), + NO_MATCH, NO_MATCH + } }, + { set_apm_ints, "IBM", { /* Allow interrupts during suspend on IBM laptops */ + MATCH(DMI_SYS_VENDOR, "IBM"), + NO_MATCH, NO_MATCH, NO_MATCH + } }, + { NULL, } +}; + + +/* + * Walk the blacklist table running matching functions until someone + * returns 1 or we hit the end. + */ + +static __init void dmi_check_blacklist(void) +{ + struct dmi_blacklist *d; + int i; + + d=&dmi_blacklist[0]; + while(d->callback) + { + for(i=0;i<4;i++) + { + int s = d->matches[i].slot; + if(s==NONE) + continue; + if(dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr)) + continue; + /* No match */ + goto fail; + } + if(d->callback(d)) + return; +fail: + d++; + } +} + + + /* * Process a DMI table entry. Right now all we care about are the BIOS - * and machine entries. For 2.4 we should pull the smbus controller info + * and machine entries. For 2.5 we should pull the smbus controller info * out of here. */ @@ -105,66 +309,47 @@ { case 0: p=dmi_string(dm,data[4]); - - if(*p && *p!=' ') + if(*p) { dmi_printk(("BIOS Vendor: %s\n", p)); + dmi_save_ident(dm, DMI_BIOS_VENDOR, 4); dmi_printk(("BIOS Version: %s\n", dmi_string(dm, data[5]))); + dmi_save_ident(dm, DMI_BIOS_VERSION, 5); dmi_printk(("BIOS Release: %s\n", dmi_string(dm, data[8]))); - } - - /* - * Check for clue free BIOS implementations who use - * the following QA technique - * - * [ Write BIOS Code ]<------ - * | ^ - * < Does it Compile >----N-- - * |Y ^ - * < Does it Boot Win98 >-N-- - * |Y - * [Ship It] - * - * Phoenix A04 08/24/2000 is known bad (Dell Inspiron 5000e) - * Phoenix A07 09/29/2000 is known good (Dell Inspiron 5000) - */ - - if(strcmp(dmi_string(dm, data[4]), "Phoenix Technologies LTD")==0) - { - if(strcmp(dmi_string(dm, data[5]), "A04")==0 - && strcmp(dmi_string(dm, data[8]), "08/24/2000")==0) - { - apm_info.get_power_status_broken = 1; - printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n"); - } + dmi_save_ident(dm, DMI_BIOS_DATE, 8); } break; + case 1: p=dmi_string(dm,data[4]); - - if(*p && *p!=' ') + if(*p) { dmi_printk(("System Vendor: %s.\n",p)); + dmi_save_ident(dm, DMI_SYS_VENDOR, 4); dmi_printk(("Product Name: %s.\n", dmi_string(dm, data[5]))); + dmi_save_ident(dm, DMI_PRODUCT_NAME, 5); dmi_printk(("Version %s.\n", dmi_string(dm, data[6]))); + dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6); dmi_printk(("Serial Number %s.\n", dmi_string(dm, data[7]))); } break; case 2: p=dmi_string(dm,data[4]); - - if(*p && *p!=' ') + if(*p) { dmi_printk(("Board Vendor: %s.\n",p)); + dmi_save_ident(dm, DMI_BOARD_VENDOR, 4); dmi_printk(("Board Name: %s.\n", dmi_string(dm, data[5]))); + dmi_save_ident(dm, DMI_BOARD_NAME, 5); dmi_printk(("Board Version: %s.\n", dmi_string(dm, data[6]))); + dmi_save_ident(dm, DMI_BOARD_VERSION, 6); } break; case 3: @@ -177,7 +362,10 @@ static int __init dmi_scan_machine(void) { - return dmi_iterate(dmi_decode); + int err = dmi_iterate(dmi_decode); + if(err == 0) + dmi_check_blacklist(); + return err; } module_init(dmi_scan_machine); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/head.S linux.ac/arch/i386/kernel/head.S --- linux.vanilla/arch/i386/kernel/head.S Mon Apr 30 15:13:08 2001 +++ linux.ac/arch/i386/kernel/head.S Tue May 1 08:54:09 2001 @@ -425,14 +425,15 @@ * change anything. */ ENTRY(gdt_table) - .quad 0x0000000000000000 /* NULL descriptor */ - .quad 0x0000000000000000 /* not used */ + .quad 0x0000000000000000 /* 0x00 NULL descriptor */ + .quad 0x0000000000000000 /* 0x08 not used */ .quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */ .quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */ .quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */ .quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */ - .quad 0x0000000000000000 /* not used */ - .quad 0x0000000000000000 /* not used */ + .quad 0x0000000000000000 /* 0x30 not used */ + .quad 0x0000000000000000 /* 0x38 not used */ + /* * The APM segments have byte granularity and their bases * and limits are set at run time. @@ -441,7 +442,19 @@ .quad 0x00409a0000000000 /* 0x48 APM CS code */ .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ .quad 0x0040920000000000 /* 0x58 APM DS data */ - .fill NR_CPUS*4,8,0 /* space for TSS's and LDT's */ + + /* PNPBIOS segments */ + .quad 0x00c09a0000000000 /* 0x60 32-bit code */ + .quad 0x00809a0000000000 /* 0x68 16-bit code */ + .quad 0x0080920000000000 /* 0x70 16-bit data */ + .quad 0x0080920000000000 /* 0x78 16-bit data */ + .quad 0x0080920000000000 /* 0x80 16-bit data */ + .quad 0x0000000000000000 /* 0x88 not used */ + .quad 0x0000000000000000 /* 0x90 not used */ + .quad 0x0000000000000000 /* 0x98 not used */ + + /* Per CPU segments */ + .fill NR_CPUS*4,8,0 /* 0xa0 space for TSS's and LDT's */ /* * This is to aid debugging, the various locking macros will be putting diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/i386_ksyms.c linux.ac/arch/i386/kernel/i386_ksyms.c --- linux.vanilla/arch/i386/kernel/i386_ksyms.c Sat May 26 16:52:54 2001 +++ linux.ac/arch/i386/kernel/i386_ksyms.c Thu May 17 14:00:19 2001 @@ -28,6 +28,7 @@ #include #include #include +#include extern void dump_thread(struct pt_regs *, struct user *); extern spinlock_t rtc_lock; @@ -72,7 +73,7 @@ EXPORT_SYMBOL(apm_info); EXPORT_SYMBOL(gdt); -#ifdef CONFIG_IO_DEBUG +#ifdef CONFIG_DEBUG_IOVIRT EXPORT_SYMBOL(__io_virt_debug); #endif @@ -164,3 +165,9 @@ #ifdef CONFIG_X86_PAE EXPORT_SYMBOL(empty_zero_page); #endif + +#ifdef CONFIG_DEBUG_BUGVERBOSE +EXPORT_SYMBOL(do_BUG); +#endif + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/i8259.c linux.ac/arch/i386/kernel/i8259.c --- linux.vanilla/arch/i386/kernel/i8259.c Fri Feb 9 19:29:44 2001 +++ linux.ac/arch/i386/kernel/i8259.c Tue Apr 3 17:54:30 2001 @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include @@ -321,7 +323,7 @@ printk("spurious 8259A interrupt: IRQ%d.\n", irq); spurious_irq_mask |= irqmask; } - irq_err_count++; + atomic_inc(&irq_err_count); /* * Theoretically we do not have to handle this IRQ, * but in Linux this does not cause problems and is @@ -414,6 +416,9 @@ { int i; +#ifdef CONFIG_X86_LOCAL_APIC + init_bsp_APIC(); +#endif init_8259A(0); for (i = 0; i < NR_IRQS; i++) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/io_apic.c linux.ac/arch/i386/kernel/io_apic.c --- linux.vanilla/arch/i386/kernel/io_apic.c Fri Feb 9 19:28:31 2001 +++ linux.ac/arch/i386/kernel/io_apic.c Tue Apr 3 17:54:30 2001 @@ -33,23 +33,15 @@ #include #include +#undef APIC_LOCKUP_DEBUG + static spinlock_t ioapic_lock = SPIN_LOCK_UNLOCKED; /* - * # of IO-APICs and # of IRQ routing registers + * # of IRQ routing registers */ -int nr_ioapics; int nr_ioapic_registers[MAX_IO_APICS]; -/* I/O APIC entries */ -struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS]; - -/* # of MP IRQ source entries */ -struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; - -/* MP IRQ source entries */ -int mp_irq_entries; - #if CONFIG_SMP # define TARGET_CPUS cpu_online_map #else @@ -122,8 +114,14 @@ static void name##_IO_APIC_irq (unsigned int irq) \ __DO_ACTION(R, ACTION, FINAL) -DO_ACTION( __mask, 0, |= 0x00010000, io_apic_sync(entry->apic))/* mask = 1 */ -DO_ACTION( __unmask, 0, &= 0xfffeffff, ) /* mask = 0 */ +DO_ACTION( __mask, 0, |= 0x00010000, io_apic_sync(entry->apic) ) + /* mask = 1 */ +DO_ACTION( __unmask, 0, &= 0xfffeffff, ) + /* mask = 0 */ +DO_ACTION( __mask_and_edge, 0, = (reg & 0xffff7fff) | 0x00010000, ) + /* mask = 1, trigger = 0 */ +DO_ACTION( __unmask_and_level, 0, = (reg & 0xfffeffff) | 0x00008000, ) + /* mask = 0, trigger = 1 */ static void mask_IO_APIC_irq (unsigned int irq) { @@ -146,14 +144,17 @@ void clear_IO_APIC_pin(unsigned int apic, unsigned int pin) { struct IO_APIC_route_entry entry; + unsigned long flags; /* * Disable it in the IO-APIC irq-routing table: */ memset(&entry, 0, sizeof(entry)); entry.mask = 1; + spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry) + 0)); io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry) + 1)); + spin_unlock_irqrestore(&ioapic_lock, flags); } static void clear_IO_APIC (void) @@ -581,6 +582,7 @@ { struct IO_APIC_route_entry entry; int apic, pin, idx, irq, first_notcon = 1, vector; + unsigned long flags; printk(KERN_DEBUG "init IO_APIC IRQs\n"); @@ -636,8 +638,10 @@ if (!apic && (irq < 16)) disable_8259A_irq(irq); } + spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); + spin_unlock_irqrestore(&ioapic_lock, flags); } } @@ -652,6 +656,7 @@ void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector) { struct IO_APIC_route_entry entry; + unsigned long flags; memset(&entry,0,sizeof(entry)); @@ -681,8 +686,10 @@ /* * Add it to the IO-APIC irq-routing table: */ + spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1)); io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); + spin_unlock_irqrestore(&ioapic_lock, flags); enable_8259A_irq(0); } @@ -699,6 +706,7 @@ struct IO_APIC_reg_00 reg_00; struct IO_APIC_reg_01 reg_01; struct IO_APIC_reg_02 reg_02; + unsigned long flags; printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries); for (i = 0; i < nr_ioapics; i++) @@ -713,10 +721,12 @@ for (apic = 0; apic < nr_ioapics; apic++) { + spin_lock_irqsave(&ioapic_lock, flags); *(int *)®_00 = io_apic_read(apic, 0); *(int *)®_01 = io_apic_read(apic, 1); if (reg_01.version >= 0x10) *(int *)®_02 = io_apic_read(apic, 2); + spin_unlock_irqrestore(&ioapic_lock, flags); printk("\n"); printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].mpc_apicid); @@ -762,8 +772,10 @@ for (i = 0; i <= reg_01.entries; i++) { struct IO_APIC_route_entry entry; + spin_lock_irqsave(&ioapic_lock, flags); *(((int *)&entry)+0) = io_apic_read(apic, 0x10+i*2); *(((int *)&entry)+1) = io_apic_read(apic, 0x11+i*2); + spin_unlock_irqrestore(&ioapic_lock, flags); printk(KERN_DEBUG " %02x %03X %02X ", i, @@ -790,7 +802,7 @@ continue; printk(KERN_DEBUG "IRQ%d ", i); for (;;) { - printk("-> %d", entry->pin); + printk("-> %d:%d", entry->apic, entry->pin); if (!entry->next) break; entry = irq_2_pin + entry->next; @@ -847,6 +859,8 @@ v = apic_read(APIC_EOI); printk(KERN_DEBUG "... APIC EOI: %08x\n", v); + v = apic_read(APIC_RRR); + printk(KERN_DEBUG "... APIC RRR: %08x\n", v); v = apic_read(APIC_LDR); printk(KERN_DEBUG "... APIC LDR: %08x\n", v); v = apic_read(APIC_DFR); @@ -938,6 +952,7 @@ { struct IO_APIC_reg_01 reg_01; int i; + unsigned long flags; for (i = 0; i < PIN_MAP_SIZE; i++) { irq_2_pin[i].pin = -1; @@ -951,7 +966,9 @@ * The number of IO-APIC IRQ registers (== #pins): */ for (i = 0; i < nr_ioapics; i++) { + spin_lock_irqsave(&ioapic_lock, flags); *(int *)®_01 = io_apic_read(i, 1); + spin_unlock_irqrestore(&ioapic_lock, flags); nr_ioapic_registers[i] = reg_01.entries+1; } @@ -988,6 +1005,7 @@ int apic; int i; unsigned char old_id; + unsigned long flags; /* * Set the IOAPIC ID to the value stored in the MPC table. @@ -995,7 +1013,9 @@ for (apic = 0; apic < nr_ioapics; apic++) { /* Read the register 0 value */ + spin_lock_irqsave(&ioapic_lock, flags); *(int *)®_00 = io_apic_read(apic, 0); + spin_unlock_irqrestore(&ioapic_lock, flags); old_id = mp_ioapics[apic].mpc_apicid; @@ -1044,12 +1064,16 @@ mp_ioapics[apic].mpc_apicid); reg_00.ID = mp_ioapics[apic].mpc_apicid; + spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(apic, 0, *(int *)®_00); + spin_unlock_irqrestore(&ioapic_lock, flags); /* * Sanity check */ + spin_lock_irqsave(&ioapic_lock, flags); *(int *)®_00 = io_apic_read(apic, 0); + spin_unlock_irqrestore(&ioapic_lock, flags); if (reg_00.ID != mp_ioapics[apic].mpc_apicid) panic("could not set ID!\n"); else @@ -1086,25 +1110,6 @@ return 0; } -static int __init nmi_irq_works(void) -{ - irq_cpustat_t tmp[NR_CPUS]; - int j, cpu; - - memcpy(tmp, irq_stat, sizeof(tmp)); - sti(); - mdelay(50); - - for (j = 0; j < smp_num_cpus; j++) { - cpu = cpu_logical_map(j); - if (nmi_count(cpu) - tmp[cpu].__nmi_count <= 3) { - printk(KERN_WARNING "CPU#%d NMI appears to be stuck.\n", cpu); - return 0; - } - } - return 1; -} - /* * In the SMP+IOAPIC case it might happen that there are an unspecified * number of pending IRQ events unhandled. These cases are very rare, @@ -1191,12 +1196,63 @@ #define enable_level_ioapic_irq unmask_IO_APIC_irq #define disable_level_ioapic_irq mask_IO_APIC_irq -static void end_level_ioapic_irq (unsigned int i) +static void end_level_ioapic_irq (unsigned int irq) { + unsigned long v; + int i; + +/* + * It appears there is an erratum which affects at least version 0x11 + * of I/O APIC (that's the 82093AA and cores integrated into various + * chipsets). Under certain conditions a level-triggered interrupt is + * erroneously delivered as edge-triggered one but the respective IRR + * bit gets set nevertheless. As a result the I/O unit expects an EOI + * message but it will never arrive and further interrupts are blocked + * from the source. The exact reason is so far unknown, but the + * phenomenon was observed when two consecutive interrupt requests + * from a given source get delivered to the same CPU and the source is + * temporarily disabled in between. + * + * A workaround is to simulate an EOI message manually. We achieve it + * by setting the trigger mode to edge and then to level when the edge + * trigger mode gets detected in the TMR of a local APIC for a + * level-triggered interrupt. We mask the source for the time of the + * operation to prevent an edge-triggered interrupt escaping meanwhile. + * The idea is from Manfred Spraul. --macro + */ + i = IO_APIC_VECTOR(irq); + v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1)); + ack_APIC_irq(); + + if (!(v & (1 << (i & 0x1f)))) { +#ifdef APIC_MISMATCH_DEBUG + atomic_inc(&irq_mis_count); +#endif + spin_lock(&ioapic_lock); + __mask_and_edge_IO_APIC_irq(irq); +#ifdef APIC_LOCKUP_DEBUG + for (;;) { + struct irq_pin_list *entry = irq_2_pin + irq; + unsigned int reg; + + if (entry->pin == -1) + break; + reg = io_apic_read(entry->apic, 0x10 + entry->pin * 2); + if (reg & 0x00004000) + printk(KERN_CRIT "Aieee!!! Remote IRR" + " still set after unlock!\n"); + if (!entry->next) + break; + entry = irq_2_pin + entry->next; + } +#endif + __unmask_and_level_IO_APIC_irq(irq); + spin_unlock(&ioapic_lock); + } } -static void mask_and_ack_level_ioapic_irq (unsigned int i) { /* nothing */ } +static void mask_and_ack_level_ioapic_irq (unsigned int irq) { /* nothing */ } static void set_ioapic_affinity (unsigned int irq, unsigned long mask) { @@ -1349,13 +1405,16 @@ int pin, i; struct IO_APIC_route_entry entry0, entry1; unsigned char save_control, save_freq_select; + unsigned long flags; pin = find_isa_irq_pin(8, mp_INT); if (pin == -1) return; + spin_lock_irqsave(&ioapic_lock, flags); *(((int *)&entry0) + 1) = io_apic_read(0, 0x11 + 2 * pin); *(((int *)&entry0) + 0) = io_apic_read(0, 0x10 + 2 * pin); + spin_unlock_irqrestore(&ioapic_lock, flags); clear_IO_APIC_pin(0, pin); memset(&entry1, 0, sizeof(entry1)); @@ -1368,8 +1427,10 @@ entry1.trigger = 0; entry1.vector = 0; + spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry1) + 1)); io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry1) + 0)); + spin_unlock_irqrestore(&ioapic_lock, flags); save_control = CMOS_READ(RTC_CONTROL); save_freq_select = CMOS_READ(RTC_FREQ_SELECT); @@ -1388,8 +1449,10 @@ CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); clear_IO_APIC_pin(0, pin); + spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry0) + 1)); io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry0) + 0)); + spin_unlock_irqrestore(&ioapic_lock, flags); } /* @@ -1434,11 +1497,11 @@ */ unmask_IO_APIC_irq(0); if (timer_irq_works()) { - if (nmi_watchdog) { + if (nmi_watchdog == NMI_IO_APIC) { disable_8259A_irq(0); setup_nmi(); enable_8259A_irq(0); - nmi_irq_works(); + check_nmi_watchdog(); } return; } @@ -1455,9 +1518,9 @@ setup_ExtINT_IRQ0_pin(pin2, vector); if (timer_irq_works()) { printk("works.\n"); - if (nmi_watchdog) { + if (nmi_watchdog == NMI_IO_APIC) { setup_nmi(); - nmi_irq_works(); + check_nmi_watchdog(); } return; } @@ -1538,19 +1601,3 @@ check_timer(); print_IO_APIC(); } - -#ifndef CONFIG_SMP -/* - * This initializes the IO-APIC and APIC hardware if this is - * a UP kernel. - */ -void IO_APIC_init_uniprocessor (void) -{ - if (!smp_found_config) - return; - connect_bsp_APIC(); - setup_local_APIC(); - setup_IO_APIC(); - setup_APIC_clocks(); -} -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/irq.c linux.ac/arch/i386/kernel/irq.c --- linux.vanilla/arch/i386/kernel/irq.c Fri Feb 9 19:29:44 2001 +++ linux.ac/arch/i386/kernel/irq.c Thu May 17 22:14:17 2001 @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -119,7 +120,12 @@ end_none }; -volatile unsigned long irq_err_count; +atomic_t irq_err_count; +#ifdef CONFIG_X86_IO_APIC +#ifdef APIC_MISMATCH_DEBUG +atomic_t irq_mis_count; +#endif +#endif /* * Generic, controller-independent functions: @@ -160,14 +166,19 @@ p += sprintf(p, "%10u ", nmi_count(cpu_logical_map(j))); p += sprintf(p, "\n"); -#if CONFIG_SMP +#if CONFIG_X86_LOCAL_APIC p += sprintf(p, "LOC: "); for (j = 0; j < smp_num_cpus; j++) p += sprintf(p, "%10u ", apic_timer_irqs[cpu_logical_map(j)]); p += sprintf(p, "\n"); #endif - p += sprintf(p, "ERR: %10lu\n", irq_err_count); + p += sprintf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); +#ifdef CONFIG_X86_IO_APIC +#ifdef APIC_MISMATCH_DEBUG + p += sprintf(p, "MIS: %10u\n", atomic_read(&irq_mis_count)); +#endif +#endif return p - buf; } @@ -460,14 +471,15 @@ * disable_irq_nosync - disable an irq without waiting * @irq: Interrupt to disable * - * Disable the selected interrupt line. Disables of an interrupt - * stack. Unlike disable_irq(), this function does not ensure existing + * Disable the selected interrupt line. Disables and Enables are + * nested. + * Unlike disable_irq(), this function does not ensure existing * instances of the IRQ handler have completed before returning. * * This function may be called from IRQ context. */ -void inline disable_irq_nosync(unsigned int irq) +inline void disable_irq_nosync(unsigned int irq) { irq_desc_t *desc = irq_desc + irq; unsigned long flags; @@ -484,9 +496,9 @@ * disable_irq - disable an irq and wait for completion * @irq: Interrupt to disable * - * Disable the selected interrupt line. Disables of an interrupt - * stack. That is for two disables you need two enables. This - * function waits for any pending IRQ handlers for this interrupt + * Disable the selected interrupt line. Enables and Disables are + * nested. + * This function waits for any pending IRQ handlers for this interrupt * to complete before returning. If you use this function while * holding a resource the IRQ handler may need you will deadlock. * @@ -505,11 +517,12 @@ } /** - * enable_irq - enable interrupt handling on an irq + * enable_irq - enable handling of an irq * @irq: Interrupt to enable * - * Re-enables the processing of interrupts on this IRQ line - * providing no disable_irq calls are now in effect. + * Undoes the effect of one call to disable_irq(). If this + * matches the last disable, processing of interrupts on this + * IRQ line is re-enabled. * * This function may be called from IRQ context. */ @@ -1016,20 +1029,9 @@ static struct proc_dir_entry * root_irq_dir; static struct proc_dir_entry * irq_dir [NR_IRQS]; -static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; - -static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; #define HEX_DIGITS 8 -static int irq_affinity_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - if (count < HEX_DIGITS+1) - return -EINVAL; - return sprintf (page, "%08lx\n", irq_affinity[(long)data]); -} - static unsigned int parse_hex_value (const char *buffer, unsigned long count, unsigned long *ret) { @@ -1067,6 +1069,19 @@ return 0; } +#if CONFIG_SMP + +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; +static int irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", irq_affinity[(long)data]); +} + static int irq_affinity_write_proc (struct file *file, const char *buffer, unsigned long count, void *data) { @@ -1078,7 +1093,6 @@ err = parse_hex_value(buffer, count, &new_value); -#if CONFIG_SMP /* * Do not allow disabling IRQs completely - it's a too easy * way to make the system unusable accidentally :-) At least @@ -1086,7 +1100,6 @@ */ if (!(new_value & cpu_online_map)) return -EINVAL; -#endif irq_affinity[irq] = new_value; irq_desc[irq].handler->set_affinity(irq, new_value); @@ -1094,6 +1107,8 @@ return full_count; } +#endif + static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -1121,7 +1136,6 @@ static void register_irq_proc (unsigned int irq) { - struct proc_dir_entry *entry; char name [MAX_NAMELEN]; if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || @@ -1134,15 +1148,23 @@ /* create /proc/irq/1234 */ irq_dir[irq] = proc_mkdir(name, root_irq_dir); - /* create /proc/irq/1234/smp_affinity */ - entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); +#if CONFIG_SMP + { + struct proc_dir_entry *entry; + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); - entry->nlink = 1; - entry->data = (void *)(long)irq; - entry->read_proc = irq_affinity_read_proc; - entry->write_proc = irq_affinity_write_proc; + if (entry) { + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + } - smp_affinity_entry[irq] = entry; + smp_affinity_entry[irq] = entry; + } +#endif } unsigned long prof_cpu_mask = -1; @@ -1157,6 +1179,9 @@ /* create /proc/irq/prof_cpu_mask */ entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + if (!entry) + return; entry->nlink = 1; entry->data = (void *)&prof_cpu_mask; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/microcode.c linux.ac/arch/i386/kernel/microcode.c --- linux.vanilla/arch/i386/kernel/microcode.c Fri Feb 9 19:29:44 2001 +++ linux.ac/arch/i386/kernel/microcode.c Tue Apr 3 17:54:30 2001 @@ -126,6 +126,7 @@ printk(KERN_ERR "microcode: failed to devfs_register()\n"); goto out; } + error = 0; printk(KERN_INFO "IA-32 Microcode Update Driver: v%s \n", MICROCODE_VERSION); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/mpparse.c linux.ac/arch/i386/kernel/mpparse.c --- linux.vanilla/arch/i386/kernel/mpparse.c Wed Nov 15 05:25:34 2000 +++ linux.ac/arch/i386/kernel/mpparse.c Tue Apr 3 17:54:30 2001 @@ -38,6 +38,18 @@ int mp_bus_id_to_type [MAX_MP_BUSSES]; int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { -1, }; int mp_current_pci_id; + +/* I/O APIC entries */ +struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS]; + +/* # of MP IRQ source entries */ +struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; + +/* MP IRQ source entries */ +int mp_irq_entries; + +int nr_ioapics; + int pic_mode; unsigned long mp_lapic_addr; @@ -225,6 +237,11 @@ MAX_IO_APICS, nr_ioapics); panic("Recompile kernel with bigger MAX_IO_APICS!.\n"); } + if (!m->mpc_apicaddr) { + printk(KERN_ERR "WARNING: bogus zero I/O APIC address" + " found in MP table, skipping!\n"); + return; + } mp_ioapics[nr_ioapics] = *m; nr_ioapics++; } @@ -361,10 +378,19 @@ return num_processors; } +static int __init ELCR_trigger(unsigned int irq) +{ + unsigned int port; + + port = 0x4d0 + (irq >> 3); + return (inb(port) >> (irq & 7)) & 1; +} + static void __init construct_default_ioirq_mptable(int mpc_default_type) { struct mpc_config_intsrc intsrc; int i; + int ELCR_fallback = 0; intsrc.mpc_type = MP_INTSRC; intsrc.mpc_irqflag = 0; /* conforming */ @@ -372,6 +398,26 @@ intsrc.mpc_dstapic = mp_ioapics[0].mpc_apicid; intsrc.mpc_irqtype = mp_INT; + + /* + * If true, we have an ISA/PCI system with no IRQ entries + * in the MP table. To prevent the PCI interrupts from being set up + * incorrectly, we try to use the ELCR. The sanity check to see if + * there is good ELCR data is very simple - IRQ0, 1, 2 and 13 can + * never be level sensitive, so we simply see if the ELCR agrees. + * If it does, we assume it's valid. + */ + if (mpc_default_type == 5) { + printk("ISA/PCI bus type with no IRQ information... falling back to ELCR\n"); + + if (ELCR_trigger(0) || ELCR_trigger(1) || ELCR_trigger(2) || ELCR_trigger(13)) + printk("ELCR contains invalid data... not using ELCR\n"); + else { + printk("Using ELCR to identify PCI interrupts\n"); + ELCR_fallback = 1; + } + } + for (i = 0; i < 16; i++) { switch (mpc_default_type) { case 2: @@ -383,6 +429,18 @@ continue; /* IRQ2 is never connected */ } + if (ELCR_fallback) { + /* + * If the ELCR indicates a level-sensitive interrupt, we + * copy that information over to the MP table in the + * irqflag field (level sensitive, active high polarity). + */ + if (ELCR_trigger(i)) + intsrc.mpc_irqflag = 13; + else + intsrc.mpc_irqflag = 0; + } + intsrc.mpc_srcbusirq = i; intsrc.mpc_dstirq = i ? i : 2; /* IRQ0 to INTIN2 */ MP_intsrc_info(&intsrc); @@ -633,7 +691,7 @@ */ void __init find_smp_config (void) { -#ifdef CONFIG_X86_IO_APIC +#ifdef CONFIG_X86_LOCAL_APIC find_intel_smp(); #endif #ifdef CONFIG_VISWS diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/nmi.c linux.ac/arch/i386/kernel/nmi.c --- linux.vanilla/arch/i386/kernel/nmi.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/i386/kernel/nmi.c Tue Apr 3 17:54:30 2001 @@ -0,0 +1,299 @@ +/* + * linux/arch/i386/nmi.c + * + * NMI watchdog support on APIC systems + * + * Started by Ingo Molnar + * + * Fixes: + * Mikael Pettersson : AMD K7 support for local APIC NMI watchdog. + * Mikael Pettersson : Power Management for local APIC NMI watchdog. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +unsigned int nmi_watchdog = NMI_NONE; +static unsigned int nmi_hz = HZ; +unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ + +#define K7_EVNTSEL_ENABLE (1 << 22) +#define K7_EVNTSEL_INT (1 << 20) +#define K7_EVNTSEL_OS (1 << 17) +#define K7_EVNTSEL_USR (1 << 16) +#define K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING 0x76 +#define K7_NMI_EVENT K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING + +#define P6_EVNTSEL0_ENABLE (1 << 22) +#define P6_EVNTSEL_INT (1 << 20) +#define P6_EVNTSEL_OS (1 << 17) +#define P6_EVNTSEL_USR (1 << 16) +#define P6_EVENT_CPU_CLOCKS_NOT_HALTED 0x79 +#define P6_NMI_EVENT P6_EVENT_CPU_CLOCKS_NOT_HALTED + +int __init check_nmi_watchdog (void) +{ + irq_cpustat_t tmp[NR_CPUS]; + int j, cpu; + + printk(KERN_INFO "testing NMI watchdog ... "); + + memcpy(tmp, irq_stat, sizeof(tmp)); + sti(); + mdelay((10*1000)/nmi_hz); // wait 10 ticks + + for (j = 0; j < smp_num_cpus; j++) { + cpu = cpu_logical_map(j); + if (nmi_count(cpu) - tmp[cpu].__nmi_count <= 5) { + printk("CPU#%d: NMI appears to be stuck!\n", cpu); + return -1; + } + } + printk("OK.\n"); + + /* now that we know it works we can reduce NMI frequency to + something more reasonable; makes a difference in some configs */ + if (nmi_watchdog == NMI_LOCAL_APIC) + nmi_hz = 1; + + return 0; +} + +static int __init setup_nmi_watchdog(char *str) +{ + int nmi; + + get_option(&str, &nmi); + + if (nmi >= NMI_INVALID) + return 0; + if (nmi == NMI_NONE) + nmi_watchdog = nmi; + /* + * If any other x86 CPU has a local APIC, then + * please test the NMI stuff there and send me the + * missing bits. Right now Intel P6 and AMD K7 only. + */ + if ((nmi == NMI_LOCAL_APIC) && + (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && + (boot_cpu_data.x86 == 6)) + nmi_watchdog = nmi; + if ((nmi == NMI_LOCAL_APIC) && + (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && + (boot_cpu_data.x86 == 6)) + nmi_watchdog = nmi; + /* + * We can enable the IO-APIC watchdog + * unconditionally. + */ + if (nmi == NMI_IO_APIC) + nmi_watchdog = nmi; + return 1; +} + +__setup("nmi_watchdog=", setup_nmi_watchdog); + +#ifdef CONFIG_PM + +#include + +struct pm_dev *nmi_pmdev; + +static void disable_apic_nmi_watchdog(void) +{ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + wrmsr(MSR_K7_EVNTSEL0, 0, 0); + break; + case X86_VENDOR_INTEL: + wrmsr(MSR_IA32_EVNTSEL0, 0, 0); + break; + } +} + +static int nmi_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + switch (rqst) { + case PM_SUSPEND: + disable_apic_nmi_watchdog(); + break; + case PM_RESUME: + setup_apic_nmi_watchdog(); + break; + } + return 0; +} + +static void nmi_pm_init(void) +{ + if (!nmi_pmdev) + nmi_pmdev = apic_pm_register(PM_SYS_DEV, 0, nmi_pm_callback); +} + +#define __pminit /*empty*/ + +#else /* CONFIG_PM */ + +static inline void nmi_pm_init(void) { } + +#define __pminit __init + +#endif /* CONFIG_PM */ + +/* + * Activate the NMI watchdog via the local APIC. + * Original code written by Keith Owens. + */ + +static void __pminit setup_k7_watchdog(void) +{ + int i; + unsigned int evntsel; + + nmi_perfctr_msr = MSR_K7_PERFCTR0; + + for(i = 0; i < 4; ++i) { + wrmsr(MSR_K7_EVNTSEL0+i, 0, 0); + wrmsr(MSR_K7_PERFCTR0+i, 0, 0); + } + + evntsel = K7_EVNTSEL_INT + | K7_EVNTSEL_OS + | K7_EVNTSEL_USR + | K7_NMI_EVENT; + + wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); + Dprintk("setting K7_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_K7_PERFCTR0, -(cpu_khz/nmi_hz*1000), -1); + apic_write(APIC_LVTPC, APIC_DM_NMI); + evntsel |= K7_EVNTSEL_ENABLE; + wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); +} + +static void __pminit setup_p6_watchdog(void) +{ + int i; + unsigned int evntsel; + + nmi_perfctr_msr = MSR_IA32_PERFCTR0; + + for(i = 0; i < 2; ++i) { + wrmsr(MSR_IA32_EVNTSEL0+i, 0, 0); + wrmsr(MSR_IA32_PERFCTR0+i, 0, 0); + } + + evntsel = P6_EVNTSEL_INT + | P6_EVNTSEL_OS + | P6_EVNTSEL_USR + | P6_NMI_EVENT; + + wrmsr(MSR_IA32_EVNTSEL0, evntsel, 0); + Dprintk("setting IA32_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_IA32_PERFCTR0, -(cpu_khz/nmi_hz*1000), 0); + apic_write(APIC_LVTPC, APIC_DM_NMI); + evntsel |= P6_EVNTSEL0_ENABLE; + wrmsr(MSR_IA32_EVNTSEL0, evntsel, 0); +} + +void __pminit setup_apic_nmi_watchdog (void) +{ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 != 6) + return; + setup_k7_watchdog(); + break; + case X86_VENDOR_INTEL: + if (boot_cpu_data.x86 != 6) + return; + setup_p6_watchdog(); + break; + default: + return; + } + nmi_pm_init(); +} + +static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED; + +/* + * the best way to detect whether a CPU has a 'hard lockup' problem + * is to check it's local APIC timer IRQ counts. If they are not + * changing then that CPU has some problem. + * + * as these watchdog NMI IRQs are generated on every CPU, we only + * have to check the current processor. + * + * since NMIs dont listen to _any_ locks, we have to be extremely + * careful not to rely on unsafe variables. The printk might lock + * up though, so we have to break up any console locks first ... + * [when there will be more tty-related locks, break them up + * here too!] + */ + +static unsigned int + last_irq_sums [NR_CPUS], + alert_counter [NR_CPUS]; + +void touch_nmi_watchdog (void) +{ + int i; + + /* + * Just reset the alert counters, (other CPUs might be + * spinning on locks we hold): + */ + for (i = 0; i < smp_num_cpus; i++) + alert_counter[i] = 0; +} + +void nmi_watchdog_tick (struct pt_regs * regs) +{ + + /* + * Since current-> is always on the stack, and we always switch + * the stack NMI-atomically, it's safe to use smp_processor_id(). + */ + int sum, cpu = smp_processor_id(); + + sum = apic_timer_irqs[cpu]; + + if (last_irq_sums[cpu] == sum) { + /* + * Ayiee, looks like this CPU is stuck ... + * wait a few IRQs (5 seconds) before doing the oops ... + */ + alert_counter[cpu]++; + if (alert_counter[cpu] == 5*nmi_hz) { + spin_lock(&nmi_print_lock); + /* + * We are in trouble anyway, lets at least try + * to get a message out. + */ + bust_spinlocks(1); + printk("NMI Watchdog detected LOCKUP on CPU%d, registers:\n", cpu); + show_registers(regs); + printk("console shuts up ...\n"); + console_silent(); + spin_unlock(&nmi_print_lock); + bust_spinlocks(0); + do_exit(SIGSEGV); + } + } else { + last_irq_sums[cpu] = sum; + alert_counter[cpu] = 0; + } + if (nmi_perfctr_msr) + wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/pci-irq.c linux.ac/arch/i386/kernel/pci-irq.c --- linux.vanilla/arch/i386/kernel/pci-irq.c Sat May 26 16:52:54 2001 +++ linux.ac/arch/i386/kernel/pci-irq.c Thu May 17 15:57:48 2001 @@ -15,6 +15,7 @@ #include #include +#include #include #include "pci-i386.h" @@ -222,15 +223,18 @@ /* * Cyrix: nibble offset 0x5C + * 0x5C bits 7:4 is INTB bits 3:0 is INTA + * 0x5D bits 7:4 is INTD bits 3:0 is INTC */ + static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq) { - return read_config_nybble(router, 0x5C, pirq-1); + return read_config_nybble(router, 0x5C, pirq^1); } static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) { - write_config_nybble(router, 0x5C, pirq-1, irq); + write_config_nybble(router, 0x5C, pirq^1, irq); return 1; } @@ -411,7 +415,7 @@ { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, pirq_piix_get, pirq_piix_set }, { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, pirq_piix_get, pirq_piix_set }, { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_0, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82820FW_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, pirq_piix_get, pirq_piix_set }, { "ALI", PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pirq_ali_get, pirq_ali_set }, diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/pci-pc.c linux.ac/arch/i386/kernel/pci-pc.c --- linux.vanilla/arch/i386/kernel/pci-pc.c Mon Apr 30 15:13:09 2001 +++ linux.ac/arch/i386/kernel/pci-pc.c Wed May 2 16:29:42 2001 @@ -997,10 +997,6 @@ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_LE, pci_fixup_serverworks }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CMIC_HE, pci_fixup_serverworks }, #endif -#if 0 -/* Our bus code shouldnt need this fixup any more. Delete once verified */ - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_6010, pci_fixup_compaq }, -#endif { PCI_FIXUP_HEADER, PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_fixup_ide_trash }, { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases }, @@ -1008,8 +1004,11 @@ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5598, pci_fixup_latency }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, pci_fixup_via_acpi }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, pci_fixup_via_acpi }, +#if 0 +/* This seems completely bogus so I am removing it -- Alan */ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C691, pci_fixup_via691 }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C598_1, pci_fixup_via691_2 }, +#endif { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, pci_fixup_piix4_acpi }, { 0 } }; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/process.c linux.ac/arch/i386/kernel/process.c --- linux.vanilla/arch/i386/kernel/process.c Fri Feb 9 19:29:44 2001 +++ linux.ac/arch/i386/kernel/process.c Tue Apr 24 18:28:36 2001 @@ -152,7 +152,7 @@ static long no_idt[2]; static int reboot_mode; -static int reboot_thru_bios; +int reboot_thru_bios; static int __init reboot_setup(char *str) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/ptrace.c linux.ac/arch/i386/kernel/ptrace.c --- linux.vanilla/arch/i386/kernel/ptrace.c Tue Apr 3 17:31:53 2001 +++ linux.ac/arch/i386/kernel/ptrace.c Fri May 4 16:44:32 2001 @@ -229,7 +229,7 @@ break; tmp = 0; /* Default return condition */ - if(addr < 17*sizeof(long)) + if(addr < FRAME_SIZE*sizeof(long)) tmp = getreg(child, addr); if(addr >= (long) &dummy->u_debugreg[0] && addr <= (long) &dummy->u_debugreg[7]){ @@ -256,7 +256,7 @@ addr > sizeof(struct user) - 3) break; - if (addr < 17*sizeof(long)) { + if (addr < FRAME_SIZE*sizeof(long)) { ret = putreg(child, addr, data); break; } @@ -369,11 +369,11 @@ } case PTRACE_GETREGS: { /* Get all gp regs from the child. */ - if (!access_ok(VERIFY_WRITE, (unsigned *)data, 17*sizeof(long))) { + if (!access_ok(VERIFY_WRITE, (unsigned *)data, FRAME_SIZE*sizeof(long))) { ret = -EIO; break; } - for ( i = 0; i < 17*sizeof(long); i += sizeof(long) ) { + for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) { __put_user(getreg(child, i),(unsigned long *) data); data += sizeof(long); } @@ -383,11 +383,11 @@ case PTRACE_SETREGS: { /* Set all gp regs in the child. */ unsigned long tmp; - if (!access_ok(VERIFY_READ, (unsigned *)data, 17*sizeof(long))) { + if (!access_ok(VERIFY_READ, (unsigned *)data, FRAME_SIZE*sizeof(long))) { ret = -EIO; break; } - for ( i = 0; i < 17*sizeof(long); i += sizeof(long) ) { + for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) { __get_user(tmp, (unsigned long *) data); putreg(child, i, tmp); data += sizeof(long); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/setup.c linux.ac/arch/i386/kernel/setup.c --- linux.vanilla/arch/i386/kernel/setup.c Sat May 26 16:52:54 2001 +++ linux.ac/arch/i386/kernel/setup.c Sat May 26 17:01:29 2001 @@ -64,6 +64,9 @@ * * VIA C3 Support. * Dave Jones , March 2001 + * + * AMD Athlon/Duron/Thunderbird bluesmoke support. + * Dave Jones , April 2001. */ /* @@ -93,6 +96,7 @@ #include #include #include +#include #include #include #include @@ -145,9 +149,9 @@ unsigned char aux_device_present; +extern void mcheck_init(struct cpuinfo_x86 *c); extern int root_mountflags; extern char _text, _etext, _edata, _end; -extern unsigned long cpu_khz; static int disable_x86_serial_nr __initdata = 1; static int disable_x86_fxsr __initdata = 0; @@ -937,27 +941,14 @@ * trampoline before removing it. (see the GDT stuff) */ reserve_bootmem(PAGE_SIZE, PAGE_SIZE); - smp_alloc_memory(); /* AP processor realmode stacks in low memory*/ #endif -#ifdef CONFIG_X86_IO_APIC +#ifdef CONFIG_X86_LOCAL_APIC /* * Find and reserve possible boot-time SMP configuration: */ find_smp_config(); #endif - paging_init(); -#ifdef CONFIG_X86_IO_APIC - /* - * get boot-time SMP configuration: - */ - if (smp_found_config) - get_smp_config(); -#endif -#ifdef CONFIG_X86_LOCAL_APIC - init_apic_mappings(); -#endif - #ifdef CONFIG_BLK_DEV_INITRD if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { @@ -977,6 +968,25 @@ #endif /* + * NOTE: before this point _nobody_ is allowed to allocate + * any memory using the bootmem allocator. + */ + +#ifdef CONFIG_SMP + smp_alloc_memory(); /* AP processor realmode stacks in low memory*/ +#endif + paging_init(); +#ifdef CONFIG_X86_LOCAL_APIC + /* + * get boot-time SMP configuration: + */ + if (smp_found_config) + get_smp_config(); + init_apic_mappings(); +#endif + + + /* * Request address space for all standard RAM and ROM resources * and also for regions reported as reserved by the e820. */ @@ -1038,6 +1048,32 @@ __setup("notsc", tsc_setup); #endif + +static void __init interpret_eblcr(struct cpuinfo_x86 *c, u32 lo, int coding) +{ + static unsigned int __initdata mult[32] = { 10, 6, 8, 0, 11, 7, 9, 0, + 10, 14, 16, 12, 0, 15, 0, 13 , + 0, 0, 0, 0, 0, 0, 17, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; + int mul = (lo>>22)&15; + + /* The mobile pIII added bit 27. This is zero on other intel and + on the cyrix III */ + + if(lo&(1>>27)) + mul+=16; + + c->clockmul = mult[mul]; + + if (mult[mul]==0) + { + printk(KERN_INFO "Unknown CPU/BUS multiplier (%d, %x).\n", + mul, lo); + return; + } +} + + static int __init get_model_name(struct cpuinfo_x86 *c) { unsigned int *v; @@ -1229,10 +1265,10 @@ set_bit(X86_FEATURE_K6_MTRR, &c->x86_capability); break; } - break; case 6: /* An Athlon/Duron. We can trust the BIOS probably */ + mcheck_init(c); break; } @@ -1472,6 +1508,248 @@ return; } +#ifdef CONFIG_X86_OOSTORE + +static u32 __init power2(u32 x) +{ + u32 s=1; + while(s<=x) + s<<=1; + return s>>=1; +} + +/* + * Set up an actual MCR + */ + +static void __init winchip_mcr_insert(int reg, u32 base, u32 size, int key) +{ + u32 lo, hi; + + hi = base & ~0xFFF; + lo = ~(size-1); /* Size is a power of 2 so this makes a mask */ + lo &= ~0xFFF; /* Remove the ctrl value bits */ + lo |= key; /* Attribute we wish to set */ + wrmsr(reg+0x110, lo, hi); + mtrr_centaur_report_mcr(reg, lo, hi); /* Tell the mtrr driver */ +} + +/* + * Figure what we can cover with MCR's + * + * Shortcut: We know you can't put 4Gig of RAM on a winchip + */ + +static u32 __init ramtop(void) /* 16388 */ +{ + int i; + u32 top = 0; + u32 clip = 0xFFFFFFFFUL; + + for (i = 0; i < e820.nr_map; i++) { + unsigned long start, end; + + if (e820.map[i].addr > 0xFFFFFFFFUL) + continue; + /* + * Don't MCR over reserved space. Ignore the ISA hole + * we frob around that catastrophy already + */ + + if (e820.map[i].type == E820_RESERVED) + { + if(e820.map[i].addr >= 0x100000UL && e820.map[i].addr < clip) + clip = e820.map[i].addr; + continue; + } + start = e820.map[i].addr; + end = e820.map[i].addr + e820.map[i].size; + if (start >= end) + continue; + if (end > top) + top = end; + } + /* Everything below 'top' should be RAM except for the ISA hole. + Because of the limited MCR's we want to map NV/ACPI into our + MCR range for gunk in RAM + + Clip might cause us to MCR insufficient RAM but that is an + acceptable failure mode and should only bite obscure boxes with + a VESA hole at 15Mb + + The second case Clip sometimes kicks in is when the EBDA is marked + as reserved. Again we fail safe with reasonable results + */ + + if(top>clip) + top=clip; + + return top; +} + +/* + * Compute a set of MCR's to give maximum coverage + */ + +static int __init winchip_mcr_compute(int nr, int key) +{ + u32 mem = ramtop(); + u32 root = power2(mem); + u32 base = root; + u32 top = root; + u32 floor = 0; + int ct = 0; + + while(ct high && fspace > low) + { + winchip_mcr_insert(ct, floor, fspace, key); + floor += fspace; + } + else if(high > low) + { + winchip_mcr_insert(ct, top, high, key); + top += high; + } + else if(low > 0) + { + base -= low; + winchip_mcr_insert(ct, base, low, key); + } + else break; + ct++; + } + /* + * We loaded ct values. We now need to set the mask. The caller + * must do this bit. + */ + + return ct; +} + +static void __init winchip_create_optimal_mcr(void) +{ + int i; + /* + * Allocate up to 6 mcrs to mark as much of ram as possible + * as write combining and weak write ordered. + * + * To experiment with: Linux never uses stack operations for + * mmio spaces so we could globally enable stack operation wc + * + * Load the registers with type 31 - full write combining, all + * writes weakly ordered. + */ + int used = winchip_mcr_compute(6, 31); + + /* + * Wipe unused MCRs + */ + + for(i=used;i<8;i++) + wrmsr(0x110+i, 0, 0); +} + +static void __init winchip2_create_optimal_mcr(void) +{ + u32 lo, hi; + int i; + + /* + * Allocate up to 6 mcrs to mark as much of ram as possible + * as write combining, weak store ordered. + * + * Load the registers with type 25 + * 8 - weak write ordering + * 16 - weak read ordering + * 1 - write combining + */ + + int used = winchip_mcr_compute(6, 25); + + /* + * Mark the registers we are using. + */ + + rdmsr(0x120, lo, hi); + for(i=0;i>17) & 7; + lo |= key<<6; /* replace with unlock key */ + wrmsr(0x120, lo, hi); +} + +static void __init winchip2_protect_mcr(void) +{ + u32 lo, hi; + + rdmsr(0x120, lo, hi); + lo&=~0x1C0; /* blank bits 8-6 */ + wrmsr(0x120, lo, hi); +} + +#endif + static void __init init_centaur(struct cpuinfo_x86 *c) { enum { @@ -1515,6 +1793,19 @@ fcr_clr=DPDC; printk(KERN_NOTICE "Disabling bugged TSC.\n"); clear_bit(X86_FEATURE_TSC, &c->x86_capability); +#ifdef CONFIG_X86_OOSTORE + winchip_create_optimal_mcr(); + /* Enable + write combining on non-stack, non-string + write combining on string, all types + weak write ordering + + The C6 original lacks weak read order + + Note 0x120 is write only on Winchip 1 */ + + wrmsr(0x120, 0x01F0001F, 0); +#endif break; case 8: switch(c->x86_mask) { @@ -1530,11 +1821,37 @@ } fcr_set=ECX8|DSMC|DTLOCK|EMMX|EBRPRED|ERETSTK|E2MMX|EAMD3D; fcr_clr=DPDC; +#ifdef CONFIG_X86_OOSTORE + winchip2_unprotect_mcr(); + winchip2_create_optimal_mcr(); + rdmsr(0x120, lo, hi); + /* Enable + write combining on non-stack, non-string + write combining on string, all types + weak write ordering + */ + lo|=31; + wrmsr(0x120, lo, hi); + winchip2_protect_mcr(); +#endif break; case 9: name="3"; fcr_set=ECX8|DSMC|DTLOCK|EMMX|EBRPRED|ERETSTK|E2MMX|EAMD3D; fcr_clr=DPDC; +#ifdef CONFIG_X86_OOSTORE + winchip2_unprotect_mcr(); + winchip2_create_optimal_mcr(); + rdmsr(0x120, lo, hi); + /* Enable + write combining on non-stack, non-string + write combining on string, all types + weak write ordering + */ + lo|=31; + wrmsr(0x120, lo, hi); + winchip2_protect_mcr(); +#endif break; case 10: name="4"; @@ -1570,6 +1887,7 @@ c->x86_cache_size = (cc>>24)+(dd>>24); } sprintf( c->x86_model_id, "WinChip %s", name ); + mcheck_init(c); break; case 6: @@ -1584,11 +1902,13 @@ get_model_name(c); display_cacheinfo(c); + + rdmsr(0x2A, lo, hi); + interpret_eblcr(c, lo, 0); break; } break; } - } @@ -1687,7 +2007,6 @@ #ifndef CONFIG_M686 static int f00f_workaround_enabled = 0; #endif - extern void mcheck_init(struct cpuinfo_x86 *c); char *p = NULL; unsigned int l1i = 0, l1d = 0, l2 = 0, l3 = 0; /* Cache sizes */ @@ -1829,6 +2148,13 @@ /* SEP CPUID bug: Pentium Pro reports SEP but doesn't have it */ if ( c->x86 == 6 && c->x86_model < 3 && c->x86_mask < 3 ) clear_bit(X86_FEATURE_SEP, &c->x86_capability); + + if ( c->x86 == 6 && c->x86_model >= 3) + { + u32 lo, hi; + rdmsr(0x2A, lo, hi); + interpret_eblcr(c, lo, (c->x86_model >= 7) ? 1: 0); + } /* Names for the Pentium II/Celeron processors detectable only by also checking the cache size. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/signal.c linux.ac/arch/i386/kernel/signal.c --- linux.vanilla/arch/i386/kernel/signal.c Tue Feb 13 21:15:04 2001 +++ linux.ac/arch/i386/kernel/signal.c Tue May 1 14:25:29 2001 @@ -370,7 +370,7 @@ /* This is the X/Open sanctioned signal stack switching. */ if (ka->sa.sa_flags & SA_ONSTACK) { - if (! on_sig_stack(esp)) + if (sas_ss_flags(esp) == 0) esp = current->sas_ss_sp + current->sas_ss_size; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/smp.c linux.ac/arch/i386/kernel/smp.c --- linux.vanilla/arch/i386/kernel/smp.c Tue Feb 13 22:13:43 2001 +++ linux.ac/arch/i386/kernel/smp.c Tue Apr 3 17:54:30 2001 @@ -429,9 +429,10 @@ atomic_t started; atomic_t finished; int wait; -}; +} __attribute__ ((__aligned__(SMP_CACHE_BYTES))); static struct call_data_struct * call_data; +static struct call_data_struct call_data_array[NR_CPUS]; /* * this function sends a 'generic call function' IPI to all other CPUs @@ -453,32 +454,45 @@ * hardware interrupt handler, you may call it from a bottom half handler. */ { - struct call_data_struct data; - int cpus = smp_num_cpus-1; + struct call_data_struct *data; + int cpus = (cpu_online_map & ~(1 << smp_processor_id())); if (!cpus) return 0; - data.func = func; - data.info = info; - atomic_set(&data.started, 0); - data.wait = wait; + data = &call_data_array[smp_processor_id()]; + + data->func = func; + data->info = info; + data->wait = wait; if (wait) - atomic_set(&data.finished, 0); - - spin_lock_bh(&call_lock); - call_data = &data; + atomic_set(&data->finished, 0); + /* We have do to this one last to make sure that the IPI service + * code desn't get confused if it gets an unexpected repeat + * trigger of an old IPI while we're still setting up the new + * one. */ + atomic_set(&data->started, 0); + + local_bh_disable(); + spin_lock(&call_lock); + call_data = data; /* Send a message to all other CPUs and wait for them to respond */ send_IPI_allbutself(CALL_FUNCTION_VECTOR); /* Wait for response */ - while (atomic_read(&data.started) != cpus) + while (atomic_read(&data->started) != cpus) barrier(); + /* It is now safe to reuse the "call_data" global, but we need + * to keep local bottom-halves disabled until after waiters have + * been acknowledged to prevent reuse of the per-cpu call data + * entry. */ + spin_unlock(&call_lock); + if (wait) - while (atomic_read(&data.finished) != cpus) + while (atomic_read(&data->finished) != cpus) barrier(); - spin_unlock_bh(&call_lock); + local_bh_enable(); return 0; } @@ -528,15 +542,17 @@ ack_APIC_irq(); /* - * Notify initiating CPU that I've grabbed the data and am - * about to execute the function + * Notify initiating CPU that I've grabbed the data and am about + * to execute the function (and avoid servicing any single IPI + * twice) */ - atomic_inc(&call_data->started); + if (test_and_set_bit(smp_processor_id(), &call_data->started)) + return; /* * At this point the info structure may be out of scope unless wait==1 */ (*func)(info); if (wait) - atomic_inc(&call_data->finished); + set_bit(smp_processor_id(), &call_data->finished); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/smpboot.c linux.ac/arch/i386/kernel/smpboot.c --- linux.vanilla/arch/i386/kernel/smpboot.c Tue Feb 13 22:13:43 2001 +++ linux.ac/arch/i386/kernel/smpboot.c Tue Apr 3 17:54:30 2001 @@ -780,7 +780,6 @@ } cycles_t cacheflush_time; -extern unsigned long cpu_khz; static void smp_tune_scheduling (void) { @@ -870,12 +869,15 @@ * get out of here now! */ if (!smp_found_config) { - printk(KERN_NOTICE "SMP motherboard not detected. Using dummy APIC emulation.\n"); + printk(KERN_NOTICE "SMP motherboard not detected.\n"); #ifndef CONFIG_VISWS io_apic_irqs = 0; #endif cpu_online_map = phys_cpu_present_map = 1; smp_num_cpus = 1; + if (APIC_init_uniprocessor()) + printk(KERN_NOTICE "Local APIC not detected." + " Using dummy APIC emulation.\n"); goto smp_done; } @@ -1003,7 +1005,7 @@ * Here we can be sure that there is an IO-APIC in the system. Let's * go and set it up: */ - if (!skip_ioapic_setup) + if (!skip_ioapic_setup && nr_ioapics) setup_IO_APIC(); #endif @@ -1021,4 +1023,3 @@ smp_done: zap_low_mappings(); } - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/time.c linux.ac/arch/i386/kernel/time.c --- linux.vanilla/arch/i386/kernel/time.c Fri Dec 29 22:07:57 2000 +++ linux.ac/arch/i386/kernel/time.c Tue May 1 08:07:48 2001 @@ -178,6 +178,15 @@ jiffies_t = jiffies; count |= inb_p(0x40) << 8; + + /* VIA686a test code... reset the latch if count > max + 1 */ + if (count > LATCH) { + outb_p(0x34, 0x43); + outb_p(LATCH & 0xff, 0x40); + outb(LATCH >> 8, 0x40); + count = LATCH - 1; + } + spin_unlock(&i8253_lock); /* @@ -413,7 +422,7 @@ if (!user_mode(regs)) x86_do_profile(regs->eip); #else - if (!smp_found_config) + if (!using_apic_timer) smp_local_timer_interrupt(regs); #endif @@ -492,6 +501,24 @@ count = inb_p(0x40); /* read the latched count */ count |= inb(0x40) << 8; + + + /* VIA686a test code... reset the latch if count > max */ + if (count > LATCH-1) { + static int last_whine; + outb_p(0x34, 0x43); + outb_p(LATCH & 0xff, 0x40); + outb(LATCH >> 8, 0x40); + count = LATCH - 1; + if(time_after(jiffies, last_whine)) + { + printk(KERN_WARNING "probable hardware bug: clock timer configuration lost - probably a VIA686a motherboard.\n"); + printk(KERN_WARNING "probable hardware bug: restoring chip configuration.\n"); + last_whine = jiffies + HZ; + } + } + + spin_unlock(&i8253_lock); count = ((LATCH-1) - count) * TICK_SIZE; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/kernel/traps.c linux.ac/arch/i386/kernel/traps.c --- linux.vanilla/arch/i386/kernel/traps.c Sat May 26 16:52:54 2001 +++ linux.ac/arch/i386/kernel/traps.c Thu May 17 14:00:42 2001 @@ -63,8 +63,6 @@ */ struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, }; -extern void bust_spinlocks(void); - asmlinkage void divide_error(void); asmlinkage void debug(void); asmlinkage void nmi(void); @@ -118,8 +116,8 @@ if (((addr >= (unsigned long) &_stext) && (addr <= (unsigned long) &_etext)) || ((addr >= module_start) && (addr <= module_end))) { - if (i && ((i % 8) == 0)) - printk("\n "); + if (i && ((i % 6) == 0)) + printk("\n "); printk("[<%08lx>] ", addr); i++; } @@ -129,7 +127,7 @@ void show_trace_task(struct task_struct *tsk) { - show_trace(&tsk->thread.esp); + show_trace((unsigned long *)(tsk->thread.esp)); } void show_stack(unsigned long * esp) @@ -155,7 +153,7 @@ show_trace(esp); } -static void show_registers(struct pt_regs *regs) +void show_registers(struct pt_regs *regs) { int i; int in_kernel = 1; @@ -212,9 +210,10 @@ { console_verbose(); spin_lock_irq(&die_lock); + bust_spinlocks(1); printk("%s: %04lx\n", str, err & 0xffff); show_registers(regs); - + bust_spinlocks(0); spin_unlock_irq(&die_lock); do_exit(SIGSEGV); } @@ -234,7 +233,7 @@ return address; } -static void inline do_trap(int trapnr, int signr, char *str, int vm86, +static inline void do_trap(int trapnr, int signr, char *str, int vm86, struct pt_regs * regs, long error_code, siginfo_t *info) { if (vm86 && regs->eflags & VM_MASK) @@ -386,83 +385,14 @@ printk("Do you have a strange power saving mode enabled?\n"); } -#if CONFIG_X86_IO_APIC - -int nmi_watchdog = 0; - -static int __init setup_nmi_watchdog(char *str) -{ - get_option(&str, &nmi_watchdog); - return 1; -} - -__setup("nmi_watchdog=", setup_nmi_watchdog); - -static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED; - -inline void nmi_watchdog_tick(struct pt_regs * regs) -{ - /* - * the best way to detect wether a CPU has a 'hard lockup' problem - * is to check it's local APIC timer IRQ counts. If they are not - * changing then that CPU has some problem. - * - * as these watchdog NMI IRQs are broadcasted to every CPU, here - * we only have to check the current processor. - * - * since NMIs dont listen to _any_ locks, we have to be extremely - * careful not to rely on unsafe variables. The printk might lock - * up though, so we have to break up console_lock first ... - * [when there will be more tty-related locks, break them up - * here too!] - */ - - static unsigned int last_irq_sums [NR_CPUS], - alert_counter [NR_CPUS]; - - /* - * Since current-> is always on the stack, and we always switch - * the stack NMI-atomically, it's safe to use smp_processor_id(). - */ - int sum, cpu = smp_processor_id(); - - sum = apic_timer_irqs[cpu]; - - if (last_irq_sums[cpu] == sum) { - /* - * Ayiee, looks like this CPU is stuck ... - * wait a few IRQs (5 seconds) before doing the oops ... - */ - alert_counter[cpu]++; - if (alert_counter[cpu] == 5*HZ) { - spin_lock(&nmi_print_lock); - /* - * We are in trouble anyway, lets at least try - * to get a message out. - */ - bust_spinlocks(); - printk("NMI Watchdog detected LOCKUP on CPU%d, registers:\n", cpu); - show_registers(regs); - printk("console shuts up ...\n"); - console_silent(); - spin_unlock(&nmi_print_lock); - do_exit(SIGSEGV); - } - } else { - last_irq_sums[cpu] = sum; - alert_counter[cpu] = 0; - } -} -#endif - asmlinkage void do_nmi(struct pt_regs * regs, long error_code) { unsigned char reason = inb(0x61); - ++nmi_count(smp_processor_id()); + if (!(reason & 0xc0)) { -#if CONFIG_X86_IO_APIC +#if CONFIG_X86_LOCAL_APIC /* * Ok, so this is none of the documented NMI sources, * so it must be the NMI watchdog. @@ -470,11 +400,9 @@ if (nmi_watchdog) { nmi_watchdog_tick(regs); return; - } else - unknown_nmi_error(reason, regs); -#else - unknown_nmi_error(reason, regs); + } #endif + unknown_nmi_error(reason, regs); return; } if (reason & 0x80) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/lib/Makefile linux.ac/arch/i386/lib/Makefile --- linux.vanilla/arch/i386/lib/Makefile Mon Apr 30 15:13:09 2001 +++ linux.ac/arch/i386/lib/Makefile Mon Apr 30 15:17:31 2001 @@ -13,5 +13,6 @@ obj-$(CONFIG_X86_USE_3DNOW) += mmx.o obj-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o +obj-$(CONFIG_DEBUG_IOVIRT) += iodebug.o include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/mm/fault.c linux.ac/arch/i386/mm/fault.c --- linux.vanilla/arch/i386/mm/fault.c Sat May 26 16:52:54 2001 +++ linux.ac/arch/i386/mm/fault.c Thu May 17 16:00:20 2001 @@ -4,6 +4,7 @@ * Copyright (C) 1995 Linus Torvalds */ +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include /* For unblank_screen() */ #include #include @@ -25,6 +27,8 @@ extern void die(const char *,struct pt_regs *,long); +extern int console_loglevel; + /* * Ugly, ugly, but the goto's result in better assembly.. */ @@ -77,17 +81,69 @@ return 0; } -extern spinlock_t console_lock, timerlist_lock; +extern spinlock_t timerlist_lock; /* * Unlock any spinlocks which will prevent us from getting the * message out (timerlist_lock is acquired through the * console unblank code) */ -void bust_spinlocks(void) +void bust_spinlocks(int yes) { - spin_lock_init(&console_lock); spin_lock_init(&timerlist_lock); + if (yes) { + oops_in_progress = 1; +#ifdef CONFIG_SMP + global_irq_lock = 0; /* Many serial drivers do __global_cli() */ +#endif + } else { + int loglevel_save = console_loglevel; +#ifdef CONFIG_VT + unblank_screen(); +#endif + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; + } +} + +void do_BUG(const char *file, int line) +{ + bust_spinlocks(1); + printk("kernel BUG at %s:%d!\n", file, line); +} + +static void print_pagetable_entries (pgd_t *pgdir, unsigned long address) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + pgd = pgdir + __pgd_offset(address); + printk("pgd entry %p: %016Lx\n", pgd, (long long)pgd_val(*pgd)); + + if (!pgd_present(*pgd)) { + printk("... pgd not present!\n"); + return; + } + pmd = pmd_offset(pgd, address); + printk("pmd entry %p: %016Lx\n", pmd, (long long)pmd_val(*pmd)); + + if (!pmd_present(*pmd)) { + printk("... pmd not present!\n"); + return; + } + pte = pte_offset(pmd, address); + printk("pte entry %p: %016Lx\n", pte, (long long)pte_val(*pte)); + + if (!pte_present(*pte)) + printk("... pte not present!\n"); } asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); @@ -271,7 +327,7 @@ * terminate things with extreme prejudice. */ - bust_spinlocks(); + bust_spinlocks(1); if (address < PAGE_SIZE) printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/i386/mm/init.c linux.ac/arch/i386/mm/init.c --- linux.vanilla/arch/i386/mm/init.c Mon Apr 30 15:13:09 2001 +++ linux.ac/arch/i386/mm/init.c Wed May 23 23:52:06 2001 @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -35,7 +37,9 @@ #include #include #include +#include +mmu_gather_t mmu_gathers[NR_CPUS]; unsigned long highstart_pfn, highend_pfn; static unsigned long totalram_pages; static unsigned long totalhigh_pages; @@ -131,15 +135,11 @@ pte_t *pte; pgd = swapper_pg_dir + __pgd_offset(vaddr); - if (pgd_none(*pgd)) { - printk("PAE BUG #00!\n"); - return; - } + if (pgd_none(*pgd)) + BUG(); pmd = pmd_offset(pgd, vaddr); - if (pmd_none(*pmd)) { - printk("PAE BUG #01!\n"); - return; - } + if (pmd_none(*pmd)) + BUG(); pte = pte_offset(pmd, vaddr); if (pte_val(*pte)) pte_ERROR(*pte); @@ -183,7 +183,7 @@ pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); set_pgd(pgd, __pgd(__pa(pmd) + 0x1)); if (pmd != pmd_offset(pgd, 0)) - printk("PAE BUG #02!\n"); + BUG(); } pmd = pmd_offset(pgd, vaddr); #else @@ -570,7 +570,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = totalhigh_pages; @@ -578,3 +578,21 @@ val->mem_unit = PAGE_SIZE; return; } + +#if CONFIG_X86_PAE + +struct kmem_cache_s *pae_pgd_cachep; + +void __init init_pae_pgd_cache(void) +{ + /* + * PAE pgds must be 16-byte aligned: + */ + pae_pgd_cachep = kmem_cache_create("pae_pgd", 32, 0, + SLAB_HWCACHE_ALIGN | SLAB_MUST_HWCACHE_ALIGN, NULL, NULL); + if (!pae_pgd_cachep) + panic("init_pae(): Cannot alloc pae_pgd SLAB cache"); +} + +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ia64/config.in linux.ac/arch/ia64/config.in --- linux.vanilla/arch/ia64/config.in Mon Apr 30 15:13:09 2001 +++ linux.ac/arch/ia64/config.in Wed Apr 18 13:47:54 2001 @@ -148,7 +148,7 @@ source drivers/mtd/Config.in source drivers/pnp/Config.in source drivers/block/Config.in -source drivers/i2o/Config.in +source drivers/message/i2o/Config.in source drivers/md/Config.in mainmenu_option next_comment diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ia64/ia32/sys_ia32.c linux.ac/arch/ia64/ia32/sys_ia32.c --- linux.vanilla/arch/ia64/ia32/sys_ia32.c Mon Apr 30 15:13:09 2001 +++ linux.ac/arch/ia64/ia32/sys_ia32.c Tue May 8 18:22:11 2001 @@ -2803,26 +2803,26 @@ return sys_ioperm((unsigned long)from, (unsigned long)num, on); } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; +struct mem_dqblk32 { __u32 dqb_ihardlimit; __u32 dqb_isoftlimit; __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u64 dqb_curspace; __kernel_time_t32 dqb_btime; __kernel_time_t32 dqb_itime; }; extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, - caddr_t addr); + _kernel_caddr_t addr); asmlinkage long sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) { int cmds = cmd >> SUBCMDSHIFT; int err; - struct dqblk d; + struct mem_dqblk d; mm_segment_t old_fs; char *spec; @@ -2832,33 +2832,35 @@ case Q_SETQUOTA: case Q_SETUSE: case Q_SETQLIM: - if (copy_from_user (&d, (struct dqblk32 *)addr, - sizeof (struct dqblk32))) + if (copy_from_user (&d, (struct mem_dqblk32 *)addr, + sizeof (struct mem_dqblk32))) return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime; break; default: return sys_quotactl(cmd, special, - id, (caddr_t)addr); + id, (__kernel_caddr_t)addr); } spec = getname32 (special); err = PTR_ERR(spec); if (IS_ERR(spec)) return err; old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); set_fs (old_fs); putname (spec); + if (err) + return err; if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user ((struct dqblk32 *)addr, &d, - sizeof (struct dqblk32))) + ((struct mem_dqblk32 *)&d)->dqb_itime = i; + ((struct mem_dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct mem_dqblk32 *)addr, &d, + sizeof (struct mem_dqblk32))) return -EFAULT; } - return err; + return 0; } extern asmlinkage long sys_utime(char * filename, struct utimbuf * times); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ia64/mm/init.c linux.ac/arch/ia64/mm/init.c --- linux.vanilla/arch/ia64/mm/init.c Mon Apr 30 15:13:10 2001 +++ linux.ac/arch/ia64/mm/init.c Wed May 23 23:52:06 2001 @@ -24,6 +24,9 @@ #include #include #include +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; /* References to section boundaries: */ extern char _stext, _etext, _edata, __init_begin, __init_end; @@ -151,7 +154,7 @@ si_meminfo (struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/m68k/mm/init.c linux.ac/arch/m68k/mm/init.c --- linux.vanilla/arch/m68k/mm/init.c Mon Oct 16 20:58:51 2000 +++ linux.ac/arch/m68k/mm/init.c Wed May 23 23:52:06 2001 @@ -31,12 +31,11 @@ #ifdef CONFIG_ATARI #include #endif +#include -static unsigned long totalram_pages; +mmu_gather_t mmu_gathers[NR_CPUS]; -#ifdef CONFIG_SUN3 -void mmu_emu_reserve_pages(unsigned long max_page); -#endif +unsigned long totalram_pages = 0; int do_check_pgt_cache(int low, int high) { @@ -86,7 +85,7 @@ void show_mem(void) { unsigned long i; - int free = 0, total = 0, reserved = 0, nonshared = 0, shared = 0; + int free = 0, total = 0, reserved = 0, shared = 0; int cached = 0; printk("\nMem-info:\n"); @@ -101,15 +100,12 @@ cached++; else if (!page_count(mem_map+i)) free++; - else if (page_count(mem_map+i) == 1) - nonshared++; else shared += page_count(mem_map+i) - 1; } printk("%d pages of RAM\n",total); printk("%d free pages\n",free); printk("%d reserved pages\n",reserved); - printk("%d pages nonshared\n",nonshared); printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); printk("%ld pages in page table cache\n",pgtable_cache_size); @@ -137,17 +133,11 @@ #ifdef CONFIG_ATARI if (MACH_IS_ATARI) - atari_stram_reserve_pages( start_mem ); -#endif - -#ifdef CONFIG_SUN3 - /* reserve rom pages */ - mmu_emu_reserve_pages(max_mapnr); + atari_stram_mem_init_hook(); #endif /* this will put all memory onto the freelists */ totalram_pages = free_all_bootmem(); - printk("tp:%ld\n", totalram_pages); for (tmp = PAGE_OFFSET ; tmp < (unsigned long)high_memory; tmp += PAGE_SIZE) { #if 0 @@ -201,13 +191,15 @@ #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { + int pages = 0; for (; start < end; start += PAGE_SIZE) { ClearPageReserved(virt_to_page(start)); set_page_count(virt_to_page(start), 1); free_page(start); totalram_pages++; + pages++; } - printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); + printk ("Freeing initrd memory: %dk freed\n", pages); } #endif @@ -217,18 +209,11 @@ i = max_mapnr; val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); - while (i-- > 0) { - if (PageReserved(mem_map+i)) - continue; - val->totalram++; - if (!page_count(mem_map+i)) - continue; - val->sharedram += page_count(mem_map+i) - 1; - } val->totalhigh = 0; val->freehigh = 0; + val->mem_unit = PAGE_SIZE; return; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips/config.in linux.ac/arch/mips/config.in --- linux.vanilla/arch/mips/config.in Mon Apr 30 15:13:11 2001 +++ linux.ac/arch/mips/config.in Tue Apr 24 17:58:04 2001 @@ -236,7 +236,7 @@ if [ "$CONFIG_DECSTATION" != "y" -a \ "$CONFIG_SGI_IP22" != "y" ]; then - source drivers/i2o/Config.in + source drivers/message/i2o/Config.in fi if [ "$CONFIG_NET" = "y" ]; then diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips/mm/init.c linux.ac/arch/mips/mm/init.c --- linux.vanilla/arch/mips/mm/init.c Mon Oct 16 20:58:51 2000 +++ linux.ac/arch/mips/mm/init.c Wed May 23 23:52:07 2001 @@ -38,6 +38,9 @@ #include #endif #include +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; static unsigned long totalram_pages; @@ -343,7 +346,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips64/config.in linux.ac/arch/mips64/config.in --- linux.vanilla/arch/mips64/config.in Mon Apr 30 15:13:11 2001 +++ linux.ac/arch/mips64/config.in Wed Apr 18 13:51:45 2001 @@ -166,7 +166,7 @@ fi endmenu -source drivers/i2o/Config.in +source drivers/message/i2o/Config.in if [ "$CONFIG_NET" = "y" ]; then mainmenu_option next_comment diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips64/mm/fault.c linux.ac/arch/mips64/mm/fault.c --- linux.vanilla/arch/mips64/mm/fault.c Tue Apr 3 17:31:55 2001 +++ linux.ac/arch/mips64/mm/fault.c Tue Apr 3 17:54:33 2001 @@ -6,6 +6,7 @@ * Copyright (C) 1995 - 2000 by Ralf Baechle * Copyright (C) 1999, 2000 by Silicon Graphics, Inc. */ +#include #include #include #include @@ -31,6 +32,7 @@ #define development_version (LINUX_VERSION_CODE & 0x100) extern void die(char *, struct pt_regs *, unsigned long write); +extern int console_loglevel; /* * Macro for exception fixup code to access integer registers. @@ -57,17 +59,34 @@ printk("Got exception 0x%lx at 0x%lx\n", retaddr, regs.cp0_epc); } -extern spinlock_t console_lock, timerlist_lock; +extern spinlock_t timerlist_lock; /* * Unlock any spinlocks which will prevent us from getting the - * message out (timerlist_lock is acquired through the + * message out (timerlist_lock is aquired through the * console unblank code) */ -void bust_spinlocks(void) +void bust_spinlocks(int yes) { - spin_lock_init(&console_lock); spin_lock_init(&timerlist_lock); + if (yes) { + oops_in_progress = 1; +#ifdef CONFIG_SMP + global_irq_lock = 0; /* Many serial drivers do __global_cli() */ +#endif + } else { + int loglevel_save = console_loglevel; + unblank_screen(); + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; + } } /* @@ -195,7 +214,7 @@ * terminate things with extreme prejudice. */ - bust_spinlocks(); + bust_spinlocks(1); printk(KERN_ALERT "Cpu %d Unable to handle kernel paging request at " "address %08lx, epc == %08x, ra == %08x\n", @@ -203,6 +222,7 @@ (unsigned int) regs->regs[31]); die("Oops", regs, write); do_exit(SIGKILL); + bust_spinlocks(0); /* * We ran out of memory, or some other thing happened to us that made diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/mips64/mm/init.c linux.ac/arch/mips64/mm/init.c --- linux.vanilla/arch/mips64/mm/init.c Mon Oct 16 20:58:51 2000 +++ linux.ac/arch/mips64/mm/init.c Wed May 23 23:52:07 2001 @@ -37,6 +37,9 @@ #include #endif #include +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; unsigned long totalram_pages; @@ -411,7 +414,7 @@ si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/parisc/mm/init.c linux.ac/arch/parisc/mm/init.c --- linux.vanilla/arch/parisc/mm/init.c Tue Dec 5 20:29:39 2000 +++ linux.ac/arch/parisc/mm/init.c Wed May 23 23:52:07 2001 @@ -458,7 +458,7 @@ i = max_mapnr; val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); #if 0 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/ppc/mm/init.c linux.ac/arch/ppc/mm/init.c --- linux.vanilla/arch/ppc/mm/init.c Sat May 26 16:52:59 2001 +++ linux.ac/arch/ppc/mm/init.c Thu May 24 00:00:38 2001 @@ -68,6 +68,9 @@ #if defined(CONFIG_4xx) #include "4xx_tlb.h" #endif +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; #define MAX_LOW_MEM (512 << 20) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390/mm/init.c linux.ac/arch/s390/mm/init.c --- linux.vanilla/arch/s390/mm/init.c Mon Apr 30 15:13:12 2001 +++ linux.ac/arch/s390/mm/init.c Wed May 23 23:52:07 2001 @@ -35,6 +35,9 @@ #include #include #include +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; static unsigned long totalram_pages; @@ -271,7 +274,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/kernel/linux32.c linux.ac/arch/s390x/kernel/linux32.c --- linux.vanilla/arch/s390x/kernel/linux32.c Mon Apr 30 15:13:12 2001 +++ linux.ac/arch/s390x/kernel/linux32.c Tue May 8 18:20:39 2001 @@ -897,24 +897,24 @@ return sys32_fcntl(fd, cmd, arg); } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; +struct mem_dqblk32 { __u32 dqb_ihardlimit; __u32 dqb_isoftlimit; __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u64 dqb_curspace; __kernel_time_t32 dqb_btime; __kernel_time_t32 dqb_itime; }; -extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr); +extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, __kernel_caddr_t addr); asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) { int cmds = cmd >> SUBCMDSHIFT; int err; - struct dqblk d; + struct mem_dqblk d; mm_segment_t old_fs; char *spec; @@ -924,33 +924,35 @@ case Q_SETQUOTA: case Q_SETUSE: case Q_SETQLIM: - if (copy_from_user (&d, (struct dqblk32 *)addr, - sizeof (struct dqblk32))) + if (copy_from_user (&d, (struct mem_dqblk32 *)addr, + sizeof (struct mem_dqblk32))) return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime; break; default: return sys_quotactl(cmd, special, - id, (caddr_t)addr); + id, (__kernel_caddr_t)addr); } spec = getname (special); err = PTR_ERR(spec); if (IS_ERR(spec)) return err; old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); set_fs (old_fs); putname (spec); + if (err) + return err; if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user ((struct dqblk32 *)addr, &d, - sizeof (struct dqblk32))) + ((struct mem_dqblk32 *)&d)->dqb_itime = i; + ((struct mem_dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct mem_dqblk32 *)addr, &d, + sizeof (struct mem_dqblk32))) return -EFAULT; } - return err; + return 0; } static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/mm/fault.c linux.ac/arch/s390x/mm/fault.c --- linux.vanilla/arch/s390x/mm/fault.c Tue Apr 3 17:31:57 2001 +++ linux.ac/arch/s390x/mm/fault.c Thu Apr 12 12:00:34 2001 @@ -33,6 +33,34 @@ extern void die(const char *,struct pt_regs *,long); +extern spinlock_t timerlist_lock; + +/* + * Unlock any spinlocks which will prevent us from getting the + * message out + */ +void bust_spinlocks(int yes) +{ + spin_lock_init(&timerlist_lock); + if (yes) { + oops_in_progress = 1; +#ifdef CONFIG_SMP + atomic_set(&global_irq_lock,0); +#endif + } else { + int loglevel_save = console_loglevel; + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; + } +} + /* * This routine handles page faults. It determines the address, * and the problem, and then passes it off to one of the appropriate diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/s390x/mm/init.c linux.ac/arch/s390x/mm/init.c --- linux.vanilla/arch/s390x/mm/init.c Mon Apr 30 15:13:12 2001 +++ linux.ac/arch/s390x/mm/init.c Wed May 23 23:52:07 2001 @@ -35,6 +35,9 @@ #include #include #include +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; static unsigned long totalram_pages; @@ -284,7 +287,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sh/mm/init.c linux.ac/arch/sh/mm/init.c --- linux.vanilla/arch/sh/mm/init.c Mon Apr 30 15:13:13 2001 +++ linux.ac/arch/sh/mm/init.c Wed May 23 23:52:07 2001 @@ -34,6 +34,9 @@ #include #include #include +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; /* * Cache of MMU context last used. @@ -215,7 +218,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = totalhigh_pages; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/kernel/sys_sunos.c linux.ac/arch/sparc/kernel/sys_sunos.c --- linux.vanilla/arch/sparc/kernel/sys_sunos.c Sat May 26 16:53:00 2001 +++ linux.ac/arch/sparc/kernel/sys_sunos.c Thu May 17 14:03:37 2001 @@ -605,7 +605,7 @@ struct sunos_nfs_mount_args { struct sockaddr_in *addr; /* file server address */ - struct nfs_fh *fh; /* File handle to be mounted */ + struct nfs3_fh *fh; /* File handle to be mounted */ int flags; /* flags */ int wsize; /* write size in bytes */ int rsize; /* read size in bytes */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/lib/debuglocks.c linux.ac/arch/sparc/lib/debuglocks.c --- linux.vanilla/arch/sparc/lib/debuglocks.c Fri Sep 10 19:06:19 1999 +++ linux.ac/arch/sparc/lib/debuglocks.c Tue Apr 3 17:54:36 2001 @@ -29,11 +29,9 @@ static inline void show(char *str, spinlock_t *lock, unsigned long caller) { int cpu = smp_processor_id(); - extern spinlock_t console_lock; - if (lock != &console_lock) - printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str, - lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3); + printk("%s(%p) CPU#%d stuck at %08lx, owner PC(%08lx):CPU(%lx)\n",str, + lock, cpu, caller, lock->owner_pc & ~3, lock->owner_pc & 3); } static inline void show_read(char *str, rwlock_t *lock, unsigned long caller) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/mm/fault.c linux.ac/arch/sparc/mm/fault.c --- linux.vanilla/arch/sparc/mm/fault.c Tue Apr 3 17:31:57 2001 +++ linux.ac/arch/sparc/mm/fault.c Tue Apr 3 17:54:36 2001 @@ -378,7 +378,7 @@ pmd = pmd_offset(pgd, address); pmd_k = pmd_offset(pgd_k, address); - if (pmd_present(*pmd) || !pmd_present(*pmd_k)) + if (!pmd_present(*pmd_k)) goto bad_area_nosemaphore; pmd_val(*pmd) = pmd_val(*pmd_k); return; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc/mm/init.c linux.ac/arch/sparc/mm/init.c --- linux.vanilla/arch/sparc/mm/init.c Mon Apr 30 15:13:13 2001 +++ linux.ac/arch/sparc/mm/init.c Wed May 23 23:52:07 2001 @@ -32,6 +32,9 @@ #include #include #include +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; unsigned long *sparc_valid_addr_bitmap; @@ -534,7 +537,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = totalhigh_pages; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/defconfig linux.ac/arch/sparc64/defconfig --- linux.vanilla/arch/sparc64/defconfig Sat May 26 16:53:00 2001 +++ linux.ac/arch/sparc64/defconfig Sat May 26 17:03:06 2001 @@ -454,7 +454,7 @@ CONFIG_ISO9660_FS=m CONFIG_JOLIET=y CONFIG_MINIX_FS=m -CONFIG_VXFS_FS=m +CONFIG_FREEVXFS_FS=m # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set CONFIG_HPFS_FS=m diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/sys_sparc32.c linux.ac/arch/sparc64/kernel/sys_sparc32.c --- linux.vanilla/arch/sparc64/kernel/sys_sparc32.c Mon Apr 30 15:13:13 2001 +++ linux.ac/arch/sparc64/kernel/sys_sparc32.c Tue May 8 18:23:56 2001 @@ -887,24 +887,24 @@ return sys32_fcntl(fd, cmd, arg); } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; +struct mem_dqblk32 { __u32 dqb_ihardlimit; __u32 dqb_isoftlimit; __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u64 dqb_curspace; __kernel_time_t32 dqb_btime; __kernel_time_t32 dqb_itime; }; -extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr); +extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, __kernel_caddr_t addr); asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) { int cmds = cmd >> SUBCMDSHIFT; int err; - struct dqblk d; + struct mem_dqblk d; mm_segment_t old_fs; char *spec; @@ -914,33 +914,35 @@ case Q_SETQUOTA: case Q_SETUSE: case Q_SETQLIM: - if (copy_from_user (&d, (struct dqblk32 *)addr, - sizeof (struct dqblk32))) + if (copy_from_user (&d, (struct mem_dqblk32 *)addr, + sizeof (struct mem_dqblk32))) return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime; break; default: return sys_quotactl(cmd, special, - id, (caddr_t)addr); + id, (__kernel_caddr_t)addr); } spec = getname (special); err = PTR_ERR(spec); if (IS_ERR(spec)) return err; old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); set_fs (old_fs); putname (spec); + if (err) + return err; if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user ((struct dqblk32 *)addr, &d, - sizeof (struct dqblk32))) + ((struct mem_dqblk32 *)&d)->dqb_itime = i; + ((struct mem_dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct mem_dqblk32 *)addr, &d, + sizeof (struct mem_dqblk32))) return -EFAULT; } - return err; + return 0; } static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/kernel/sys_sunos32.c linux.ac/arch/sparc64/kernel/sys_sunos32.c --- linux.vanilla/arch/sparc64/kernel/sys_sunos32.c Sat May 26 16:53:00 2001 +++ linux.ac/arch/sparc64/kernel/sys_sunos32.c Thu May 17 14:04:09 2001 @@ -570,7 +570,7 @@ struct sunos_nfs_mount_args { struct sockaddr_in *addr; /* file server address */ - struct nfs_fh *fh; /* File handle to be mounted */ + struct nfs3_fh *fh; /* File handle to be mounted */ int flags; /* flags */ int wsize; /* write size in bytes */ int rsize; /* read size in bytes */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/sparc64/mm/init.c linux.ac/arch/sparc64/mm/init.c --- linux.vanilla/arch/sparc64/mm/init.c Sat May 26 16:53:00 2001 +++ linux.ac/arch/sparc64/mm/init.c Wed May 23 23:52:07 2001 @@ -30,6 +30,9 @@ #include #include #include +#include + +mmu_gather_t mmu_gathers[NR_CPUS]; extern void device_scan(void); @@ -1520,7 +1523,7 @@ void si_meminfo(struct sysinfo *val) { val->totalram = num_physpages; - val->sharedram = 0; + val->sharedram = atomic_read(&shmem_nrpages); val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/Makefile linux.ac/arch/um/Makefile --- linux.vanilla/arch/um/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/Makefile Sun May 13 20:23:29 2001 @@ -0,0 +1,84 @@ +include arch/$(ARCH)/Makefile-$(SUBARCH) + +include/linux/version.h: arch/$(ARCH)/Makefile + +ARCH_DIR = $(TOPDIR)/arch/um + +MAKEBOOT = $(MAKE) -C $(ARCH_DIR)/boot + +ifeq ($(CONFIG_DEBUGSYM),y) +DEBUG = -g +CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) +endif + +ifeq ($(CONFIG_GCOV),y) +CFLAGS += -fprofile-arcs -ftest-coverage +endif + +ifeq ($(CONFIG_GPROF), y) +PROFILE += -pg -DPROFILING +LINK_PROFILE = $(PROFILE) -Wl,--wrap,__monstartup +endif + +SUBDIRS += $(ARCH_DIR)/fs $(ARCH_DIR)/drivers $(ARCH_DIR)/kernel \ + $(ARCH_DIR)/sys-$(SUBARCH) + +LIBS += $(shell [ -e $(ARCH_DIR)/fs/fs.o ] && echo $(ARCH_DIR)/fs/fs.o) \ + $(ARCH_DIR)/kernel/um.o $(ARCH_DIR)/drivers/um_drivers.o \ + $(ARCH_DIR)/sys-$(SUBARCH)/sys.o + +ifeq ($(CONFIG_PT_PROXY), y) +SUBDIRS += $(ARCH_DIR)/ptproxy +LIBS += $(ARCH_DIR)/ptproxy/ptproxy.a +endif + +NESTING = 0 + +CFLAGS += $(DEBUG) $(PROFILE) $(ARCH_CFLAGS) -D__arch_um__ \ + -DSUBARCH=\"$(SUBARCH)\" -DNESTING=$(NESTING) + +#-D_FILE_OFFSET_BITS=64 + +LINKFLAGS += -r + +$(ARCH_DIR)/link.ld: + m4 -DSTART=$(shell echo $$((($(NESTING) + 1) * $(START_ADDR)))) \ + -DSUBARCH=$(SUBARCH) -DELF_SUBARCH=$(ELF_SUBARCH) \ + $(ARCH_DIR)/link.ld.in > $(ARCH_DIR)/link.ld + +ARCH_SYMLINKS = include/asm-um/arch arch/um/include/sysdep + +linux: $(ARCH_SYMLINKS) $(ARCH_DIR)/main.o vmlinux $(ARCH_DIR)/link.ld + mv vmlinux vmlinux.o + $(CC) -Wl,-T,$(ARCH_DIR)/link.ld $(LINK_PROFILE) -o linux -static \ + $(ARCH_DIR)/main.o vmlinux.o -L/usr/lib + rm -f $(ARCH_DIR)/link.ld + +USER_CFLAGS := $(patsubst -I%,,$(CFLAGS)) +USER_CFLAGS := $(patsubst -D__KERNEL__,,$(USER_CFLAGS)) -I../include + +$(ARCH_DIR)/main.o: $(ARCH_DIR)/main.c + $(CC) -D__KERNEL__ $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +archmrproper: + $(MAKE) -C $(ARCH_DIR)/sys-$(SUBARCH) archmrproper + rm -f linux x.i $(ARCH_SYMLINKS) include/asm \ + $(addprefix $(ARCH_DIR)/kernel/,$(KERN_SYMLINKS)) + +archclean: + find . \( -name '*.bb' -o -name '*.bbg' -o -name '*.da' \ + -o -name '*.gcov' \) -type f -print | xargs rm -f + rm -f gmon.out + rm -f link.ld + @$(MAKEBOOT) clean + +archdep: $(ARCH_SYMLINKS) + @$(MAKEBOOT) dep + +include/asm-um/arch: + cd $(TOPDIR)/include/asm-um && ln -sf ../asm-$(SUBARCH) arch + +arch/um/include/sysdep: + cd $(TOPDIR)/arch/um/include && ln -sf sysdep-$(SUBARCH) sysdep + +export SUBARCH diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/Makefile-i386 linux.ac/arch/um/Makefile-i386 --- linux.vanilla/arch/um/Makefile-i386 Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/Makefile-i386 Sun May 13 19:41:45 2001 @@ -0,0 +1,3 @@ +START_ADDR = 0x10000000 +ARCH_CFLAGS = -U__$(SUBARCH)__ -U$(SUBARCH) +ELF_SUBARCH = $(SUBARCH) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/Makefile-ia64 linux.ac/arch/um/Makefile-ia64 --- linux.vanilla/arch/um/Makefile-ia64 Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/Makefile-ia64 Mon Apr 9 23:25:02 2001 @@ -0,0 +1 @@ +START_ADDR = 0x1000000000000000 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/Makefile-ppc linux.ac/arch/um/Makefile-ppc --- linux.vanilla/arch/um/Makefile-ppc Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/Makefile-ppc Mon Apr 9 23:25:02 2001 @@ -0,0 +1,2 @@ +START_ADDR = 0x20000000 +ARCH_CFLAGS = -U__powerpc__ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/boot/Makefile linux.ac/arch/um/boot/Makefile --- linux.vanilla/arch/um/boot/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/boot/Makefile Mon Apr 9 23:25:02 2001 @@ -0,0 +1,3 @@ +dep: + +clean: diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/config.in linux.ac/arch/um/config.in --- linux.vanilla/arch/um/config.in Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/config.in Tue Apr 24 17:54:11 2001 @@ -0,0 +1,82 @@ +define_bool CONFIG_USERMODE y + +mainmenu_name "Linux/Usermode Kernel Configuration" + +define_bool CONFIG_ISA y +define_bool CONFIG_SBUS n + +define_bool CONFIG_UID16 y + +define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y + +mainmenu_option next_comment +comment 'Code maturity level options' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +endmenu + +mainmenu_option next_comment +comment 'Processor features' +bool 'Symmetric multi-processing support' CONFIG_SMP +comment 'General Setup' +define_bool CONFIG_STDIO_CONSOLE y +bool 'Enable loadable module support' CONFIG_MODULES +bool 'Networking support' CONFIG_NET +bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT +bool 'Sysctl support' CONFIG_SYSCTL +tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS +if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then + int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 +bool 'Virtual serial line' CONFIG_SSL +tristate 'Host filesystem' CONFIG_HOSTFS +fi +endmenu + +mainmenu_option next_comment +comment 'Block devices' +define_bool CONFIG_BLK_DEV_UBD y +bool 'Always do synchronous disk IO for UBD' CONFIG_BLK_DEV_UBD_SYNC +tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP +dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET +tristate 'RAM disk support' CONFIG_BLK_DEV_RAM +if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then + int ' Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096 +fi +dep_bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM +endmenu + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + +if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'Network device support' + + bool 'Network device support' CONFIG_NETDEVICES + if [ "$CONFIG_NETDEVICES" = "y" ]; then + bool 'Virtual ethernet device' CONFIG_NET_UM_ETH + if [ "$CONFIG_NET_UM_ETH" = "y" ]; then + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_NETLINK" = "y" ]; then + tristate 'Ethertap network tap (EXPERIMENTAL)' CONFIG_ETHERTAP + fi + fi + fi + fi + bool 'Old slip-based network device' CONFIG_NET_UMN + endmenu +fi + +source fs/Config.in + +mainmenu_option next_comment +comment 'Kernel hacking' +bool 'Enable kernel debugging symbols' CONFIG_DEBUGSYM +dep_bool 'Enable ptrace proxy' CONFIG_PT_PROXY $CONFIG_DEBUGSYM +dep_bool 'Enable gprof support' CONFIG_GPROF $CONFIG_DEBUGSYM +dep_bool 'Enable gcov support' CONFIG_GCOV $CONFIG_DEBUGSYM +endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/config.release linux.ac/arch/um/config.release --- linux.vanilla/arch/um/config.release Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/config.release Mon Apr 9 23:25:02 2001 @@ -0,0 +1,192 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_USERMODE=y +CONFIG_ISA=y +# CONFIG_SBUS is not set +CONFIG_UID16=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Processor features +# +# CONFIG_SMP is not set + +# +# General Setup +# +CONFIG_STDIO_CONSOLE=y +CONFIG_MODULES=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_SYSCTL=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_SSL=y +CONFIG_HOSTFS=y + +# +# Block devices +# +CONFIG_BLK_DEV_UBD=y +# CONFIG_BLK_DEV_UBD_SYNC is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +CONFIG_NET_UM_ETH=y +CONFIG_NET_UMN=y + +# +# File systems +# +CONFIG_QUOTA=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m +CONFIG_REISERFS_FS=y +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_UMSDOS_FS=y +CONFIG_VFAT_FS=y +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +CONFIG_MINIX_FS=m +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_UTF8 is not set + +# +# Kernel hacking +# +# CONFIG_DEBUGSYM is not set diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/defconfig linux.ac/arch/um/defconfig --- linux.vanilla/arch/um/defconfig Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/defconfig Mon Apr 9 23:25:02 2001 @@ -0,0 +1,195 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_USERMODE=y +CONFIG_ISA=y +# CONFIG_SBUS is not set +CONFIG_UID16=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Processor features +# +# CONFIG_SMP is not set + +# +# General Setup +# +CONFIG_STDIO_CONSOLE=y +CONFIG_MODULES=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_SYSCTL=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_SSL=y +CONFIG_HOSTFS=m + +# +# Block devices +# +CONFIG_BLK_DEV_UBD=y +# CONFIG_BLK_DEV_UBD_SYNC is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +CONFIG_NET_UM_ETH=y +CONFIG_NET_UMN=y + +# +# File systems +# +CONFIG_QUOTA=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_UMSDOS_FS=m +CONFIG_VFAT_FS=m +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=m +# CONFIG_JOLIET is not set +CONFIG_MINIX_FS=m +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_UTF8 is not set + +# +# Kernel hacking +# +CONFIG_DEBUGSYM=y +CONFIG_PT_PROXY=y +# CONFIG_GPROF is not set +# CONFIG_GCOV is not set diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/Makefile linux.ac/arch/um/drivers/Makefile --- linux.vanilla/arch/um/drivers/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/Makefile Sun May 13 20:23:29 2001 @@ -0,0 +1,38 @@ +# +# Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +O_TARGET := um_drivers.o + +obj-y = +obj-m = + +EXTRA_CFLAGS = -I../include + +USER_CFLAGS := $(patsubst -I%,,$(CFLAGS)) +USER_CFLAGS := $(patsubst -D__KERNEL__,,$(USER_CFLAGS)) $(EXTRA_CFLAGS) + +CFLAGS_stdio_console.o := $(CFLAGS) +CFLAGS_stdio_console_user.o := $(USER_CFLAGS) +CFLAGS_ssl.o := $(CFLAGS) +CFLAGS_chan_kern.o := $(CFLAGS) +CFLAGS_chan_user.o := $(USER_CFLAGS) +CFLAGS_ubd.o := $(CFLAGS) +CFLAGS_ubd_user.o := $(USER_CFLAGS) +CFLAGS_umn_kern.o := $(CFLAGS) +CFLAGS_umn_user.o := $(USER_CFLAGS) +CFLAGS_eth_kern.o := $(CFLAGS) +CFLAGS_eth_user.o := $(USER_CFLAGS) + +obj-$(CONFIG_STDIO_CONSOLE) += stdio_console.o stdio_console_user.o +obj-$(CONFIG_SSL) += ssl.o +obj-$(CONFIG_SSL) += chan_user.o chan_kern.o +obj-$(CONFIG_STDIO_CONSOLE) += chan_user.o chan_kern.o +obj-$(CONFIG_BLK_DEV_UBD) += ubd.o ubd_user.o +obj-$(CONFIG_NET_UMN) += umn_user.o umn_kern.o +obj-$(CONFIG_NET_UM_ETH) += eth_kern.o eth_user.o + +override CFLAGS = + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/chan_kern.c linux.ac/arch/um/drivers/chan_kern.c --- linux.vanilla/arch/um/drivers/chan_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/chan_kern.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include "chan.h" +#include "user_util.h" +#include "kern.h" + +void tty_interrupt(struct io_chan *chan, void *tty_ptr, int count) +{ + struct tty_struct *tty = tty_ptr; + int n; + + if(count == 0) return; + n = tty_read(&chan->in, tty); + tty_flip_buffer_push(tty); + if(n > 0) chan->in.hung_up = 0; + if(n >= 0) reactivate_fd(chan->in.fd); +} + +void tty_eof(void *tty_ptr, int *hung_up) +{ + struct tty_struct *tty = tty_ptr; + + if(tty == NULL) return; + if(!*hung_up) tty_hangup(tty); + *hung_up = 1; +} + +void tty_receive_char(void *tty_ptr, char ch) +{ + struct tty_struct *tty = tty_ptr; + + if(tty == NULL) return; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; + if((tty->flip.flag_buf_ptr == NULL) || + (tty->flip.char_buf_ptr == NULL)) + return; + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = ch; +} + +static void accept_interrupt(int irq, void *dev, struct pt_regs *unused) +{ + accept_connection(dev); +} + +static int setup_accept_irq(int fd, void *data) +{ + return(um_request_irq(ACCEPT_IRQ, fd, accept_interrupt, + SA_INTERRUPT | SA_SHIRQ, "socket", data)); +} + +static int open_chan(struct chan *chan, int (*irq_setup)(int fd, void *data), + int input, void *data) +{ + struct io_chan subchan; + int err; + + if(chan->opened) return(0); + switch(chan->type){ + case XTERM: + err = open_xterm(chan); + break; + case PTY: + err = open_pty(chan); + break; + case TTY: + err = open_tty(chan); + break; + case PTS: + err = open_pts(chan); + break; + case FD: + err = open_fd(chan); + break; + case SOCKET: + subchan = ((struct io_chan) + PTS_IO_CHAN_INIT(NULL, -1, 1, INIT_STATIC)); + err = open_chan_pair(&subchan, irq_setup, data); + if(err) return(err); + chan->data.sock.pty = subchan.in.fd; + err = open_socket(chan); + irq_setup = setup_accept_irq; + data = chan; + break; + default: + printk("open_chan : Unknown channel type - %d\n", chan->type); + err = -ENODEV; + break; + } + if(err) return(err); + if(input && irq_setup) err = (*irq_setup)(chan->fd, data); + if(err) return(err); + chan->opened = 1; + return(0); +} + +int open_chan_pair(struct io_chan *chan, int (*irq_setup)(int fd, void *data), + void *data) +{ + int err; + + err = open_chan(&chan->in, irq_setup, 1, data); + if(err) return(err); + if(chan->out.type != COPY) + err = open_chan(&chan->out, irq_setup, 0, data); + return(err); +} + +static void close_chan(struct chan *chan) +{ + switch(chan->type){ + case XTERM: + if(chan->data.xterm.pid != -1) + kill(chan->data.xterm.pid, SIGKILL); + break; + default: + break; + } +} + +void close_chan_pair(struct io_chan *chan) +{ + close_chan(&chan->in); + if(chan->out.type != COPY) + close_chan(&chan->out); +} + +int write_chan(struct io_chan *chan, const char *buf, int len) +{ + struct chan *c; + int n; + + if(chan->out.type == COPY) c = &chan->in; + else c = &chan->out; + if(c->fd == -1) return(-ENODEV); + n = write(c->fd, buf, len); + if(errno == EAGAIN) n = 0; + if(n < 0) return(-errno); + return(n); +} + +static int parse_chan(char *str, int device, struct chan *chan, int pri, + struct chan_opts *opts) +{ + if(pri <= chan->init_pri) return(0); + if(!strncmp(str, "pty", strlen("pty"))){ + *chan = ((struct chan) PTY_CHAN_INIT(opts->announce, + opts->dev, opts->raw_pty, + pri)); + } + else if(!strncmp(str, "tty", strlen("tty"))){ + char *tty_name; + + tty_name = &str[strlen("tty")]; + if(*tty_name != ':'){ + printk("parse_chan : channel type 'tty' must " + "specify a device\n"); + return(-1); + } + tty_name++; + *chan = ((struct chan) TTY_CHAN_INIT(tty_name, opts->raw_pty, + pri)); + } + else if(!strncmp(str, "pts", strlen("pts"))){ + *chan = ((struct chan) PTS_CHAN_INIT(opts->announce, + opts->dev, opts->raw_pty, + pri)); + } + else if(!strncmp(str, "xterm", strlen("xterm"))){ + *chan = ((struct chan) + XTERM_CHAN_INIT(device, opts->xterm_title, + opts->raw_pty, pri)); + } + else if(!strncmp(str, "socket", strlen("socket"))){ + char *sock; + int n; + + sock = &str[strlen("socket")]; + if(*sock != ':'){ + printk("parse_chan : channel type 'socket' must " + "specify a socket\n"); + return(-1); + } + sock++; + n = simple_strtoul(sock, NULL, 0); + if(n == 0){ + printk("parse_chan : invalid socket number - '%s'\n", + sock); + return(-1); + } + *chan = ((struct chan) SOCK_CHAN_INIT(n, pri)); + } + else if(!strncmp(str, "fd", strlen("fd"))){ + char *fd, *end; + int n; + + fd = &str[strlen("fd")]; + if(*fd != ':'){ + printk("parse_chan : channel type 'fd' must " + "specify a file descriptor\n"); + return(-1); + } + fd++; + n = simple_strtoul(fd, &end, 0); + if(*end != '\0'){ + printk("parse_chan : couldn't parse file descriptor " + "'%s'", fd); + return(-1); + } + *chan = ((struct chan) FD_CHAN_INIT(n, pri)); + return(0); + } + else { + printk("parse_chan couldn't parse \"%s\"\n", str); + return(-1); + } + chan->init_pri = pri; + return(0); +} + +int parse_chan_pair(char *str, int device, struct io_chan *chan, int pri, + struct chan_opts *opts) +{ + char *in, *out; + int err; + + if((out = strchr(str, ',')) != NULL){ + in = str; + *out = '\0'; + out++; + err = parse_chan(in, device, &chan->in, pri, opts); + if(err) return(err); + err = parse_chan(out, device, &chan->out, pri, opts); + return(err); + } + else { + err = parse_chan(str, device, &chan->in, pri, opts); + if(err) return(err); + chan->out.type = COPY; + return(0); + } +} + + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/chan_user.c linux.ac/arch/um/drivers/chan_user.c --- linux.vanilla/arch/um/drivers/chan_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/chan_user.c Sun May 13 19:44:45 2001 @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ssl.h" +#include "chan.h" +#include "user.h" +#include "user_util.h" +#include "kern_util.h" + +int tty_read(struct chan *chan, void *tty) +{ + int n, count = 0; + char ch; + + while((n = read(chan->fd, &ch, sizeof(ch))) == sizeof(ch)){ + tty_receive_char(tty, ch); + count++; + } + if(n < 0){ + if(errno == EIO){ + tty_eof(tty, &chan->hung_up); + return(0); + } + else if(errno != EAGAIN){ + printk("tty_read read failed, errno = %d\n", errno); + return(-1); + } + } + return(1); +} + +int open_fd(struct chan *chan) +{ + raw(chan->fd, 0); + return(0); +} + +struct xterm_info { + char tty[2]; + int fd; + int console_num; + int *pid_out; + char *title; +}; + +static void xterm_tramp(void *arg) +{ + struct xterm_info *info; + int pid; + char title[256], flag[sizeof("Sxxnn\0")]; + + info = arg; + sprintf(flag, "-S%c%c%d", info->tty[0], info->tty[1], info->fd); + sprintf(title, info->title, info->console_num); + if((pid = fork()) != 0) *info->pid_out = pid; + else { + execlp("xterm", "xterm", flag, "-T", title, NULL); + printk("execlp of xterm failed - errno = %d\n", errno); + exit(1); + } +} + +static int getmaster(char *line, int raw) +{ + struct termios tt; + struct stat stb; + char *pty, *bank, *cp; + int master; + + pty = &line[strlen("/dev/ptyp")]; + for (bank = "pqrs"; *bank; bank++) { + line[strlen("/dev/pty")] = *bank; + *pty = '0'; + if (stat(line, &stb) < 0) + break; + for (cp = "0123456789abcdef"; *cp; cp++) { + *pty = *cp; + master = open(line, O_RDWR); + if (master >= 0) { + char *tp = &line[strlen("/dev/")]; + int ok; + + /* verify slave side is usable */ + *tp = 't'; + ok = access(line, R_OK|W_OK) == 0; + *tp = 'p'; + if (ok) { + if(raw){ + tcgetattr(master, &tt); + cfmakeraw(&tt); + tcsetattr(master, TCSADRAIN, + &tt); + } + return(master); + } + (void) close(master); + } + } + } + return(-1); +} + +int open_xterm(struct chan *chan) +{ + struct xterm_info info; + int master, slave; + char dev[] = "/dev/ptyXX", c; + + master = getmaster(dev, chan->data.xterm.raw); + if(master == -1) return(-ENODEV); + dev[strlen("/dev/")] = 't'; + slave = open(dev, O_RDWR); + if(slave == -1) return(-errno); + info.tty[0] = dev[strlen("/dev/pty")]; + info.tty[1] = dev[strlen("/dev/ptyX")]; + info.fd = master; + info.console_num = chan->data.xterm.line; + info.pid_out = &chan->data.xterm.pid; + info.title = chan->data.xterm.title; + tracing_cb(xterm_tramp, &info); + while((read(slave, &c, sizeof(c)) == sizeof(c)) && (c != '\n')) ; + if(chan->data.xterm.raw) raw(slave, 1); + chan->fd = slave; + return(0); +} + +int open_pty(struct chan *chan) +{ + char dev[sizeof("/dev/ptyxx\0")] = "/dev/ptyxx"; + + chan->fd = getmaster(dev, chan->data.pty.raw); + if(chan->fd < 0) return(-errno); + if(chan->data.pty.announce) + (*chan->data.pty.announce)(dev, chan->data.pty.dev); + return(0); +} + +int open_tty(struct chan *chan) +{ + int fd; + + fd = open(chan->data.tty.dev, O_RDWR); + if(fd < 0) return(-errno); + chan->fd = fd; + if(chan->data.tty.raw) raw(fd, 1); + return(0); +} + +int open_pts(struct chan *chan) +{ + int fd; + + if((fd = get_pty()) < 0){ + printk("open_pts : Failed to open pts\n"); + return(-errno); + } + chan->fd = fd; + if(chan->data.tty.raw) raw(fd, 1); + if(chan->data.pts.announce) + (*chan->data.pts.announce)(ptsname(fd), chan->data.pts.dev); + return(0); +} + +int open_socket(struct chan *chan) +{ + struct sockaddr_in addr; + int sock; + + sock = socket(PF_INET, SOCK_STREAM, 0); + if(sock < 0) return(-errno); + addr.sin_family = AF_INET; + addr.sin_port = htons(chan->data.sock.port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + if(bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) + return(-errno); + if(listen(sock, 1) < 0) return(-errno); + chan->data.sock.connected = 0; + chan->fd = sock; + return(0); +} + +struct telnetd_args { + int fd; + char helper[sizeof("/tmp/uml_dev/nnnn\0")]; +}; + +static void telnetd_tramp(void *arg) +{ + struct telnetd_args info = *((struct telnetd_args *) arg); + + if(fork() == 0){ + dup2(info.fd, 0); + dup2(info.fd, 1); + dup2(info.fd, 2); + close(info.fd); + execlp("in.telnetd", "in.telnetd", "-L", info.helper, NULL); + printk("execlp of telnetd failed - errno = %d\n", errno); + exit(1); + } +} + +void accept_connection(struct chan *chan) +{ + struct telnetd_args info; + char *name; + + if((info.fd = accept(chan->fd, NULL, 0)) < 0){ + printk("tty_read: accept failed - errno = %d\n", + errno); + return; + } + mkdir("/tmp/uml_dev", 0777); + name = ptsname(chan->data.sock.pty); + name = rindex(name, '/'); + sprintf(info.helper, "/tmp/uml_dev%s", name); + if(symlink("/home/jdike/bin/telnet_helper", info.helper)){ + printk("symlink from '%s' failed, errno = %d\n", info.helper, + errno); + return; + } + tracing_cb(telnetd_tramp, &info); + chan->data.sock.connected = 1; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/eth_kern.c linux.ac/arch/um/drivers/eth_kern.c --- linux.vanilla/arch/um/drivers/eth_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/eth_kern.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,384 @@ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "user_util.h" +#include "eth_net.h" +#include "radix.h" + +extern int uml_net_user_mac(void); +extern int uml_net_user_close(int); +extern int uml_net_user_open(struct sockaddr *, void *, int, int); +extern int uml_net_user_join(int, int); +extern int uml_net_user_tx(int, char *, int); +extern int uml_net_user_rx(int, char *, int); + +#define MAC_NODE_NIBBLE 6 +#define MAC_TREE_DEPTH 48 + +struct mac_node { + RADIX_ENTRY(mac_node,MAC_NODE_NIBBLE) next; + int action; +}; + +RADIX_HEAD(mac_tree,mac_node); + +struct uml_net_private { + spinlock_t lock; + int socket; + int net_num; + struct net_device_stats stats; + struct net_device *next; + struct sockaddr addr; +}; + +static struct mac_tree mac_tree; +static struct net_device *uml_net_dev = NULL; + +void uml_net_interrupt(int, void *, struct pt_regs *); + +static int uml_net_rx(struct net_device *); +static int uml_net_open(struct net_device *); +static int uml_net_close(struct net_device *); +static int uml_net_set_mac(struct net_device *, void *); +static void uml_net_tx_timeout(struct net_device *dev); +static void uml_net_set_multicast_list(struct net_device *); +static int uml_net_ioctl(struct net_device *, struct ifreq *, int); +static int uml_net_start_xmit(struct sk_buff *, struct net_device *); +static struct net_device_stats *uml_net_get_stats(struct net_device *); +static int uml_net_insert_mac_tree(long long key,int value); + +int __init uml_net_probe(void) +{ + struct net_device *dev = NULL; + struct uml_net_private *lp; + struct sockaddr_in *sin; + long long key; + char *priv; + int i; + + RADIX_INIT(&mac_tree); + if(uml_net_insert_mac_tree(0xffffffffffff,0)) return -1; + + for (i = 0; i < 4; i++) { + + dev = init_etherdev(NULL, 0); + + if (dev == NULL) + return -ENOMEM; + + printk("User-mode Linux network interface 0.011 (%s)\n", + dev->name); + + if ((priv = kmalloc(sizeof(struct uml_net_private) + 15, + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + lp = (struct uml_net_private *)(((unsigned long)priv + 15) + & ~15); + + memset(lp, 0, sizeof(struct uml_net_private)); + sin = (struct sockaddr_in *)&lp->addr; + + sin->sin_family = AF_INET; + sin->sin_port = htons(UML_NET_PORT); + sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + lp->net_num = 100; + + /* Fill in the generic fields of the device structure. */ + ether_setup(dev); + + dev->priv = lp; + dev->open = uml_net_open; + dev->hard_start_xmit = uml_net_start_xmit; + dev->stop = uml_net_close; + dev->get_stats = uml_net_get_stats; + dev->set_multicast_list = uml_net_set_multicast_list; + dev->tx_timeout = uml_net_tx_timeout; + dev->set_mac_address = 0; // uml_net_set_mac; + dev->do_ioctl = uml_net_ioctl; + dev->watchdog_timeo = (HZ >> 1); + lp->next = uml_net_dev; + uml_net_dev = dev; + + spin_lock_init(&lp->lock); + + key = uml_net_user_mac(); + key <<= 16; + memcpy(dev->dev_addr, &key, ETH_ALEN); + uml_net_insert_mac_tree(key,dev->ifindex); + } + return 0; +} + +static int uml_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + + spin_lock(&lp->lock); + + if(dev->flags & IFF_UP) { + spin_unlock(&lp->lock); + return -EBUSY; + } + + switch (cmd) { + case SIOCDEVPRIVATE: + { + /* setting net number */ + lp->net_num = ifr->ifr_flags; + break; + } + case SIOCDEVPRIVATE+1: + { + + /* setting dst addr */ + if(ifr->ifr_dstaddr.sa_family) { + memcpy(&lp->addr, &ifr->ifr_dstaddr, + sizeof(struct sockaddr)); + } + break; + } + } + + spin_unlock(&lp->lock); + return 0; +} + +static int uml_net_open(struct net_device *dev) +{ + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + int err = 0; + + spin_lock(&lp->lock); + if(lp->socket) { + spin_unlock(&lp->lock); + return(-ENXIO); + } + lp->socket = uml_net_user_open(&lp->addr, (void *)dev, dev->irq, + lp->net_num); + + if (lp->socket < 0) { + printk("uml_net_open: failed(%d)\n", lp->socket); + lp->socket = 0; + spin_unlock(&lp->lock); + return -ENETUNREACH; + } + + dev->irq = UM_ETH_IRQ; + if((err = um_request_irq(dev->irq, lp->socket, uml_net_interrupt, + SA_INTERRUPT | SA_SHIRQ, dev->name, dev)) != 0) { + spin_unlock(&lp->lock); + uml_net_user_close(lp->socket); + printk("uml_net_open: failed to get irq(%d)\n", err); + return -ENETUNREACH; + } + + spin_unlock(&lp->lock); + + netif_start_queue(dev); + + return 0; +} + +static void uml_net_tx_timeout(struct net_device *dev) +{ + printk("uml_net_tx_timeout enter\n"); + + dev->trans_start = jiffies; + netif_wake_queue(dev); + + printk("uml_net_tx_timeout exit\n"); +} + +static int uml_net_get_mac_node(long long key) +{ + int res = 0; + int value = -1; + RADIX_GET(&mac_tree,mac_node,next,MAC_NODE_NIBBLE,long long, + key,48,action,value,res); + if(res) { + return -1; + } + return value; +} + +static int uml_net_insert_mac_tree(long long key,int value) +{ + int res = 0; + + RADIX_INSERT(&mac_tree,mac_node,next,MAC_NODE_NIBBLE,long long, + key,48,res); + + if(res) { + return -1; + } + RADIX_SET(&mac_tree,mac_node,next,MAC_NODE_NIBBLE,long long, + key,48,action,value,res); + if(res) { + return -1; + } + return 0; +} + +static int uml_net_set_mac(struct net_device *dev, void *addr) +{ + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + struct sockaddr *hwaddr = (struct sockaddr *)addr; + long long key = 0; + + memcpy(&key,hwaddr->sa_data,ETH_ALEN); + uml_net_insert_mac_tree(key,dev->ifindex); + + spin_lock(&lp->lock); + memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); + spin_unlock(&lp->lock); + + return 0; +} + +static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + unsigned long flags; + int size; + + netif_stop_queue(dev); + + spin_lock_irqsave(&lp->lock, flags); + + size = uml_net_user_tx(lp->socket, skb->data, skb->len); + if (size == skb->len) { + lp->stats.tx_packets++; + lp->stats.tx_bytes += skb->len; + dev->trans_start = jiffies; + netif_start_queue(dev); + /* this is normally done in the interrupt when tx finishes */ + netif_wake_queue(dev); + } else { + printk("uml_net_start_xmit: failed(%d)\n", size); + } + + spin_unlock_irqrestore(&lp->lock, flags); + + dev_kfree_skb(skb); + + return 0; +} + +void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + int count = 0; + int retval = 0; + + spin_lock(&lp->lock); + + while ((retval = uml_net_rx(dev)) > 0) { + count++; + if(count > 100) { + break; + } + } + + spin_unlock(&lp->lock); + + if (retval < 0) { + uml_net_close(dev); + } +} + +static int uml_net_rx(struct net_device *dev) +{ + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + unsigned char buffer[MAX_PACKET]; + struct sk_buff *skb; + int pkt_len = 0; + unsigned int value; + long long key = 0; + + if ((pkt_len = uml_net_user_rx(lp->socket, buffer, MAX_PACKET)) > 0) { + /* + * look at the dev flags and the dest MAC to determin if this + * frame is for us + */ + + if (dev->flags & IFF_PROMISC) { + goto uml_net_rx_accept; + } + + memcpy(&key,buffer,ETH_ALEN); + if ((key & 0x010000000000) && (dev->flags & IFF_ALLMULTI)) { + goto uml_net_rx_accept; + } + + value = uml_net_get_mac_node(key); + if (value == dev->ifindex || value == 0) { + goto uml_net_rx_accept; + } + + goto uml_net_rx_drop; /* No match */ + +uml_net_rx_accept: + if ((skb = dev_alloc_skb(MAX_PACKET)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); + skb_put(skb, pkt_len); + eth_copy_and_sum(skb, buffer, pkt_len, 0); + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + + lp->stats.rx_bytes += skb->len; + lp->stats.rx_packets++; + } + return 1; + } else if(pkt_len < 0) { + return -1; + } + return 0; + +uml_net_rx_drop: + lp->stats.rx_dropped++; + return 1; +} + +static int uml_net_close(struct net_device *dev) +{ + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + + netif_stop_queue(dev); + + spin_lock(&lp->lock); + + uml_net_user_close(lp->socket); + lp->socket = 0; + free_irq(dev->irq, dev); + + spin_unlock(&lp->lock); + return 0; +} + +static struct net_device_stats *uml_net_get_stats(struct net_device *dev) +{ + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + return &lp->stats; +} + +static void uml_net_set_multicast_list(struct net_device *dev) +{ + if (dev->flags & IFF_PROMISC) { + printk("%s: Promiscuous mode enabled.\n", dev->name); + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/eth_net.h linux.ac/arch/um/drivers/eth_net.h --- linux.vanilla/arch/um/drivers/eth_net.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/eth_net.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,13 @@ +#ifndef INCLUDE_NET_ETH_NET_H +#define INCLUDE_NET_ETH_NET_H + +#define MAX_PACKET 1544 +#define UML_NET_PORT 5299 +#define DEFAULT_NET_NUM 100 +#define UML_HDR_SIZE (sizeof(unsigned int)*2) + +enum packet_type { + PACKET_DATA = 1, + PACKET_MGMT +}; +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/eth_user.c linux.ac/arch/um/drivers/eth_user.c --- linux.vanilla/arch/um/drivers/eth_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/eth_user.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "user.h" +#include "user_util.h" +#include "eth_net.h" + +int uml_net_user_mac(void) +{ + static int first = 1; + + if(first) { + srand(time(NULL)); + first = 0; + } + return rand(); +} + +static int nb_write(int fd, void *buf, int tot) { + int len = 0; + int remain = tot; + int retval; + +write_try_again: + retval = write(fd, buf+len, remain); + if (retval < 0) { + if (errno == EAGAIN || errno == EINTR) { + goto write_try_again; + } + return -1; + } + if (retval < remain) { + remain -= retval; + len += retval; + goto write_try_again; + } + return 0; +} + +static int nb_read(int fd, void *buf, int tot, int flag) { + int len = 0; + int remain = tot; + int retval; + +read_try_again: + retval = read(fd, buf+len, remain); + if (retval < 0) { + if (errno == EINTR) { + goto read_try_again; + } + if (flag && (errno == EAGAIN)) { + goto read_try_again; + } else { + return 0; + } + return -1; + } + if(retval < remain) { + if(!retval) { + return -1; + } + len += retval; + remain -= retval; + flag = 1; + goto read_try_again; + } + return tot; +} + +int uml_net_user_join(int fd, int net_num) +{ + unsigned int header[3]; + + if (fd < 0) + return -ENOTCONN; + + header[0] = htonl(PACKET_MGMT); + header[1] = htonl(sizeof(unsigned int)); + header[2] = htonl(net_num); + + return nb_write(fd, header, UML_HDR_SIZE + sizeof(unsigned int)); +} + +int uml_net_user_open(struct sockaddr *addr, void *dev, int irq,int net_num) +{ + int new_fd; + + if ((new_fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) + return -ENOMEM; + + if (connect(new_fd,(struct sockaddr*)addr, + sizeof(struct sockaddr)) < 0) { + close(new_fd); + return -ENOTCONN; + } + + if(uml_net_user_join(new_fd,net_num) < 0) { + close(new_fd); + return -ENOTCONN; + } + + return new_fd; +} + +int uml_net_user_tx(int fd, char *buffer, int len) +{ + unsigned char hbuf[MAX_PACKET + UML_HDR_SIZE]; + unsigned char *buf = hbuf + UML_HDR_SIZE; + unsigned int *header = (unsigned int*)hbuf; + int retval = 0; + + if (fd < 0) + return -ENOTCONN; + + if(len > MAX_PACKET) panic("Too big packet"); + + memcpy(buf,buffer,len); + + header[0] = htonl(PACKET_DATA); + header[1] = htonl(len); + + retval = nb_write(fd, hbuf, UML_HDR_SIZE + len); + if (retval) { + return -1; + } + return len; +} + +int uml_net_user_rx(int fd, char *buf, int len) +{ + int retval = 0; + int header[2]; + + if (fd < 0) + return -1; + + retval = nb_read(fd, header, UML_HDR_SIZE,0); + if (retval < 0) { + return -1; + } + if(retval) { + retval = nb_read(fd, buf, ntohl(header[1]),1); + if (retval < 0) { + return -1; + } + } + + reactivate_fd(fd); + return retval; +} + +int __inline__ uml_net_user_close(int fd) +{ + shutdown(fd,SHUT_RDWR); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/radix.h linux.ac/arch/um/drivers/radix.h --- linux.vanilla/arch/um/drivers/radix.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/radix.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,143 @@ +#ifndef _RADIX_H_ +#define _RADIX_H_ + +#include + +#define RADIX_INIT(head) \ +{ \ + (head)->RdX_root = NULL; \ +} + +#define RADIX_ENTRY(type,bits) \ +struct { \ + struct type *RdX_next[(0x01 << bits)]; \ +} + +#define RADIX_HEAD(name,type) \ +struct name { \ + struct type *RdX_root; \ +}; + +#define RADIX_INSERT(head,type,field,bits,tdef,key,klen,result) \ +{ \ + struct type **RdX_node = &((head)->RdX_root); \ + tdef RdX_mask; \ + int RdX_index; \ + int RdX_size = sizeof(tdef)*8; \ + char RdX_status = 0; \ + int RdX_count = 0; \ + \ + if(klen % bits) result = -1; \ + else { \ + RdX_mask = (0x01 << bits)-1; \ + while((RdX_count*bits) < klen) { \ + RdX_status = 1; \ + if(!(*RdX_node)) { \ + RdX_status = 2; \ + (*RdX_node) = (struct type *)kmalloc(sizeof(struct type), \ + GFP_KERNEL); \ + if(!(*RdX_node)) { \ + result = -2; \ + break; \ + } \ + RdX_status = 3; \ + memset((*RdX_node),0,sizeof(struct type)); \ + RdX_status = 4; \ + } \ + RdX_index = (key >> (RdX_size - bits - (RdX_count * bits))) & RdX_mask; \ + RdX_status = 5; \ + RdX_node = &((*RdX_node)->field.RdX_next[RdX_index]); \ + RdX_status = 6; \ + RdX_count++; \ + result = 0; \ + } \ + if(result != -2 && (!(*RdX_node))) { \ + (*RdX_node) = (struct type *)kmalloc(sizeof(struct type), \ + GFP_KERNEL); \ + if(!(*RdX_node)) { \ + result = -2; \ + } else { \ + memset((*RdX_node),0,sizeof(struct type)); \ + } \ + } \ + } \ +} + +#define RADIX_DO(head,type,field,bits,tdef,key,klen,flm,elem,result,RxD_set) \ +{ \ + struct type *RdX_node = (head)->RdX_root; \ + tdef RdX_mask; \ + int RdX_index; \ + int RdX_size = sizeof(tdef)*8; \ + int RdX_count = 0; \ + \ + if(klen % bits) result = -1; \ + else { \ + RdX_mask = (0x01 << bits)-1; \ + while((RdX_count*bits) < klen) { \ + if(!RdX_node) { \ + result = -2; \ + break; \ + } else { \ + RdX_index = (key >> (RdX_size - bits - (RdX_count * bits))) & RdX_mask;\ + RdX_node = RdX_node->field.RdX_next[RdX_index]; \ + } \ + RdX_count++; \ + } \ + if(!RdX_node) { \ + result = -3; \ + } else { \ + if(RxD_set) { \ + RdX_node->flm = elem; \ + } else { \ + elem = RdX_node->flm; \ + } \ + result = 0; \ + } \ + } \ +} + +#define RADIX_SET(head,type,field,bits,tdef,key,klen,flm,elem,result) \ + RADIX_DO(head,type,field,bits,tdef,key,klen,flm,elem,result,1) +#define RADIX_GET(head,type,field,bits,tdef,key,klen,flm,elem,result) \ + RADIX_DO(head,type,field,bits,tdef,key,klen,flm,elem,result,0) + +#define RADIX_VISIT_ALL(head,type,field,bits,visit,extra) \ +{ \ + int RdX_node_count = (0x1 << bits); \ + struct type *RdX_stack[RdX_node_count]; \ + int RdX_stack_count[RdX_node_count]; \ + int RdX_stack_depth = 0; \ + struct type *RdX_node; \ + int RdX_done = 0; \ + int R = 0; \ + \ + if((RdX_node = (head)->RdX_root)) { \ + while(!RdX_done) { \ + if(RdX_node->field.RdX_next[R]) { \ + RdX_stack[RdX_stack_depth] = RdX_node; \ + RdX_stack_count[RdX_stack_depth] = R + 1; \ + RdX_stack_depth++; \ + RdX_node = RdX_node->field.RdX_next[R]; \ + R = 0; \ + } else { \ + R++; \ + if(R >= RdX_node_count) { \ + if(visit(RdX_node,extra)) { \ + RdX_done = 1; \ + break; \ + } \ + RdX_stack_depth--; \ + if(RdX_stack_depth >= 0) { \ + RdX_node = RdX_stack[RdX_stack_depth]; \ + R = RdX_stack_count[RdX_stack_depth]; \ + } else { \ + RdX_done = 1; \ + } \ + } \ + } \ + } \ + } \ +} + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/ssl.c linux.ac/arch/um/drivers/ssl.c --- linux.vanilla/arch/um/drivers/ssl.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/ssl.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/fs.h" +#include "linux/tty.h" +#include "linux/tty_driver.h" +#include "linux/major.h" +#include "linux/mm.h" +#include "linux/init.h" +#include "asm/termbits.h" +#include "asm/irq.h" +#include "ssl.h" +#include "chan.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +static int ssl_version = 1; + +static struct tty_driver ssl_driver; + +static int ssl_refcount = 0; + +#define NR_PORTS 64 + +static struct tty_struct *ssl_table[NR_PORTS]; +static struct termios *ssl_termios[NR_PORTS]; +static struct termios *ssl_termios_locked[NR_PORTS]; + +static struct ssl { + struct io_chan chan; + int count; + struct tty_struct *tty; +} private[NR_PORTS] = { [0 ... NR_PORTS - 1] = + { PTY_IO_CHAN_INIT(NULL, 0, 1, INIT_STATIC), + 0, NULL } }; + +static void ssl_interrupt(int irq, void *dev, struct pt_regs *unused) +{ + struct ssl *line = dev; + + tty_interrupt(&line->chan, line->tty, line->count); +} + +static int setup_ssl_irq(int fd, void *data) +{ + return(um_request_irq(SSL_IRQ, fd, ssl_interrupt, + SA_INTERRUPT | SA_SHIRQ, "ssl", data)); +} + +void ssl_announce(char *dev_name, int dev) +{ + printk("Serial line %d assigned device '%s'\n", dev, dev_name); +} + +DECLARE_MUTEX(ssl_sem); + +int ssl_open(struct tty_struct *tty, struct file *filp) +{ + int line, err; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + return -ENODEV; + down(&ssl_sem); + if(tty == NULL) panic("NULL tty in ssl_open"); + private[line].tty = tty; + tty->driver_data = &private[line]; + err = open_chan_pair(&private[line].chan, setup_ssl_irq, + &private[line]); + up(&ssl_sem); + if(err){ + printk("Couldn't open serial line %d - errno = %d\n", line, + -err); + up(&ssl_sem); + return(err); + } + private[line].count++; + up(&ssl_sem); + return(0); +} + +static void ssl_close(struct tty_struct *tty, struct file * filp) +{ + int line; + + line = MINOR(tty->device) - tty->driver.minor_start; + private[line].count--; + if(private[line].count == 0) private[line].tty = NULL; +} + +static int ssl_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int line; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + panic("Bad tty in ssl_put_char"); + return(write_chan(&private[line].chan, buf, count)); +} + +static void ssl_put_char(struct tty_struct *tty, unsigned char ch) +{ + int line; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + panic("Bad tty in ssl_put_char"); + write_chan(&private[line].chan, &ch, sizeof(ch)); +} + +static void ssl_flush_chars(struct tty_struct *tty) +{ + return; +} + +static int ssl_write_room(struct tty_struct *tty) +{ + return(16384); +} + +static int ssl_chars_in_buffer(struct tty_struct *tty) +{ + return(0); +} + +static void ssl_flush_buffer(struct tty_struct *tty) +{ + return; +} + +static int ssl_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int ret; + + ret = 0; + switch(cmd){ + case TCGETS: + case TCSETS: + case TCFLSH: + case TCSETSF: + case TCSETSW: + case TCGETA: + ret = -ENOIOCTLCMD; + break; + default: + printk("Unimplemented ioctl in ssl_ioctl : 0x%x\n", cmd); + ret = -ENOIOCTLCMD; + break; + } + return(ret); +} + +static void ssl_throttle(struct tty_struct * tty) +{ + printk("Someone should implement ssl_throttle\n"); +} + +static void ssl_unthrottle(struct tty_struct * tty) +{ + printk("Someone should implement ssl_unthrottle\n"); +} + +static void ssl_set_termios(struct tty_struct *tty, + struct termios *old_termios) +{ +} + +static void ssl_stop(struct tty_struct *tty) +{ + printk("Someone should implement ssl_stop\n"); +} + +static void ssl_start(struct tty_struct *tty) +{ + printk("Someone should implement ssl_start\n"); +} + +void ssl_hangup(struct tty_struct *tty) +{ +} + +extern void tty_register_devfs (struct tty_driver *driver, unsigned int flags, + unsigned int minor); +extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor); + +int ssl_init(void) +{ + int i; + + for(i=0;ichan, term->tty, term->count); +} + +DECLARE_MUTEX(stdio_sem); + +static int setup_console_irq(int fd, void *data) +{ + return(um_request_irq(CONSOLE_IRQ, fd, console_interrupt, + SA_INTERRUPT | SA_SHIRQ, "console", data)); +} + +static int open_console(int line, struct tty_struct *tty) +{ + int err; + + down(&stdio_sem); + err = open_chan_pair(&vts[line].chan, setup_console_irq, &vts[line]); + if(err < 0){ + printk("Failed to open virtual console %d, errno = %d\n", + line, err); + up(&stdio_sem); + return(err); + } + vts[line].count++; + vts[line].tty = tty; + up(&stdio_sem); + return(0); +} + +static int con_open(struct tty_struct * tty, struct file * filp) +{ + int line, ret; + + line = MINOR(tty->device) - tty->driver.minor_start; + ret = open_console(line, tty); + update_console_size(CHAN_IN_FD(vts[line].chan), &tty->winsize.ws_row, + &tty->winsize.ws_col); + return(ret); +} + +static void con_close(struct tty_struct * tty, struct file * filp) +{ + int line; + + line = MINOR(tty->device) - tty->driver.minor_start; + vts[line].count--; +} + +static int con_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int line; + + line = MINOR(tty->device) - tty->driver.minor_start; + return(write_chan(&vts[line].chan, buf, count)); +} + +static int write_room(struct tty_struct * tty) +{ + return(1024); +} + +static void set_termios(struct tty_struct *tty, struct termios * old) +{ +} + +static int chars_in_buffer(struct tty_struct *tty) +{ + return(0); +} + +extern void tty_register_devfs(struct tty_driver *driver, unsigned int flags, + unsigned int minor); +extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor); + +int stdio_init(void) +{ + int i; + + for(i=0;iindex].chan)); + write_chan(&vts[console->index].chan, string, len); + raw(CHAN_OUT_FD(vts[console->index].chan), 0); +} + +static kdev_t console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, c->index); +} + +static int console_setup(struct console *co, char *options) +{ + return(0); +} + +static struct console stdiocons = { + "tty", + console_write, + NULL, + console_device, + NULL, + NULL, + console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + +void stdio_console_init(void) +{ + save_console_flags(); + register_console(&stdiocons); +} + +void stdio_announce(char *dev_name, int dev) +{ + printk("Virtual console %d assigned device '%s'\n", dev, dev_name); +} + +static struct chan_opts opts = { + announce: stdio_announce, + dev: -1, + xterm_title: "Virtual Console #%d", + raw_pty: 1 +}; + +static int console_chan_setup(char *str) +{ + int i, n; + char *end; + + if(*str == '=') n = -1; + else { + n = simple_strtoul(str, &end, 0); + if(*end != '='){ + printk("console_chan_setup failed to parse \"%s\"\n", + str); + return(1); + } + str = end; + } + str++; + if(n == -1){ + for(i=0;i +#include +#include +#include +#include +#include +#include +#include +#include "stdio_console.h" +#include "user_util.h" +#include "kern_util.h" +#include "user.h" + +struct termios cooked_tt; + +void save_console_flags(void) +{ + if(isatty(0)) tcgetattr(0, &cooked_tt); + else if(isatty(1)) tcgetattr(1, &cooked_tt); + else tcgetattr(2, &cooked_tt); +} + +void cooked(int fd) +{ + tcsetattr(fd, TCSADRAIN, &cooked_tt); +} + +void update_console_size(int fd, unsigned short *rows_out, + unsigned short *cols_out) +{ + struct winsize size; + + if(ioctl(fd, TIOCGWINSZ, &size) == 0){ + *rows_out = size.ws_row; + *cols_out = size.ws_col; + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/ubd.c linux.ac/arch/um/drivers/ubd.c --- linux.vanilla/arch/um/drivers/ubd.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/ubd.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,549 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "ubd_user.h" +#define MAJOR_NR UBD_MAJOR +#include "linux/blk.h" +#include "linux/blkdev.h" +#include "linux/hdreg.h" +#include "linux/init.h" +#include "linux/devfs_fs_kernel.h" +#include "linux/cdrom.h" +#include +#include "asm/segment.h" +#include "asm/uaccess.h" +#include "asm/irq.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +static int ubd_open(struct inode * inode, struct file * filp); +static int ubd_release(struct inode * inode, struct file * file); +static int ubd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg); + +#define MAX_DEV (8) + +static int blk_sizes[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = BLOCK_SIZE }; + +static int hardsect_sizes[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = BLOCK_SIZE }; + +static int sizes[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = 0 }; + +static struct block_device_operations ubd_blops = { + open: ubd_open, + release: ubd_release, + ioctl: ubd_ioctl, +}; + +static struct hd_struct ubd_part[MAX_DEV] = +{ [ 0 ... MAX_DEV - 1 ] = { 0, 0, 0 } }; + +static int fake_major = 0; + +static struct gendisk ubd_gendisk = { + MAJOR_NR, /* Major number */ + "ubd", /* Major name */ + 0, /* Bits to shift to get real from partition */ + 1, /* Number of partitions per real */ + ubd_part, /* hd struct */ + sizes, /* block sizes */ + MAX_DEV, /* number */ + NULL, /* internal */ + NULL, /* next */ + &ubd_blops, /* file operations */ +}; + +static struct gendisk fake_gendisk = { + 0, /* Major number */ + "ubd", /* Major name */ + 0, /* Bits to shift to get real from partition */ + 1, /* Number of partitions per real */ + ubd_part, /* hd struct */ + sizes, /* block sizes */ + MAX_DEV, /* number */ + NULL, /* internal */ + NULL, /* next */ + &ubd_blops, /* file operations */ +}; + +#ifdef CONFIG_BLK_DEV_UBD_SYNC +#define OPEN_FLAGS O_RDWR | O_SYNC +#else +#define OPEN_FLAGS O_RDWR +#endif + +struct { + char *file; + int is_dir; + int count; + int fd; + __u64 size; + int boot_openflags; + int openflags; +} ubd_dev[MAX_DEV] = { { "root_fs", 0, 0, -1, 0, OPEN_FLAGS, OPEN_FLAGS }, + [ 1 ... MAX_DEV - 1 ] = + { NULL, 0, 0, -1, -1, OPEN_FLAGS, OPEN_FLAGS } }; + +static struct hd_driveid ubd_id = { + cyls: 0, + heads: 128, + sectors: 32, +}; + +static int fake_ide = 0; +static struct proc_dir_entry *proc_ide_root = NULL; +static struct proc_dir_entry *proc_ide = NULL; + +static void make_proc_ide(void) +{ + proc_ide_root = proc_mkdir("ide", 0); + proc_ide = proc_mkdir("ide0", proc_ide_root); +} + +static int proc_ide_read_media(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int len; + + strcpy(page, "disk\n"); + len = strlen("disk\n"); + len -= off; + if (len < count){ + *eof = 1; + if (len <= 0) return 0; + } + else len = count; + *start = page + off; + return len; + +} + +static void make_ide_entries(char *dev_name) +{ + struct proc_dir_entry *dir, *ent; + char name[64]; + + if(!fake_ide) return; + if(proc_ide_root == NULL) make_proc_ide(); + dir = proc_mkdir(dev_name, proc_ide); + ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir); + if(!ent) return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_ide_read_media; + ent->write_proc = NULL; + sprintf(name,"ide0/%s", dev_name); + proc_symlink(dev_name, proc_ide_root, name); +} + +static int fake_ide_setup(char *str) +{ + fake_ide = 1; + return(1); +} + +__setup("fake_ide", fake_ide_setup); + +static int ubd_setup(char *str) +{ + int n; + int sync, perm = O_RDWR; + + n = *str++; + if(n == '='){ + char *end; + int major; + + if(!strcmp(str, "sync")){ + sync = 1; + return(1); + } + major = simple_strtoul(str, &end, 0); + if(*end != '\0'){ + printk("ubd_setup : didn't parse major number\n"); + return(1); + } + fake_gendisk.major = major; + fake_major = major; + printk("Setting extra ubd major number to %d\n", major); + return(1); + } + if(n < '0'){ + printk("ubd_setup : index out of range\n"); + return(1); + } + n -= '0'; + if(n >= MAX_DEV){ + printk("ubd_setup : index out of range\n"); + return(1); + } + sync = ubd_dev[n].boot_openflags & O_SYNC; + if (*str == 'r') { + perm = O_RDONLY; + str++; + } + if (*str == 's') { + sync = O_SYNC; + str++; + } + if(*str++ != '='){ + printk("ubd_setup : Expected '='\n"); + return(1); + } + ubd_dev[n].file = str; + ubd_dev[n].boot_openflags = perm | sync; + return(1); +} + +__setup("ubd", ubd_setup); + +static int fakehd(char *str) +{ + printk("fakehd : Changing ubd_gendisk.major_name to \"hd\".\n"); + ubd_gendisk.major_name = "hd"; + return(1); +} + +__setup("fakehd", fakehd); + +static void do_ubd_request(request_queue_t * q); + +int thread_fds[2] = { -1, -1 }; + +int intr_count = 0; + +extern int errno; + +#ifdef CONFIG_SMP +#error end_request needs some locking +#endif + +static void ubd_finish(void) +{ + int nsect; + + nsect = CURRENT->current_nr_sectors; + CURRENT->sector += nsect; + CURRENT->buffer += nsect << 9; + CURRENT->errors = 0; + CURRENT->nr_sectors -= nsect; + CURRENT->current_nr_sectors = 0; + end_request(1); +} + +static void ubd_handler(void) +{ + struct io_thread_req req; + + DEVICE_INTR = NULL; + intr_count++; + if(read_ubd_fs(thread_fds[0], &req, sizeof(req)) != sizeof(req)){ + printk("Pid %d - spurious interrupt in ubd_handler, " + "errno = %d\n", getpid(), errno); + return; + } + if((req.offset != ((__u64) (CURRENT->sector)) << 9) || + (req.length != (CURRENT->current_nr_sectors) << 9)) + panic("I/O op mismatch"); + ubd_finish(); + reactivate_fd(thread_fds[0]); + if (!QUEUE_EMPTY) do_ubd_request(NULL); +} + +static void ubd_intr(int irq, void *dev, struct pt_regs *unused) +{ + ubd_handler(); +} + +static int io_pid = -1; + +void kill_io_thread(void) +{ + if(io_pid != -1) kill(io_pid, SIGKILL); +} + +__exitcall(kill_io_thread); + +int sync = 0; + +int ubd_init(void) +{ + unsigned long stack; + int i, err; + char name[6], dev_name[sizeof("ubd0x")]; + devfs_handle_t devfs_handle; + request_queue_t *q; + + devfs_handle = devfs_mk_dir (NULL, "ubd", NULL); + if (devfs_register_blkdev(MAJOR_NR, "ubd", &ubd_blops)) { + printk("ubd: unable to get major %d\n", MAJOR_NR); + return -1; + } + q = BLK_DEFAULT_QUEUE(MAJOR_NR); + blk_init_queue(q, DEVICE_REQUEST); + elevator_init(&q->elevator, ELEVATOR_NOOP); + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ + blksize_size[MAJOR_NR] = blk_sizes; + hardsect_size[MAJOR_NR] = hardsect_sizes; + ubd_gendisk.next = gendisk_head; + gendisk_head = &ubd_gendisk; + if (fake_major != 0){ + if(devfs_register_blkdev(fake_major, "ubd", &ubd_blops)) { + printk("ubd: unable to get major %d\n", fake_major); + return -1; + } + blk_init_queue(BLK_DEFAULT_QUEUE(fake_major), DEVICE_REQUEST); + read_ahead[fake_major] = 8; /* 8 sector (4kB) read-ahead */ + blksize_size[fake_major] = blk_sizes; + fake_gendisk.next = gendisk_head; + gendisk_head = &fake_gendisk; + } + for(i=0;ii_rdev); + if(n > MAX_DEV) + return -ENODEV; + if(ubd_is_dir(ubd_dev[n].file)){ + ubd_dev[n].is_dir = 1; + return(0); + } + ubd_dev[n].openflags = ubd_dev[n].boot_openflags; + if(ubd_dev[n].count == 0){ + if((ubd_dev[n].fd = open_ubd_fs(ubd_dev[n].file, + &ubd_dev[n].openflags)) < 0){ + printk("ubd%d: Can't open \"%s\": errno = %d\n", n, + ubd_dev[n].file, -ubd_dev[n].fd); + } + } + if(ubd_dev[n].fd < 0) + return -ENODEV; + ubd_dev[n].count++; + if ((filp->f_mode & FMODE_WRITE) && + ((ubd_dev[n].openflags & ~O_SYNC) == O_RDONLY)){ + if(--ubd_dev[n].count == 0) close_fd(ubd_dev[n].fd); + return -EROFS; + } + return(0); +} + +static int ubd_release(struct inode * inode, struct file * file) +{ + int n; + + n = DEVICE_NR(inode->i_rdev); + if(n > MAX_DEV) + return -ENODEV; + if(--ubd_dev[n].count == 0) close_fd(ubd_dev[n].fd); + return(0); +} + +static void do_one_request(void) +{ + struct io_thread_req req; + int block, nsect, dev, again; + + if(CURRENT->rq_status == RQ_INACTIVE) return; + if (DEVICE_INTR) return; + do { + again = 0; + INIT_REQUEST; + block = CURRENT->sector; + nsect = CURRENT->current_nr_sectors; + dev = MINOR(CURRENT->rq_dev); + if(ubd_dev[dev].is_dir){ + strcpy(CURRENT->buffer, "HOSTFS:"); + strcat(CURRENT->buffer, ubd_dev[dev].file); + end_request(1); + return; + } + req.read = (CURRENT->cmd == READ); + req.fd = ubd_dev[dev].fd; + req.offset = ((__u64) block) << 9; + req.length = nsect << 9; + req.buffer = CURRENT->buffer; + req.req = CURRENT; + if((req.offset >= ubd_dev[dev].size) && + (ubd_dev[dev].size != -1)){ + end_request(0); + again = 1; + } + } while(again); + if(thread_fds[0] == -1){ + do_io(&req); + ubd_finish(); + } + else { + SET_INTR(ubd_handler); + write_ubd_fs(thread_fds[1], (char *) &req, sizeof(req)); + } +} + +static void do_ubd_request(request_queue_t * q) +{ + if(thread_fds[0] == -1){ + while(!QUEUE_EMPTY){ + do_one_request(); + } + } + else { + do_one_request(); + } +} + +static int ubd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct hd_geometry *loc = (struct hd_geometry *) arg; + int dev, err; + + if ((!inode) || !(inode->i_rdev)) + return -EINVAL; + dev = DEVICE_NR(inode->i_rdev); + if (dev > MAX_DEV) + return -EINVAL; + switch (cmd) { + struct hd_geometry g; + struct cdrom_volctrl volume; + case HDIO_GETGEO: + if (!loc) return -EINVAL; + g.heads = 128; + g.sectors = 32; + g.cylinders = ubd_dev[dev].size / (128 * 32); + g.start = 2; + return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0; + case BLKRASET: + if(!suser()) return -EACCES; + if(arg > 0xff) return -EINVAL; + read_ahead[MAJOR(inode->i_rdev)] = arg; + return 0; + case BLKRAGET: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + return 0; + case BLKGETSIZE: /* Return device size */ + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + put_user(ubd_dev[dev].size >> 9, (long *) arg); + return 0; + case BLKFLSBUF: + if(!suser()) return -EACCES; + return 0; + + case BLKRRPART: /* Re-read partition tables */ + return 0; /* revalidate_hddisk(inode->i_rdev, 1); */ + + case HDIO_SET_UNMASKINTR: + if (!suser()) return -EACCES; + if ((arg > 1) || (MINOR(inode->i_rdev) & 0x3F)) + return -EINVAL; + return 0; + + case HDIO_GET_UNMASKINTR: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + return 0; + + case HDIO_GET_MULTCOUNT: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + return 0; + + case HDIO_SET_MULTCOUNT: + if (!suser()) return -EACCES; + if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL; + return 0; + + case HDIO_GET_IDENTITY: + ubd_id.cyls = ubd_dev[dev].size / (128 * 32); + if (copy_to_user((char *) arg, (char *) &ubd_id, + sizeof(ubd_id))) + return -EFAULT; + return 0; + + case CDROMVOLREAD: + if(copy_from_user(&volume, (char *) arg, sizeof(volume))) + return -EFAULT; + volume.channel0 = 255; + volume.channel1 = 255; + volume.channel2 = 255; + volume.channel3 = 255; + if(copy_from_user((char *) arg, &volume, sizeof(volume))) + return -EFAULT; + return 0; + + default: + return -EINVAL; + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/ubd_user.c linux.ac/arch/um/drivers/ubd_user.c --- linux.vanilla/arch/um/drivers/ubd_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/ubd_user.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "user.h" +#include "ubd_user.h" + +extern void panic(char *fmt, ...); + +int open_ubd_fs(char *file, int *openflags) +{ + int fd; + + if((fd = open(file, *openflags, 0)) < 0){ + if((*openflags != O_RDWR) || + ((errno != EROFS) && (errno != EACCES))) return(-errno); + *openflags = O_RDONLY; + if((fd = open(file, *openflags, 0)) < 0) return(-errno); + } + return(fd); +} + +int read_ubd_fs(int fd, void *buffer, int len) +{ + return(read(fd, buffer, len)); +} + +int write_ubd_fs(int fd, char *buffer, int len) +{ + return(write(fd, buffer, len)); +} + +int ubd_is_dir(char *file) +{ + struct stat buf; + + if(stat(file, &buf) < 0) return(0); + return(S_ISDIR(buf.st_mode)); +} + +int do_io(struct io_thread_req *req) +{ + int n; + + if(lseek(req->fd, req->offset, SEEK_SET) < 0){ + printk("do_io - lseek failed : errno = %d\n", + errno); + return(-1); + } + if(req->read){ + n = read(req->fd, req->buffer, req->length); + if(n != req->length) + memset(&req->buffer[n], 0, req->length - n); + else if(n <= 0){ + printk("do_io - read returned %d : " + "errno = %d\n", n, errno); + return(-1); + } + } + else { + n = write(req->fd, req->buffer, req->length); + if(n != req->length){ + printk("do_io - write returned %d : " + "errno = %d\n", n, errno); + return(-1); + } + } + return(0); +} + +int kernel_fds[2] = { -1, -1 }; + +int io_count = 0; + +int io_thread(void *arg) +{ + struct io_thread_req req; + int n; + + while(1){ + n = read(kernel_fds[0], &req, sizeof(req)); + if(n < 0) printk("io_thread - read returned %d, errno = %d\n", + n, errno); + else if(n < sizeof(req)){ + printk("io_thread - short read : length = %d\n", n); + continue; + } + io_count++; + do_io(&req); + if(write(kernel_fds[1], &req, sizeof(req)) != sizeof(req)) + printk("io_thread - write failed, errno = %d\n", + errno); + } +} + +int start_io_thread(unsigned long sp, int *fds_out) +{ + int pid; + + if((kernel_fds[0] = get_pty()) < 0) return(-1); + raw(kernel_fds[0], 1); + if((fds_out[0] = open(ptsname(kernel_fds[0]), O_RDWR)) < 0){ + printk("Couldn't open tty for slip line\n"); + return(-1); + } + fds_out[1] = fds_out[0]; + kernel_fds[1] = kernel_fds[0]; + pid = clone(io_thread, (void *) sp, CLONE_VM | CLONE_FILES | SIGCHLD, + NULL); + if(pid < 0){ + printk("start_io_thread - clone failed : errno = %d\n", errno); + return(-errno); + } + return(pid); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/umn_kern.c linux.ac/arch/um/drivers/umn_kern.c --- linux.vanilla/arch/um/drivers/umn_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/umn_kern.c Sun May 13 19:44:45 2001 @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/skbuff.h" +#include "linux/if_arp.h" +#include "linux/spinlock.h" +#include "umn.h" +#include "user_util.h" +#include "kern.h" +#include "kern_util.h" + +struct umn { + int used; + int tty_fd; + int slave; + int slipno; + int rcount; + int esc; + char rbuff[3000]; + int buffsize; + struct net_device dev; + char *ptp_addr; + spinlock_t lock; +}; + +struct umn umn = { 0, -1, -1, -1, -1, 0, { }, 0, { }, "192.168.0.254", + SPIN_LOCK_UNLOCKED }; + +static int xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&umn.lock, flags); + ret = umn_send_packet(umn.tty_fd, skb->data, skb->len); + dev_kfree_skb(skb); + spin_unlock_irqrestore(&umn.lock, flags); + return(ret); +} + +void umn_rcv(char *data, int len) +{ + struct sk_buff *skb; + + skb = dev_alloc_skb(len); + memcpy(skb_put(skb, len), data, len); + skb->mac.raw = skb->data; + skb->dev = &umn.dev; + skb->protocol = htons(ETH_P_IP); + netif_rx(skb); +} + +/* SLIP protocol characters. */ +#define END 0300 /* indicates end of frame */ +#define ESC 0333 /* indicates byte stuffing */ +#define ESC_END 0334 /* ESC ESC_END means END 'data' */ +#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ + +void slip_unesc(unsigned char s) +{ + + switch(s) { + case END: + if (umn.rcount > 2) { + umn_rcv(umn.rbuff, umn.rcount); + } + umn.esc = 0; + umn.rcount = 0; + return; + + case ESC: + umn.esc = 1; + return; + case ESC_ESC: + if(umn.esc){ + umn.esc = 0; + s = ESC; + } + break; + case ESC_END: + if(umn.esc){ + umn.esc = 0; + s = END; + } + break; + } + if (umn.rcount < umn.buffsize) { + umn.rbuff[umn.rcount++] = s; + return; + } + printk("umn receive overflow\n"); +} + +spinlock_t umn_lock = SPIN_LOCK_UNLOCKED; + +void umn_handler(int irq, void *dev, struct pt_regs *unused) +{ + umn_read(umn.tty_fd); + reactivate_fd(umn.tty_fd); +} + +static int umn_open(struct net_device *dev) +{ + int err; + + spin_lock(&umn_lock); + if(umn.used){ + spin_unlock(&umn_lock); + return(-ENXIO); + } + umn.used = 1; + spin_unlock(&umn_lock); + umn.tty_fd = open_umn_tty(&umn.slave, &umn.slipno); + umn.rcount = 0; + umn.esc = 0; + umn.buffsize = sizeof(umn.rbuff)/sizeof(umn.rbuff[0]); + if((err = um_request_irq(UMN_IRQ, umn.tty_fd, umn_handler, + SA_INTERRUPT, "umn", &umn)) != 0){ + printk("umn_init : request_irq failed - errno = %d\n", -err); + return(err); + } + if(umn.tty_fd == -1) return(-1); + else return(0); +} + +static int umn_close (struct net_device *dev) +{ + spin_lock(&umn_lock); + if(!umn.used){ + spin_unlock(&umn_lock); + return(-ENXIO); + } + umn.used = 0; + spin_unlock(&umn_lock); + close_umn_tty(umn.tty_fd, umn.slave); + free_irq(UMN_IRQ, &umn); + return(0); +} + +static int umn_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) +{ + printk("umn ioctl = %d\n", cmd); + if((cmd >= SIOCDEVPRIVATE) && (cmd <= SIOCDEVPRIVATE + 15)) + return(-EOPNOTSUPP); + return(-EOPNOTSUPP); +} + +static int set_addr(struct net_device *dev, void *addr) +{ + struct sockaddr *sa; + char in_addr[sizeof("255.255.255.255")]; + unsigned char *eth_addr; + + sa = addr; + eth_addr = (unsigned char *) sa->sa_data; + sprintf(in_addr, "%d.%d.%d.%d", eth_addr[0], eth_addr[1], + eth_addr[2], eth_addr[3]); + return(set_umn_addr(umn.slave, in_addr, umn.ptp_addr)); +} + +static int init_dev(struct net_device *dev) +{ + dev->mtu = 1500; + dev->hard_start_xmit = xmit; + dev->open = umn_open; + dev->stop = umn_close; + dev->get_stats = NULL; + dev->do_ioctl = umn_ioctl; + dev->hard_header_len = 0; + dev->addr_len = 4; + dev->type = ARPHRD_ETHER; + dev->tx_queue_len = 256; + dev->set_mac_address = set_addr; + dev_init_buffers(dev); + dev->flags = IFF_NOARP; + return 0; +} + +int __init umn_init(void) +{ + struct net_device *d; + + d = &umn.dev; + strncpy(d->name, "umn", IFNAMSIZ); + d->init = init_dev; + if(register_netdev(d)) + printk("Couldn't initialize umn\n"); + return(0); +} + +__initcall(umn_init); + +static int umn_setup(char *str) +{ + if(*str++ != '='){ + printk("umn_setup : Expected '='\n"); + return(1); + } + umn.ptp_addr = str; + return(1); +} + +__setup("umn", umn_setup); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/drivers/umn_user.c linux.ac/arch/um/drivers/umn_user.c --- linux.vanilla/arch/um/drivers/umn_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/drivers/umn_user.c Thu Apr 19 14:32:21 2001 @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "umn.h" +#include "user_util.h" +#include "kern_util.h" +#include "user.h" + +char in_buff[3000]; +char out_buff[3000]; + +/* SLIP protocol characters. */ +#define END 0300 /* indicates end of frame */ +#define ESC 0333 /* indicates byte stuffing */ +#define ESC_END 0334 /* ESC ESC_END means END 'data' */ +#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ + +static int slip_esc(unsigned char *s, unsigned char *d, int len) +{ + unsigned char *ptr = d; + unsigned char c; + + /* + * Send an initial END character to flush out any + * data that may have accumulated in the receiver + * due to line noise. + */ + + *ptr++ = END; + + /* + * For each byte in the packet, send the appropriate + * character sequence, according to the SLIP protocol. + */ + + while (len-- > 0) { + switch(c = *s++) { + case END: + *ptr++ = ESC; + *ptr++ = ESC_END; + break; + case ESC: + *ptr++ = ESC; + *ptr++ = ESC_ESC; + break; + default: + *ptr++ = c; + break; + } + } + *ptr++ = END; + return (ptr - d); +} + +int umn_send_packet(int fd, void *data, int len) +{ + int actual, n; + + actual = slip_esc(data, out_buff, len); + n = write(fd, out_buff, actual); + if(n == actual) return(0); + else return(1); +} + +void umn_read(int fd) +{ + int i, n; + + while(1){ + n = read(fd, in_buff, sizeof(in_buff)/sizeof(in_buff[0])); + if(n == 0) printk("umn_read hit EOF\n"); + else if(n < 0){ + if(errno == EIO){ + close(fd); + return; + } + else if((errno == EBADF) || (errno == EAGAIN)) return; + printk("umn_read had error, errno = %d\n", errno); + } + else { + for(i=0;itramp_arg, error[256]; + + execvp(argv[0], argv); + sprintf(error, "exec of %s failed, errno = %d\n", argv[0], errno); + write(args->fd, error, strlen(error)); + return(1); +} + +static int run_ifconfig(char *prog, void *args, char *error_out, + int error_len) +{ + unsigned long stack; + int pid, status, fds[2], n; + struct helper_arg arg; + + stack = alloc_stack(); + pipe(fds); + arg.tramp_arg = args; + arg.fd = fds[1]; + pid = clone(ifconfig_addr_tramp, (void *) stack_sp(stack), SIGCHLD, + &arg); + close(fds[1]); + if(pid < 0){ + printk("clone of \"%s\" failed\n", prog); + return(-1); + } + pid = waitpid(pid, &status, 0); + n = read(fds[0], error_out, error_len - 1); + error_out[n] = '\0'; + if(pid < 0){ + printk("wait for \"%s\" failed - errno = %d\n", prog, errno); + return(-1); + } + if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)){ + close(fds[0]); + return(-1); + } + close(fds[0]); + free_stack(stack); + return(0); +} + +int open_umn_tty(int *slave_out, int *slipno_out) +{ + int sfd, mfd; + + if((mfd = get_pty()) < 0){ + printk("umn : Failed to open pty\n"); + return(-1); + } + if((sfd = open(ptsname(mfd), O_RDWR)) < 0){ + printk("Couldn't open tty for slip line\n"); + return(-1); + } + if(set_up_tty(sfd)) return(-1); + *slave_out = sfd; + return(mfd); +} + +int slipify(int fd) +{ + int disc, sencap, n; + + disc = N_SLIP; + sencap = 0; + if((n = ioctl(fd, TIOCSETD, &disc)) < 0){ + printk("Failed to set slip line discipline - errno = %d\n", + errno); + return(-1); + } + if(ioctl(fd, SIOCSIFENCAP, &sencap) < 0){ + printk("Couldn't set slip encapsulation - errno = %d\n", + errno); + return(-1); + } + return(n); +} + +int set_umn_addr(int fd, char *addr, char *ptp_addr) +{ + char slip_name[sizeof("slxxxx")], fd_name[sizeof("nnnnn")]; + char errors[3][256]; + char *helper_args[] = { "umn_helper", fd_name, ptp_addr, addr, + NULL }; + char *ifconfig_args[] = { "um_ifconfig", slip_name, ptp_addr, + "pointopoint", addr, "up", NULL }; + int slipno; + + sprintf(fd_name, "%d", fd); + + /* Try running umn_helper first */ + if(!run_ifconfig(helper_args[0], helper_args, errors[0], + sizeof(errors[0]))) + return(0); + + /* Then try setting up slip ourselves and then ifconfiging it + * with the old setuid ifconfig - this is for old 2.2 systems + * that don't have the current permissions checking in the slip + * driver. + */ + slipno = slipify(fd); + sprintf(slip_name, "sl%d", slipno); + if((slipno != -1) && + !run_ifconfig(ifconfig_args[0], ifconfig_args, errors[1], + sizeof(errors[1]))) + return(0); + + /* Now the only hope is that we're running as root and then we + * can run ifconfig directly + */ + if(getuid() == 0){ + ifconfig_args[0] = "ifconfig"; + if(!run_ifconfig(ifconfig_args[0], ifconfig_args, errors[2], + sizeof(errors[2]))) + return(0); + } + else errors[2][0] = '\0'; + printk("umn - umn_helper failed : %s\n", errors[0]); + printk("umn - um_ifconfig failed : %s\n", errors[1]); + if(errors[2][0] != '\0'){ + printk("umn - ifconfig failed : %s\n", errors[2]); + } + return(1); +} + +void close_umn_tty(int master, int slave) +{ + close(slave); + close(master); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/fs/Makefile linux.ac/arch/um/fs/Makefile --- linux.vanilla/arch/um/fs/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/fs/Makefile Mon Apr 9 23:25:02 2001 @@ -0,0 +1,16 @@ +# +# Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +O_TARGET := fs.o + +subdir-$(CONFIG_HOSTFS) = hostfs + +MOD_SUB_DIRS := $(subdir-m) +SUB_DIRS := $(subdir-y) + +obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o)) +obj-m += $(join $(subdir-m),$(subdir-m:%=/%.o)) + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/fs/hostfs/Makefile linux.ac/arch/um/fs/hostfs/Makefile --- linux.vanilla/arch/um/fs/hostfs/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/fs/hostfs/Makefile Sun May 13 20:23:29 2001 @@ -0,0 +1,26 @@ +# +# Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +EXTRA_CFLAGS += -I../../include +USER_CFLAGS := $(patsubst -I%,,$(CFLAGS)) +USER_CFLAGS := $(patsubst -D__KERNEL__,,$(USER_CFLAGS)) + +O_TARGET := +obj-y := + +ifneq ($(CONFIG_HOSTFS), n) + O_TARGET := hostfs.o + obj-y := hostfs_kern.o hostfs_user.o + CFLAGS_hostfs_kern.o := $(CFLAGS) + CFLAGS_hostfs_user.o := $(USER_CFLAGS) +endif + +ifeq ($(CONFIG_HOSTFS), m) + obj-m := $(O_TARGET) +endif + +override CFLAGS = + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/fs/hostfs/hostfs.h linux.ac/arch/um/fs/hostfs/hostfs.h --- linux.vanilla/arch/um/fs/hostfs/hostfs.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/fs/hostfs/hostfs.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,68 @@ +#ifndef __UM_FS_HOSTFS +#define __UM_FS_HOSTFS + +#define HOSTFS_FILE 1 +#define HOSTFS_DIR 2 +#define HOSTFS_SYMLINK 3 + +/* These are exactly the same definitions as in fs.h, but the names are + * changed so that this file can be included in both kernel and user files. + */ + +#define HOSTFS_ATTR_MODE 1 +#define HOSTFS_ATTR_UID 2 +#define HOSTFS_ATTR_GID 4 +#define HOSTFS_ATTR_SIZE 8 +#define HOSTFS_ATTR_ATIME 16 +#define HOSTFS_ATTR_MTIME 32 +#define HOSTFS_ATTR_CTIME 64 +#define HOSTFS_ATTR_ATIME_SET 128 +#define HOSTFS_ATTR_MTIME_SET 256 +#define HOSTFS_ATTR_FORCE 512 /* Not a change, but a change it */ +#define HOSTFS_ATTR_ATTR_FLAG 1024 + +struct hostfs_iattr { + unsigned int ia_valid; + mode_t ia_mode; + uid_t ia_uid; + gid_t ia_gid; + loff_t ia_size; + time_t ia_atime; + time_t ia_mtime; + time_t ia_ctime; + unsigned int ia_attr_flags; +}; + +extern int stat_file(const char *path, int *dev_out, unsigned long *inode_out, + int *mode_out, int *nlink_out, int *uid_out, + int *gid_out, unsigned long *size_out, + unsigned long *atime_out, unsigned long *mtime_out, + unsigned long *ctime_out, int *blksize_out, + int *blocks_out); +extern int access_file(char *path, int r, int w, int x); +extern int open_file(char *path, int r, int w); +extern int file_type(const char *path); +extern void *open_dir(char *path, int *err_out); +extern char *read_dir(void *stream, unsigned long long *pos, int *len_out); +extern void close_file(void *stream); +extern void close_dir(void *stream); +extern int read_file(int fd, unsigned long long *offset, char *buf, int len); +extern int write_file(int fd, unsigned long long *offset, const char *buf, + int len, int append); +extern int lseek_file(int fd, long long offset, int whence); +extern int file_create(char *name, int ur, int uw, int ux, int gr, + int gw, int gx, int or, int ow, int ox); +extern int set_attr(const char *file, struct hostfs_iattr *attrs); +extern int make_symlink(const char *from, const char *to); +extern int unlink_file(const char *file); +extern int do_mkdir(const char *file, int mode); +extern int do_rmdir(const char *file); +extern int link_file(const char *from, const char *to); +extern int do_readlink(char *file, char *buf, int size); +extern int rename_file(char *from, char *to); +extern int do_statfs(char *root, long *bsize_out, long *blocks_out, + long *bfree_out, long *bavail_out, long *files_out, + long *ffree_out, void *fsid_out, int fsid_size, + long *namelen_out, long *spare_out); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/fs/hostfs/hostfs_kern.c linux.ac/arch/um/fs/hostfs/hostfs_kern.c --- linux.vanilla/arch/um/fs/hostfs/hostfs_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/fs/hostfs/hostfs_kern.c Sun May 13 20:24:53 2001 @@ -0,0 +1,701 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hostfs.h" +#include "kern_util.h" +#include "kern.h" +#include "user_util.h" + +int hostfs_d_delete(struct dentry *dentry) +{ + return(1); +} + +struct dentry_operations hostfs_dentry_ops = { + d_delete: hostfs_d_delete, +}; + +static char *root_ino = "/"; + +#define HOSTFS_SUPER_MAGIC 0x00c0ffee + +static struct inode_operations hostfs_iops; +static struct address_space_operations hostfs_link_aops; + +static char *dentry_name(struct dentry *dentry, int extra) +{ + struct dentry *parent; + char *root, *name; + int len; + + len = 0; + parent = dentry; + while(parent->d_parent != parent){ + len += parent->d_name.len + 1; + parent = parent->d_parent; + } + root = parent->d_inode->u.generic_ip; + len += strlen(root); + name = kmalloc(len + extra + 1, GFP_KERNEL); + if(name == NULL) return(NULL); + + name[len] = '\0'; + parent = dentry; + while(parent->d_parent != parent){ + len -= parent->d_name.len + 1; + name[len] = '/'; + strncpy(&name[len + 1], parent->d_name.name, + parent->d_name.len); + parent = parent->d_parent; + } + strncpy(name, root, strlen(root)); + return(name); +} + +static char *inode_name(struct inode *ino, int extra) +{ + struct dentry *dentry; + + dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias); + return(dentry_name(dentry, extra)); +} + +static int read_name(struct inode *ino, char *name) +{ + /* The non-int inode fields are copied into ints by stat_file and + * then copied into the inode because passing the actual pointers + * in and having them treated as int * breaks on big-endian machines + */ + int err; + + int i_dev, i_mode, i_nlink, i_blksize, i_blocks; + unsigned long i_size; + err = stat_file(name, &i_dev, &ino->i_ino, &i_mode, &i_nlink, + &ino->i_uid, &ino->i_gid, &i_size, &ino->i_atime, + &ino->i_mtime, &ino->i_ctime, &i_blksize, &i_blocks); + if(err) return(err); + ino->i_dev = i_dev; + ino->i_mode = i_mode; + ino->i_nlink = i_nlink; + ino->i_size = i_size; + ino->i_blksize = i_blksize; + ino->i_blocks = i_blocks; + if((ino->i_sb->s_dev == ROOT_DEV) && (ino->i_uid == getuid())) + ino->i_uid = 0; + return(0); +} + +static int read_inode(struct inode *ino) +{ + char *name; + int err; + + name = inode_name(ino, 0); + if(name == NULL) return(-ENOMEM); + err = read_name(ino, name); + kfree(name); + return(err); +} + +void hostfs_delete_inode(struct inode *ino) +{ + if(ino->u.generic_ip) kfree(ino->u.generic_ip); + ino->u.generic_ip = NULL; + clear_inode(ino); +} + +int hostfs_statfs(struct super_block *sb, struct statfs *sf) +{ + int err; + + err = do_statfs(sb->s_root->d_inode->u.generic_ip, &sf->f_bsize, + &sf->f_blocks, &sf->f_bfree, &sf->f_bavail, + &sf->f_files, &sf->f_ffree, &sf->f_fsid, + sizeof(sf->f_fsid), &sf->f_namelen, sf->f_spare); + if(err) return(err); + sf->f_type = HOSTFS_SUPER_MAGIC; + return(0); +} + +static struct super_operations hostfs_sbops = { + put_inode: force_delete, + delete_inode: hostfs_delete_inode, + statfs: hostfs_statfs, +}; + +ssize_t hostfs_write(struct file *file, const char *buf, size_t len, + loff_t *start) +{ + unsigned long page = __get_free_page(GFP_KERNEL); + int ret, one_len, n; + + if(page == 0) return(-ENOMEM); + ret = 0; + while(len > 0){ + one_len = (len < PAGE_SIZE) ? len : PAGE_SIZE; + if(copy_from_user((void *) page, &buf[ret], one_len)){ + ret = -EFAULT; + break; + } + n = write_file((int) file->private_data, start, (void *) page, + one_len, file->f_flags & O_APPEND); + if(n < 0){ + ret = n; + break; + } + ret += n; + len -= n; + if(n < one_len) break; + } + free_page(page); + return(ret); +} + +int hostfs_readdir(struct file *file, void *ent, filldir_t filldir) +{ + void *dir; + char *name; + unsigned long long next; + int error, len; + + name = dentry_name(file->f_dentry, 0); + if(name == NULL) return(-ENOMEM); + dir = open_dir(name, &error); + kfree(name); + if(dir == NULL) return(-error); + next = file->f_pos; + while((name = read_dir(dir, &next, &len)) != NULL){ + error = (*filldir)(ent, name, len, file->f_pos, + file->f_dentry->d_inode->i_ino, + DT_UNKNOWN); + if(error) break; + file->f_pos = next; + } + close_dir(dir); + return(0); +} + +unsigned int hostfs_poll(struct file *file, struct poll_table_struct *table) +{ + not_implemented(); + return(-EINVAL); +} + +int hostfs_ioctl(struct inode *ino, struct file *file, unsigned int code, + unsigned long data) +{ + not_implemented(); + return(-EINVAL); +} + +int hostfs_file_open(struct inode *ino, struct file *file) +{ + char *name; + int r = 0, w = 0, fd; + + if(file->f_mode & FMODE_READ) r = 1; + if(file->f_mode & FMODE_WRITE) w = 1; + name = dentry_name(file->f_dentry, 0); + if(name == NULL) return(-ENOMEM); + fd = open_file(name, r, w); + kfree(name); + if(fd < 0) return(fd); + file->private_data = (void *) fd; + return(0); +} + +int hostfs_dir_open(struct inode *ino, struct file *file) +{ + return(0); +} + +int hostfs_file_release(struct inode *ino, struct file *file) +{ + close_file(file->private_data); + return(0); +} + +int hostfs_dir_release(struct inode *ino, struct file *file) +{ + return(0); +} + +int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + return(0); +} + +int hostfs_fasync(int fd, struct file *file, int on) +{ + not_implemented(); + return(-EINVAL); +} + +static struct file_operations hostfs_file_fops = { + owner: NULL, + read: generic_file_read, + write: hostfs_write, + poll: hostfs_poll, + mmap: generic_file_mmap, + open: hostfs_file_open, + release: hostfs_file_release, + fsync: hostfs_fsync, + fasync: hostfs_fasync +}; + +static struct file_operations hostfs_dir_fops = { + owner: NULL, + readdir: hostfs_readdir, + poll: hostfs_poll, + ioctl: hostfs_ioctl, + open: hostfs_dir_open, + release: hostfs_dir_release, + fsync: hostfs_fsync, + fasync: hostfs_fasync +}; + +int hostfs_writepage(struct page *page) +{ + not_implemented(); + return(-EINVAL); +} + +int hostfs_readpage(struct file *file, struct page *page) +{ + char *buffer; + long long start; + int err = 0; + + start = page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + err = read_file((int) file->private_data, &start, buffer, + PAGE_CACHE_SIZE); + if(err > 0){ + flush_dcache_page(page); + SetPageUptodate(page); + if (PageError(page)) ClearPageError(page); + err = 0; + } + kunmap(page); + UnlockPage(page); + return(err); +} + +int hostfs_prepare_write(struct file *file, struct page *page, unsigned from, + unsigned to) +{ + not_implemented(); + return(-EINVAL); +} + +int hostfs_commit_write(struct file *file, struct page *page, unsigned from, + unsigned to) +{ + not_implemented(); + return(-EINVAL); +} + +static struct address_space_operations hostfs_aops = { + writepage: hostfs_writepage, + readpage: hostfs_readpage, + prepare_write: hostfs_prepare_write, + commit_write: hostfs_commit_write +}; + +static struct inode *get_inode(struct super_block *sb, struct dentry *dentry, + int *error) +{ + struct inode *inode; + char *name; + int type, err = 0; + + inode = get_empty_inode(); + if(inode == NULL) return(NULL); + inode->u.generic_ip = NULL; + if(error) *error = 0; + insert_inode_hash(inode); + if(dentry){ + name = dentry_name(dentry, 0); + if(name == NULL){ + err = -ENOMEM; + goto out; + } + type = file_type(name); + kfree(name); + } + else type = HOSTFS_DIR; + inode->i_sb = sb; + + if(type == HOSTFS_SYMLINK) + inode->i_op = &page_symlink_inode_operations; + else inode->i_op = &hostfs_iops; + + if(type == HOSTFS_DIR) inode->i_fop = &hostfs_dir_fops; + else inode->i_fop = &hostfs_file_fops; + + if(type == HOSTFS_SYMLINK) inode->i_mapping->a_ops = &hostfs_link_aops; + else inode->i_mapping->a_ops = &hostfs_aops; + + return(inode); + out: + iput(inode); + if(error) *error = err; + return(NULL); +} + +int hostfs_create(struct inode *dir, struct dentry *dentry, int mode) +{ + struct inode *inode; + char *name; + int error; + + inode = get_inode(dir->i_sb, dentry, &error); + if(error) return(error); + name = dentry_name(dentry, 0); + if(name == NULL){ + iput(inode); + return(-ENOMEM); + } + error = file_create(name, + mode | S_IRUSR, mode | S_IWUSR, mode | S_IXUSR, + mode | S_IRGRP, mode | S_IWGRP, mode | S_IXGRP, + mode | S_IROTH, mode | S_IWOTH, mode | S_IXOTH); + if(!error) error = read_name(inode, name); + kfree(name); + if(error){ + iput(inode); + return(error); + } + d_instantiate(dentry, inode); + return(0); +} + +struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry) +{ + struct inode *inode; + char *name; + int error; + + inode = get_inode(ino->i_sb, dentry, &error); + if(error != 0) return(ERR_PTR(error)); + name = dentry_name(dentry, 0); + if(name == NULL) return(ERR_PTR(-ENOMEM)); + error = read_name(inode, name); + kfree(name); + if(error){ + iput(inode); + if(error == -ENOENT) inode = NULL; + else return(ERR_PTR(error)); + } + d_add(dentry, inode); + dentry->d_op = &hostfs_dentry_ops; + return(NULL); +} + +static char *inode_dentry_name(struct inode *ino, struct dentry *dentry) +{ + char *file; + int len; + + file = inode_name(ino, dentry->d_name.len + 2); + if(file == NULL) return(NULL); + strcat(file, "/"); + len = strlen(file); + strncat(file, dentry->d_name.name, dentry->d_name.len); + file[len + dentry->d_name.len] = '\0'; + return(file); +} + +int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from) +{ + char *from_name, *to_name; + int err; + + if((from_name = inode_dentry_name(ino, from)) == NULL) + return(-ENOMEM); + to_name = dentry_name(to, 0); + if(to_name == NULL){ + kfree(from_name); + return(-ENOMEM); + } + err = link_file(to_name, from_name); + kfree(from_name); + kfree(to_name); + return(err); +} + +int hostfs_unlink(struct inode *ino, struct dentry *dentry) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = unlink_file(file); + kfree(file); + return(err); +} + +int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = make_symlink(file, to); + kfree(file); + return(err); +} + +int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = do_mkdir(file, mode); + kfree(file); + return(err); +} + +int hostfs_rmdir(struct inode *ino, struct dentry *dentry) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = do_rmdir(file); + kfree(file); + return(err); +} + +int hostfs_mknod(struct inode *ino, struct dentry *dentry, int mode, int dev) +{ + not_implemented(); + return(-EINVAL); +} + +int hostfs_rename(struct inode *from_ino, struct dentry *from, + struct inode *to_ino, struct dentry *to) +{ + char *from_name, *to_name; + int err; + + if((from_name = inode_dentry_name(from_ino, from)) == NULL) + return(-ENOMEM); + if((to_name = inode_dentry_name(to_ino, to)) == NULL){ + kfree(from_name); + return(-ENOMEM); + } + err = rename_file(from_name, to_name); + kfree(from_name); + kfree(to_name); + return(err); +} + +void hostfs_truncate(struct inode *ino) +{ + not_implemented(); +} + +int hostfs_permission(struct inode *ino, int desired) +{ + char *name; + int r = 0, w = 0, x = 0, err; + + if(desired & MAY_READ) r = 1; + if(desired & MAY_WRITE) w = 1; + if(desired & MAY_EXEC) x = 1; + name = inode_name(ino, 0); + if(name == NULL) return(-ENOMEM); + err = access_file(name, r, w, x); + kfree(name); + return(err); +} + +int hostfs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct hostfs_iattr attrs; + char *name; + int err; + + attrs.ia_valid = 0; + if(attr->ia_valid & ATTR_MODE){ + attrs.ia_valid |= HOSTFS_ATTR_MODE; + attrs.ia_mode = attr->ia_mode; + } + if(attr->ia_valid & ATTR_UID){ + attrs.ia_valid |= HOSTFS_ATTR_UID; + attrs.ia_uid = attr->ia_uid; + } + if(attr->ia_valid & ATTR_GID){ + attrs.ia_valid |= HOSTFS_ATTR_GID; + attrs.ia_gid = attr->ia_gid; + } + if(attr->ia_valid & ATTR_SIZE){ + attrs.ia_valid |= HOSTFS_ATTR_SIZE; + attrs.ia_size = attr->ia_size; + } + if(attr->ia_valid & ATTR_ATIME){ + attrs.ia_valid |= HOSTFS_ATTR_ATIME; + attrs.ia_atime = attr->ia_atime; + } + if(attr->ia_valid & ATTR_MTIME){ + attrs.ia_valid |= HOSTFS_ATTR_MTIME; + attrs.ia_mtime = attr->ia_mtime; + } + if(attr->ia_valid & ATTR_CTIME){ + attrs.ia_valid |= HOSTFS_ATTR_CTIME; + attrs.ia_ctime = attr->ia_ctime; + } + if(attr->ia_valid & ATTR_ATIME_SET){ + attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET; + } + if(attr->ia_valid & ATTR_MTIME_SET){ + attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET; + } + name = dentry_name(dentry, 0); + if(name == NULL) return(-ENOMEM); + err = set_attr(name, &attrs); + kfree(name); + return(err); +} + +int hostfs_getattr(struct dentry *dentry, struct iattr *attr) +{ + not_implemented(); + return(-EINVAL); +} + +static struct inode_operations hostfs_iops = { + create: hostfs_create, + lookup: hostfs_lookup, + link: hostfs_link, + unlink: hostfs_unlink, + symlink: hostfs_symlink, + mkdir: hostfs_mkdir, + rmdir: hostfs_rmdir, + mknod: hostfs_mknod, + rename: hostfs_rename, + truncate: hostfs_truncate, + permission: hostfs_permission, + setattr: hostfs_setattr, + getattr: hostfs_getattr, +}; + +int hostfs_link_readpage(struct file *file, struct page *page) +{ + char *buffer, *name; + long long start; + int err; + + start = page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + name = inode_name(page->mapping->host, 0); + if(name == NULL) return(-ENOMEM); + err = do_readlink(name, buffer, PAGE_CACHE_SIZE); + kfree(name); + if(err == 0){ + flush_dcache_page(page); + SetPageUptodate(page); + if (PageError(page)) ClearPageError(page); + } + kunmap(page); + UnlockPage(page); + return(err); +} + +static struct address_space_operations hostfs_link_aops = { + readpage: hostfs_link_readpage, +}; + +static struct super_block *hostfs_read_super_common(struct super_block *sb, + char *data) +{ + struct inode * root_inode; + char *name; + + sb->s_blocksize = 1024; + sb->s_blocksize_bits = 10; + sb->s_magic = HOSTFS_SUPER_MAGIC; + sb->s_op = &hostfs_sbops; + if((data == NULL) || (*((char *) data) == '\0')) data = root_ino; + name = kmalloc(strlen(data) + 1, GFP_KERNEL); + if(name == NULL) return(NULL); + strcpy(name, data); + root_inode = get_inode(sb, NULL, NULL); + if(root_inode == NULL){ + kfree(name); + return(NULL); + } + root_inode->u.generic_ip = name; + sb->s_root = d_alloc_root(root_inode); + if(read_inode(root_inode)){ + kfree(name); + iput(root_inode); + return(NULL); + } + return(sb); +} + +struct super_block *hostfs_read_super(struct super_block *sb, void *data, + int silent) +{ + return(hostfs_read_super_common(sb, data)); +} + +struct super_block *hostfs_root_read_super(struct super_block *sb, void *data, + int silent) +{ + struct buffer_head * bh; + struct super_block *ret = NULL; + kdev_t dev = sb->s_dev; + int blocksize = get_hardsect_size(dev); + + if(blocksize == 0) blocksize = BLOCK_SIZE; + if(!(bh = bread (dev, 0, blocksize))) return NULL; + if(strncmp(bh->b_data, "HOSTFS:", strlen("HOSTFS:"))) goto out; + ret = hostfs_read_super_common(sb, bh->b_data + strlen("HOSTFS:")); + out: + brelse (bh); + return(ret); +} + +DECLARE_FSTYPE(hostfs_type, "hostfs", hostfs_read_super, 0); +DECLARE_FSTYPE_DEV(hostfs_root_type, "root hostfs", hostfs_root_read_super); + +static int __init init_hostfs(void) +{ + return(register_filesystem(&hostfs_type) || + register_filesystem(&hostfs_root_type)); +} + +static void __exit exit_hostfs(void) +{ + unregister_filesystem(&hostfs_type); + unregister_filesystem(&hostfs_root_type); +} + +module_init(init_hostfs) +module_exit(exit_hostfs) + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/fs/hostfs/hostfs_user.c linux.ac/arch/um/fs/hostfs/hostfs_user.c --- linux.vanilla/arch/um/fs/hostfs/hostfs_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/fs/hostfs/hostfs_user.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hostfs.h" +#include "kern_util.h" +#include "user.h" + +int stat_file(const char *path, int *dev_out, unsigned long *inode_out, + int *mode_out, int *nlink_out, int *uid_out, int *gid_out, + unsigned long *size_out, unsigned long *atime_out, + unsigned long *mtime_out, unsigned long *ctime_out, + int *blksize_out, int *blocks_out) +{ + struct stat buf; + + if(lstat(path, &buf) < 0) + return(-errno); + if(dev_out != NULL) *dev_out = buf.st_dev; + if(inode_out != NULL) *inode_out = buf.st_ino; + if(mode_out != NULL) *mode_out = buf.st_mode; + if(nlink_out != NULL) *nlink_out = buf.st_nlink; + if(uid_out != NULL) *uid_out = buf.st_uid; + if(gid_out != NULL) *gid_out = buf.st_gid; + if(size_out != NULL) *size_out = buf.st_size; + if(atime_out != NULL) *atime_out = buf.st_atime; + if(mtime_out != NULL) *mtime_out = buf.st_mtime; + if(ctime_out != NULL) *ctime_out = buf.st_ctime; + if(blksize_out != NULL) *blksize_out = buf.st_blksize; + if(blocks_out != NULL) *blocks_out = buf.st_blocks; + return(0); +} + +int file_type(const char *path) +{ + struct stat buf; + + if(lstat(path, &buf) < 0) return(-errno); + if(S_ISDIR(buf.st_mode)) return(HOSTFS_DIR); + else if(S_ISLNK(buf.st_mode)) return(HOSTFS_SYMLINK); + else return(HOSTFS_FILE); +} + +int access_file(char *path, int r, int w, int x) +{ + int mode = 0; + + if(r) mode = R_OK; + if(w) mode |= W_OK; + if(x) mode |= X_OK; + if(access(path, mode) != 0) return(-errno); + else return(0); +} + +int open_file(char *path, int r, int w) +{ + int mode = 0, fd; + + if(r && !w) mode = O_RDONLY; + else if(!r && w) mode = O_WRONLY; + else if(r && w) mode = O_RDWR; + else panic("Impossible mode in open_file"); + fd = open(path, mode); + if(fd < 0) return(-errno); + else return(fd); +} + +void *open_dir(char *path, int *err_out) +{ + DIR *dir; + + dir = opendir(path); + *err_out = errno; + if(dir == NULL) return(NULL); + return(dir); +} + +char *read_dir(void *stream, unsigned long long *pos, int *len_out) +{ + DIR *dir = stream; + struct dirent *ent; + + seekdir(dir, *pos); + ent = readdir(dir); + if(ent == NULL) return(NULL); + *len_out = strlen(ent->d_name); + *pos = telldir(dir); + return(ent->d_name); +} + +int read_file(int fd, unsigned long long *offset, char *buf, int len) +{ + int n; + + if(lseek(fd, *offset, SEEK_SET) != *offset) + return(-errno); + n = read(fd, buf, len); + if(n < 0) return(-errno); + *offset += n; + return(n); +} + +int write_file(int fd, unsigned long long *offset, const char *buf, int len, + int append) +{ + int n, where, off; + + off = *offset; + where = SEEK_SET; + if(append){ + where = SEEK_END; + off = 0; + } + if((off = lseek(fd, off, where)) == (off_t) -1) + return(-errno); + n = write(fd, buf, len); + if(n < 0) return(-errno); + *offset = off + n; + return(n); +} + +int lseek_file(int fd, long long offset, int whence) +{ + int ret; + + ret = lseek(fd, (int) offset, whence); + if(ret < 0) return(-errno); + return(0); +} + +void close_file(void *stream) +{ + close((int) stream); +} + +void close_dir(void *stream) +{ + closedir(stream); +} + +int file_create(char *name, int ur, int uw, int ux, int gr, + int gw, int gx, int or, int ow, int ox) +{ + int mode, fd; + + mode = 0; + mode |= ur ? S_IRUSR : 0; + mode |= uw ? S_IWUSR : 0; + mode |= ux ? S_IXUSR : 0; + mode |= gr ? S_IRGRP : 0; + mode |= gw ? S_IWGRP : 0; + mode |= gx ? S_IXGRP : 0; + mode |= or ? S_IROTH : 0; + mode |= ow ? S_IWOTH : 0; + mode |= ox ? S_IXOTH : 0; + fd = open(name, O_CREAT, mode); + if(fd < 0) return(-errno); + close(fd); + return(0); +} + +int set_attr(const char *file, struct hostfs_iattr *attrs) +{ + struct utimbuf buf; + int err, ma; + + if(attrs->ia_valid & HOSTFS_ATTR_MODE){ + if(chmod(file, attrs->ia_mode) != 0) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_UID){ + if(chown(file, attrs->ia_uid, -1)) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_GID){ + if(chown(file, -1, attrs->ia_gid)) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_SIZE){ + if(truncate(file, attrs->ia_size)) return(-errno); + } + ma = HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET; + if((attrs->ia_valid & ma) == ma){ + buf.actime = attrs->ia_atime; + buf.modtime = attrs->ia_mtime; + if(utime(file, &buf) != 0) return(-errno); + } + else { + if(attrs->ia_valid & HOSTFS_ATTR_ATIME_SET){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, &buf.modtime, NULL, + NULL, NULL); + if(err != 0) return(err); + buf.actime = attrs->ia_atime; + if(utime(file, &buf) != 0) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_MTIME_SET){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &buf.actime, NULL, NULL, + NULL, NULL); + if(err != 0) return(err); + buf.modtime = attrs->ia_mtime; + if(utime(file, &buf) != 0) return(-errno); + } + } + if(attrs->ia_valid & HOSTFS_ATTR_CTIME) ; + if(attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &attrs->ia_atime, &attrs->ia_mtime, + NULL, NULL, NULL); + if(err != 0) return(err); + } + return(0); +} + +int make_symlink(const char *from, const char *to) +{ + int err; + + err = symlink(to, from); + if(err) return(-errno); + return(0); +} + +int unlink_file(const char *file) +{ + int err; + + err = unlink(file); + if(err) return(-errno); + return(0); +} + +int do_mkdir(const char *file, int mode) +{ + int err; + + err = mkdir(file, mode); + if(err) return(-errno); + return(0); +} + +int do_rmdir(const char *file) +{ + int err; + + err = rmdir(file); + if(err) return(-errno); + return(0); +} + +int link_file(const char *to, const char *from) +{ + int err; + + err = link(to, from); + if(err) return(-errno); + return(0); +} + +int do_readlink(char *file, char *buf, int size) +{ + int err; + + err = readlink(file, buf, size); + if(err < 0) return(-errno); + if(err < size) buf[err] = '\0'; + return(0); +} + +int rename_file(char *from, char *to) +{ + int err; + + err = rename(from, to); + if(err < 0) return(-errno); + return(0); +} + +int do_statfs(char *root, long *bsize_out, long *blocks_out, long *bfree_out, + long *bavail_out, long *files_out, long *ffree_out, + void *fsid_out, int fsid_size, long *namelen_out, + long *spare_out) +{ + struct statfs buf; + int err; + + err = statfs(root, &buf); + if(err < 0) return(-errno); + *bsize_out = buf.f_bsize; + *blocks_out = buf.f_blocks; + *bfree_out = buf.f_bfree; + *bavail_out = buf.f_bavail; + *files_out = buf.f_files; + *ffree_out = buf.f_ffree; + memcpy(fsid_out, &buf.f_fsid, + sizeof(buf.f_fsid) > fsid_size ? fsid_size : + sizeof(buf.f_fsid)); + *namelen_out = buf.f_namelen; + spare_out[0] = buf.f_spare[0]; + spare_out[1] = buf.f_spare[1]; + spare_out[2] = buf.f_spare[2]; + spare_out[3] = buf.f_spare[3]; + spare_out[4] = buf.f_spare[4]; + spare_out[5] = buf.f_spare[5]; + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/chan.h linux.ac/arch/um/include/chan.h --- linux.vanilla/arch/um/include/chan.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/chan.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __TTY_H__ +#define __TTY_H__ + +struct chan { + enum { XTERM, PTY, SOCKET, FD, TTY, PTS, FILE_CHAN, COPY } type; + int fd; + int hung_up; + int opened; + int init_pri; + union { + struct { + int pid; + int line; + char *title; + int raw; + } xterm; + struct { + void (*announce)(char *dev_name, int dev); + int dev; + int raw; + } pty, pts; + struct { + char *dev; + int raw; + void *tty_state; + } tty; + struct { + int port; + int connected; + int pty; + } sock; + } data; +}; + +struct io_chan { + struct chan in; + struct chan out; +}; + +#define INIT_STATIC (0) +#define INIT_ALL (1) +#define INIT_ONE (2) + +#define PTY_CHAN_INIT(announce, dev, raw, pri) \ + { PTY, -1, 1, 0, pri, { pty: { announce, dev, raw } } } + +#define PTS_CHAN_INIT(announce, dev, raw, pri) \ + { PTS, -1, 0, 0, pri, { pts: { announce, dev, raw } } } + +#define XTERM_CHAN_INIT(n, title, raw, pri) \ + { XTERM, -1, 0, 0, pri, { xterm: { -1, n, title, raw } } } + +#define TTY_CHAN_INIT(dev, raw, pri) \ + { TTY, -1, 0, 0, pri, { tty: { dev, raw } } } + +#define SOCK_CHAN_INIT(port, pri) \ + { SOCKET, -1, 0, 0, pri, { sock: { port, 0, -1 } } } + +#define FD_CHAN_INIT(fd, pri) { FD, fd, 0, 0, pri } + +#define COPY_CHAN_INIT() { COPY } + +#define PTY_IO_CHAN_INIT(announce, data, raw, pri) \ + { PTY_CHAN_INIT(announce, data, raw, pri), COPY_CHAN_INIT() } + +#define PTS_IO_CHAN_INIT(announce, data, raw, pri) \ + { PTS_CHAN_INIT(announce, data, raw, pri), COPY_CHAN_INIT() } + +#define XTERM_IO_CHAN_INIT(n, title, raw, pri) \ + { XTERM_CHAN_INIT(n, title, raw, pri), COPY_CHAN_INIT() } + +#define STDIO_IO_CHAN_INIT(pri) \ + { { FD, 0, 0, 0, pri, { } }, { FD, 1, 0, 0, pri, { } } } + +#define TTY_IO_CHAN_INIT(dev, raw, pri) \ + { TTY_CHAN_INIT(dev, raw, pri), COPY_CHAN_INIT() } + +#define SOCK_IO_CHAN_INIT(port, pri) \ + { SOCK_CHAN_INIT(port, pri), COPY_CHAN_INIT() } + +#define FD_IO_CHAN_INIT(in, out, pri) \ + { FD_CHAN_INIT(in, pri), FD_CHAN_INIT(out, pri) } + +#define CHAN_IN_FD(chan) ((chan).in.fd) +#define CHAN_OUT_FD(chan) (((chan).out.type == COPY) ? ((chan).in.fd) : \ + ((chan).out.fd)) + +struct chan_opts { + void (*announce)(char *dev_name, int dev); + int dev; + char *xterm_title; + int raw_pty; +}; + +extern int tty_read(struct chan *chan, void *tty); +extern void tty_eof(void *tty_ptr, int *hung_up); +extern void tty_interrupt(struct io_chan *chan, void *tty_ptr, int count); +extern void tty_receive_char(void *tty_ptr, char ch); +extern int open_chan_pair(struct io_chan *chan, + int (*irq_setup)(int fd, void *data), void *data); +extern int open_fd(struct chan *chan); +extern int open_xterm(struct chan *chan); +extern int open_pty(struct chan *chan); +extern int open_tty(struct chan *chan); +extern int open_pts(struct chan *chan); +extern int open_socket(struct chan *chan); +extern int write_chan(struct io_chan *chan, const char *buf, int len); +extern int parse_chan_pair(char *str, int device, struct io_chan *chan, + int pri, struct chan_opts *opts); +extern void accept_connection(struct chan *chan); +extern void close_chan_pair(struct io_chan *chan); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/debug.h linux.ac/arch/um/include/debug.h --- linux.vanilla/arch/um/include/debug.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/debug.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,9 @@ +#ifndef __DEBUG_H +#define __DEBUG_H + +extern void debugger_proxy(int status, pid_t pid); +extern void child_proxy(pid_t pid, int status); +extern void init_proxy (pid_t, int, int); +extern int start_debugger(char *prog, int startup, int stop, int *debugger_fd); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/kern.h linux.ac/arch/um/include/kern.h --- linux.vanilla/arch/um/include/kern.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/kern.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __KERN_H__ +#define __KERN_H__ + +/* These are all user-mode things which are convenient to call directly + * from kernel code and for which writing a wrapper is too much of a pain. + * The regular include files can't be included because this file is included + * only into kernel code, and user-space includes conflict with kernel + * includes. + */ + +extern int errno; + +extern int getpid(void); +extern int clone(int (*proc)(void *), void *sp, int flags, void *data); +extern int sleep(int); +extern int printf(char *fmt, ...); +extern char *strerror(int errnum); +extern char *ptsname(int __fd); +extern int munmap(void *, int); +extern void *sbrk(int increment); +extern void *malloc(int size); +extern void perror(char *err); +extern int kill(int pid, int sig); +extern int getuid(void); + +#endif __KERN_H__ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/kern_util.h linux.ac/arch/um/include/kern_util.h --- linux.vanilla/arch/um/include/kern_util.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/kern_util.h Thu Apr 19 14:33:16 2001 @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __KERN_UTIL_H__ +#define __KERN_UTIL_H__ + +#include "sysdep/ptrace.h" + +#define x(s) #s +#define xx(s) x(s) + +#ifndef _UNISTD_H +extern int write(int, const char *, int); +#endif + +extern int ncpus; +extern char *linux_prog; +extern char *gdb_init; + +typedef long syscall_handler_t(struct sys_pt_regs regs); +extern syscall_handler_t *sys_call_table[]; + +#define ROUND_DOWN(addr) ((void *)(((unsigned long) addr) & PAGE_MASK)) +#define ROUND_UP(addr) ROUND_DOWN(((unsigned long) addr) + PAGE_SIZE - 1) + +extern int kernel_fork(unsigned long flags, int (*fn)(void *), void * arg); +extern unsigned long stack_sp(unsigned long page); +extern int kernel_thread_proc(void *data); +extern long execute_syscall(struct sys_pt_regs regs); +extern void syscall_segv(int sig); +extern int current_pid(void); +extern void do_bh(void); +extern void set_init_pid(int pid); +extern unsigned long alloc_stack(void); +extern int do_signal(void *t, unsigned long *error, int *again_out); +extern int is_stack_fault(unsigned long sp); +extern unsigned long segv(unsigned long address, unsigned long ip, + int is_write, int is_user); +extern int set_user_thread(void *task, int on, int restore_regs); +extern void syscall_ready(void); +extern void set_tracing(void *t, int tracing); +extern int is_tracing(void *task); +extern int segv_syscall(void); +extern void ret_from_sys_call(void *t); +extern void add_perm_vma(unsigned long start, unsigned long end, char rperm, + char wperm, char xperm, char private); +extern void kern_finish_exec(void *task, int new_pid, unsigned long stack); +extern int page_size(void); +extern int page_mask(void); +extern int need_finish_fork(void); +extern int do_proc_op(void *t, int proc_id); +extern void free_stack(unsigned long stack); +extern void add_input_request(int op, void (*proc)(int), void *arg); +extern int sys_execve(char *file, char **argv, char **env); +extern char *current_cmd(void); +extern void timer_handler(int sig, void *sc, int usermode); +extern int set_signals(int enable); +extern void force_sigbus(void); +extern int pid_to_processor_id(int pid); +extern void block_signals(void); +extern void unblock_signals(void); +extern void deliver_signals(void *t); +extern void lock_syscall(void); +extern void unlock_syscall(void); +extern void lock_trap(void); +extern void unlock_trap(void); +extern void lock_pid(void); +extern void unlock_pid(void); +extern int cpu_idle(void); +extern void finish_fork(void); +extern void *get_current_task(void); +extern void paging_init(void); +extern unsigned long um_virt_to_phys(void *t, unsigned long addr); +extern void init_flush_vm(void); +extern void *process_state(void *t, unsigned long *cr2_out, int *err_out); +extern struct sys_pt_regs *syscall_state(void *t, void **stack_out, + int *size_out); +extern int have_signals(void *t, int altstack); +extern void syscall_trace(void); +extern void save_altstack(void *t, unsigned long sp); +extern struct sys_pt_regs *altstack_state(void *t, void **stack_out, + int *size_out); +extern int hz(void); +extern int switching_modes(void *t); +extern void idle_timer(void); +extern unsigned int do_IRQ(int irq, int user_mode); +extern int external_pid(void *t); +extern void boot_timer_handler(int sig); +extern void interrupt_end(void); +extern void tracing_reboot(void); +extern void tracing_halt(void); +extern void tracing_cb(void (*proc)(void *), void *arg); +extern void probe_stack(unsigned long sp); +extern void debugger_signal(int status, int pid); +extern void child_signal(int pid, int status); +extern int init_ptrace_proxy(int idle_pid, int startup, int stop); +extern void add_process_vmas(void); +extern void check_stack_overflow(void *ptr); +extern void relay_signal(int sig, void *sc, int usermode); +extern int singlestepping(void *t); +extern void not_implemented(void); +extern void setup_kernel_stack(void); +extern void set_syscall_regs(void *t); +extern void finish_fork_handler(int sig); +extern int user_context(unsigned long sp); +extern void timer_irq(int user_mode); +extern void signal_deliverer(void); +extern void set_repeat_syscall(int again); +extern int get_repeat_syscall(void *t); +extern void set_sigreturn_syscall(int syscall); +extern void force_flush_all(void); +extern void unprotect_stack(unsigned long stack); +extern void kern_start_exec(int new_pid); +extern void do_exitcalls(void); +extern int get_restore_regs(void *t); +extern int attach_debugger(int idle_pid, int pid); +extern void *round_up(unsigned long addr); +extern void *round_down(unsigned long addr); +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/process.h linux.ac/arch/um/include/process.h --- linux.vanilla/arch/um/include/process.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/process.h Sun May 13 19:41:45 2001 @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __PROCESS_H__ +#define __PROCESS_H__ + +#include + +extern void sig_handler(int sig, struct sigcontext sc); +extern void irq_handler(int sig, struct sigcontext sc); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-i386/ptrace.h linux.ac/arch/um/include/sysdep-i386/ptrace.h --- linux.vanilla/arch/um/include/sysdep-i386/ptrace.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-i386/ptrace.h Sun May 13 19:41:45 2001 @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_I386_PTRACE_H +#define __SYSDEP_I386_PTRACE_H + +struct sys_pt_regs { + unsigned long regs[17]; +}; + +#define EMPTY_REGS { { [ 0 ... 16 ] = 0 } } + +#define UM_REG(r, n) ((r)->regs[n]) + +#define UM_IP(r) UM_REG(r, EIP) +#define UM_SP(r) UM_REG(r, UESP) +#define UM_ELF_ZERO(r) UM_REG(r, EDX) + +#define UM_SYSCALL_RET(r) UM_REG(r, EAX) +#define UM_SYSCALL_NR(r) UM_REG(r, ORIG_EAX) + +#define UM_SYSCALL_ARG1(r) UM_REG(r, EBX) +#define UM_SYSCALL_ARG2(r) UM_REG(r, ECX) +#define UM_SYSCALL_ARG3(r) UM_REG(r, EDX) +#define UM_SYSCALL_ARG4(r) UM_REG(r, ESI) +#define UM_SYSCALL_ARG5(r) UM_REG(r, EDI) +#define UM_SYSCALL_ARG6(r) UM_REG(r, EBP) + +#define UM_IP_OFFSET (EIP * sizeof(long)) +#define UM_SP_OFFSET (UESP * sizeof(long)) +#define UM_ELF_ZERO_OFFSET (EDX * sizeof(long)) + +#define UM_SYSCALL_RET_OFFSET (EAX * sizeof(long)) +#define UM_SYSCALL_NR_OFFSET (ORIG_EAX * sizeof(long)) + +#define UM_SYSCALL_ARG1_OFFSET (EBX * sizeof(long)) +#define UM_SYSCALL_ARG2_OFFSET (ECX * sizeof(long)) +#define UM_SYSCALL_ARG3_OFFSET (EDX * sizeof(long)) +#define UM_SYSCALL_ARG4_OFFSET (ESI * sizeof(long)) +#define UM_SYSCALL_ARG5_OFFSET (EDI * sizeof(long)) +#define UM_SYSCALL_ARG6_OFFSET (EBP * sizeof(long)) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-i386/sigcontext.h linux.ac/arch/um/include/sysdep-i386/sigcontext.h --- linux.vanilla/arch/um/include/sysdep-i386/sigcontext.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-i386/sigcontext.h Sun May 13 19:41:45 2001 @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYS_SIGCONTEXT_I386_H +#define __SYS_SIGCONTEXT_I386_H + +#define SC_FAULT_ADDR(sc) ((sc)->cr2) +#define SC_FAULT_WRITE(sc) (((sc)->err) & 2) +#define SC_IP(sc) ((sc)->eip) +#define SC_SP(sc) ((sc)->esp_at_signal) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-i386/syscalls.h linux.ac/arch/um/include/sysdep-i386/syscalls.h --- linux.vanilla/arch/um/include/sysdep-i386/syscalls.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-i386/syscalls.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "asm/unistd.h" +#include "kern_util.h" + +extern syscall_handler_t sys_modify_ldt; + +#define ARCH_SYSCALLS \ + [ __NR_vm86old ] = sys_ni_syscall, \ + [ __NR_modify_ldt ] = sys_modify_ldt, \ + [ __NR_lchown32 ] = sys_lchown, \ + [ __NR_getuid32 ] = sys_getuid, \ + [ __NR_getgid32 ] = sys_getgid, \ + [ __NR_geteuid32 ] = sys_geteuid, \ + [ __NR_getegid32 ] = sys_getegid, \ + [ __NR_setreuid32 ] = sys_setreuid, \ + [ __NR_setregid32 ] = sys_setregid, \ + [ __NR_getgroups32 ] = sys_getgroups, \ + [ __NR_setgroups32 ] = sys_setgroups, \ + [ __NR_fchown32 ] = sys_fchown, \ + [ __NR_setresuid32 ] = sys_setresuid, \ + [ __NR_getresuid32 ] = sys_getresuid, \ + [ __NR_setresgid32 ] = sys_setresgid, \ + [ __NR_getresgid32 ] = sys_getresgid, \ + [ __NR_chown32 ] = sys_chown, \ + [ __NR_setuid32 ] = sys_setuid, \ + [ __NR_setgid32 ] = sys_setgid, \ + [ __NR_setfsuid32 ] = sys_setfsuid, \ + [ __NR_setfsgid32 ] = sys_setfsgid, \ + [ __NR_pivot_root ] = sys_pivot_root, \ + [ __NR_mincore ] = sys_mincore, \ + [ __NR_madvise ] = sys_madvise + +#define LAST_SYSCALL __NR_madvise + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-ia64/ptrace.h linux.ac/arch/um/include/sysdep-ia64/ptrace.h --- linux.vanilla/arch/um/include/sysdep-ia64/ptrace.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-ia64/ptrace.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_PTRACE_H +#define __SYSDEP_IA64_PTRACE_H + +struct sys_pt_regs { + int foo; +}; + +#define EMPTY_REGS { 0 } + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-ia64/sigcontext.h linux.ac/arch/um/include/sysdep-ia64/sigcontext.h --- linux.vanilla/arch/um/include/sysdep-ia64/sigcontext.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-ia64/sigcontext.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_SIGCONTEXT_H +#define __SYSDEP_IA64_SIGCONTEXT_H + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-ia64/syscalls.h linux.ac/arch/um/include/sysdep-ia64/syscalls.h --- linux.vanilla/arch/um/include/sysdep-ia64/syscalls.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-ia64/syscalls.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_SYSCALLS_H +#define __SYSDEP_IA64_SYSCALLS_H + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/sysdep-ppc/ptrace.h linux.ac/arch/um/include/sysdep-ppc/ptrace.h --- linux.vanilla/arch/um/include/sysdep-ppc/ptrace.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/sysdep-ppc/ptrace.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,67 @@ +/* + * Licensed under the GPL + */ + +#ifndef __SYS_PTRACE_PPC_H +#define __SYS_PTRACE_PPC_H + +/* the following taken from */ + +#ifdef CONFIG_PPC64 +#define PPC_REG unsigned long /*long*/ +#else +#define PPC_REG unsigned long +#endif +struct sys_pt_regs_s { + PPC_REG gpr[32]; + PPC_REG nip; + PPC_REG msr; + PPC_REG orig_gpr3; /* Used for restarting system calls */ + PPC_REG ctr; + PPC_REG link; + PPC_REG xer; + PPC_REG ccr; + PPC_REG mq; /* 601 only (not used at present) */ + /* Used on APUS to hold IPL value. */ + PPC_REG trap; /* Reason for being here */ + PPC_REG dar; /* Fault registers */ + PPC_REG dsisr; + PPC_REG result; /* Result of a system call */ +}; + +struct sys_pt_regs { + union { + struct sys_pt_regs_s s; + PPC_REG regs[sizeof(struct sys_pt_regs_s) / sizeof(PPC_REG)]; + } u; +}; + +#define EMPTY_REGS { { { { [ 0 ... 31 ] = 0 }, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } } + +#define UM_SYSCALL_RET(r) ((r)->u.s.gpr[3]) +#define UM_SP(r) ((r)->u.s.gpr[1]) +#define UM_SYSCALL_NR(r) ((r)->u.s.gpr[3]) +#define UM_SYSCALL_ARG1(r) ((r)->u.s.gpr[4]) +#define UM_SYSCALL_ARG2(r) ((r)->u.s.gpr[5]) +#define UM_SYSCALL_ARG3(r) ((r)->u.s.gpr[7]) +#define UM_SYSCALL_ARG4(r) ((r)->u.s.gpr[8]) +#define UM_SYSCALL_ARG5(r) ((r)->u.s.gpr[9]) +#define UM_SYSCALL_ARG6(r) ((r)->u.s.gpr[10]) + +#define UM_SYSCALL_NR_OFFSET (PT_R3 * sizeof(PPC_REG)) +#define UM_SP_OFFSET (PT_R1 * sizeof(PPC_REG)) +#define UM_IP_OFFSET (PT_NIP * sizeof(PPC_REG)) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/ubd_user.h linux.ac/arch/um/include/ubd_user.h --- linux.vanilla/arch/um/include/ubd_user.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/ubd_user.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UM_UBD_USER_H +#define __UM_UBD_USER_H + +#include "asm/types.h" + +struct io_thread_req { + int read; + int fd; + __u64 offset; + unsigned long length; + char *buffer; + void *req; +}; + +extern int open_ubd_fs(char *file, int *openflags); +extern int read_ubd_fs(int fd, void *buffer, int len); +extern int write_ubd_fs(int fd, char *buffer, int len); +extern int start_io_thread(unsigned long sp, int *fds_out); +extern int do_io(struct io_thread_req *req); +extern int ubd_is_dir(char *file); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/umn.h linux.ac/arch/um/include/umn.h --- linux.vanilla/arch/um/include/umn.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/umn.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UMN_H +#define __UMN_H + +extern int open_umn_tty(int *slave_out, int *slipno_out); +extern void close_umn_tty(int master, int slave); +extern int umn_send_packet(int fd, void *data, int len); +extern int set_umn_addr(int fd, char *addr, char *ptp_addr); +extern void slip_unesc(unsigned char s); +extern void umn_read(int fd); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/user.h linux.ac/arch/um/include/user.h --- linux.vanilla/arch/um/include/user.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/user.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __USER_H__ +#define __USER_H__ + +extern void panic(char *fmt, ...); +extern int printk(char *fmt, ...); +extern void schedule(void); +extern void *kmalloc(int size, int flags); +extern void kfree(void *ptr); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/include/user_util.h linux.ac/arch/um/include/user_util.h --- linux.vanilla/arch/um/include/user_util.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/include/user_util.h Sun May 13 19:41:45 2001 @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __USER_UTIL_H__ +#define __USER_UTIL_H__ + +#include "asm/types.h" +#include "sysdep/ptrace.h" + +extern int grantpt(int __fd); +extern int unlockpt(int __fd); +extern char *ptsname(int __fd); + +#ifndef _UNISTD_H +extern int write(int, const char *, int); +#endif + +#define y(s) #s +#define yy(s) y(s) + +enum { OP_NONE, OP_EXEC, OP_SWITCH, OP_THREAD, OP_FORK, OP_FORK_FINISH, + OP_TRACE_ON, OP_TRACE_OFF, OP_REBOOT, OP_HALT, OP_CB }; + +struct cpu_task { + int pid; + void *task; +}; + +extern struct cpu_task cpu_tasks[]; + +extern int physmem_fd; +extern int brk_fd; + +extern unsigned long low_physmem; +extern unsigned long high_physmem; +extern unsigned long physmem; +extern unsigned long end_vm; +extern unsigned long start_vm; + +extern void *last_brk; +extern void *brk_start; + +extern int physmem_inode; + +extern int tracing_pid; + +extern char host_info[]; + +extern char saved_command_line[]; +extern char command_line[]; + +extern int gdb_pid; + +extern void *open_maps(void); +extern int read_map(void *fd, unsigned long *start_out, + unsigned long *end_out, char *r_out, char *w_out, + char *x_out, char *p_out); +extern void close_maps(void *fd); +extern unsigned long get_brk(void); +extern __u64 file_size(char *file); +extern void stop(void); +extern int proc_start_thread(unsigned long ip, unsigned long sp); +extern void stack_protections(unsigned long address, int len); +extern void abandon_proc_space(int (*proc)(void *), unsigned long sp); +extern int signals(int (*init_proc)(void *), void *sp); +extern unsigned long setup_memory(unsigned long total_size, + unsigned long physmem_size, int *fd_out, + int *inode_out); +extern void map(unsigned long virt, void *p, unsigned long len, + int r, int w, int x); +extern int unmap(unsigned long address, unsigned long len); +extern void protect(unsigned long addr, unsigned long len, int r, int w, + int x); +extern void stop_pid(int pid); +extern void kill_pid(int pid); +extern void usr1_pid(int pid); +extern void cont_pid(int pid); +extern int __personality(int); +extern void signal_handler(void *task, unsigned long handler, int sig); +extern int syscall_handler(void *unused); +extern int do_syscall(void *task, int pid); +extern int wait_for_stop(int pid, int sig, int cont_type); +extern void *add_signal_handler(int sig, void (*handler)(int)); +extern void signal_init(void); +extern void finish_exec(int old_pid, int new_pid, struct sys_pt_regs *regs); +extern int start_fork_tramp(unsigned long sig_stack, unsigned long temp_stack, + int clone_vm, int (*tramp)(void *)); +extern void trace_myself(void); +extern void block_shlib_mem(void); +extern void unblock_shlib_mem(void); +extern void get_profile_timer(void); +extern void set_timers(int set_signal); +extern int clone_and_wait(int (*fn)(void *), void *arg, void *sp, int flags); +extern int input_loop(void); +extern void set_handler(int sig, void (*handler)(int), int flags, ...); +extern void set_sigstack(void *stack); +extern void continue_execing_proc(int pid); +extern int linux_main(int argc, char **argv); +extern void remap_data(void *segment_start, void *segment_end); +extern void set_cmdline(char *cmd); +extern void continue_fork(void *task, int pid, struct sys_pt_regs *regs); +extern void input_cb(void (*proc)(void *), void *arg, int arg_len); +extern void setup_input(void); +extern int exit_kernel(int pid, void *task, int *signal_out); +extern int alt_stack_handler(void *arg); +extern int setup_altstack(unsigned long stack, int stack_size); +extern void process_stack_handler(int sig); +extern int get_pty(void); +extern void save_signal_state(int *sig_ptr); +extern void change_sig(int signal, int on); +extern void fill_in_regs(struct sys_pt_regs *regs, void *sc); +extern void fill_in_sigcontext(void *sc, struct sys_pt_regs *regs, + unsigned long cr2, int err); +extern int activate_fd(int irq, int fd, void *dev_id); +extern void reactivate_fd(int fd); +extern void free_irq_fd(void *dev_id); +extern void forward_interrupts(int pid); +extern void init_irq_signals(int on_sigstack); +extern void *um_kmalloc(int size); +extern int raw(int fd, int complain); +extern void cooked(int fd); +extern int switcheroo(int fd, int prot, void *from, void *to, int size); +extern int create_vm_file(unsigned long len); +extern void *update_brk(void); +extern void check_brk(void *process_brk); +extern void idle_sleep(int secs); +extern int get_one_stack(char **stack_out, struct sys_pt_regs *regs_out); +extern void setup_machinename(char *machine_out); +extern void setup_hostinfo(void); +extern void add_arg(char *cmd_line, char *arg); +extern void sigio_handler(int sig, void *sc, int usermode); +extern void init_new_thread(void *sig_stack, void (*usr1_handler)(int)); +extern void start_exec(int old_pid, int new_pid, int *error, + struct sys_pt_regs *regs); +extern void attach_process(int pid); +extern void calc_sigframe_size(void); +extern int fork_tramp(void *sig_stack); +extern void do_exec(int old_pid, int new_pid); +extern void tracer_panic(char *msg, ...); +extern void close_fd(int); +extern char *get_umid(void); +extern void ptrace_cont_pid(int pid); +extern void create_pid_file(char *name); +extern void remap_profiling_buffers(void); +extern int load_initrd(char *filename, void *buf, int size); +extern int ptrace_getregs(long pid, struct sys_pt_regs *regs_out); +extern int ptrace_setregs(long pid, struct sys_pt_regs *regs_in); +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/Makefile linux.ac/arch/um/kernel/Makefile --- linux.vanilla/arch/um/kernel/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/Makefile Sun May 13 20:23:29 2001 @@ -0,0 +1,74 @@ +OBJ = um.o + +OBJS = process.o current.o exec_kern.o exec_user.o init_task.o irq.o \ + irq_user.o mem.o ptrace.o reboot.o resource.o \ + segment.o setup.o signal_user.o smp.o syscall_kern.o \ + syscall_user.o sys_call_table.o time.o time_kern.o tlb.o trap_kern.o \ + trap_user.o um_arch.o user_util.o + +OX_OBJS = ksyms.o process_kern.o signal_kern.o user_syms.o + +EXTRA_CFLAGS += -I../include + +USER_CFLAGS := $(patsubst -I%,,$(CFLAGS)) +USER_CFLAGS := $(patsubst -D__KERNEL__,,$(USER_CFLAGS)) $(EXTRA_CFLAGS) + +UNMAP_CFLAGS := $(patsubst -pg -DPROFILING,,$(USER_CFLAGS)) +UNMAP_CFLAGS := $(patsubst -fprofile-arcs -ftest-coverage,,$(UNMAP_CFLAGS)) + +ifeq ($(CONFIG_MODULES), y) + DMODULES = -DCONFIG_MODULES +endif + +ifeq ($(CONFIG_MODVERSIONS), y) + DMODVERSIONS = -DCONFIG_MODVERSIONS +endif + +CFLAGS_user_syms.o = -DAUTOCONF_INCLUDED $(DMODULES) $(DMODVERSIONS) -I- \ + -I../include + +all: $(OBJ) unmap_fin.o + +exec_user.o: exec_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +process.o: process.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +syscall_user.o: syscall_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +trap_user.o: trap_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +signal_user.o: signal_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +time.o: time.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +user_util.o: user_util.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +irq_user.o: irq_user.c + $(CC) $(USER_CFLAGS) -c -o $@ $< + +unmap.o: unmap.c + echo $(UNMAP_CFLAGS) + $(CC) $(UNMAP_CFLAGS) -c -o $@ $< + +unmap_fin.o : unmap.o + ld -r -o $@ $< -lc -L/usr/lib + +$(OBJ): $(OBJS) $(OX_OBJS) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ + +clean: + rm -f $(OBJS) $(OX_OBJS) + +modules: + +fastdep: + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/current.c linux.ac/arch/um/kernel/current.c --- linux.vanilla/arch/um/kernel/current.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/current.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" + +#ifndef __SMP__ + +struct task_struct *current_task; + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/exec_kern.c linux.ac/arch/um/kernel/exec_kern.c --- linux.vanilla/arch/um/kernel/exec_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/exec_kern.c Sun May 13 19:43:32 2001 @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/malloc.h" +#include "linux/smp_lock.h" +#include "asm/ptrace.h" +#include "asm/pgtable.h" +#include "asm/pgalloc.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +extern struct mm_struct kernel_maps; + +static void add_kernel_vma(struct task_struct *task) +{ + struct vm_area_struct *vma, *new; + + if(task->mm != init_task.mm){ + for(vma = kernel_maps.mmap;vma != NULL;vma = vma->vm_next){ + new = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if(!new) panic("No memory in add_kernel_vma"); + *new = *vma; + new->vm_next = NULL; + new->vm_flags |= VM_LOCKED; + new->vm_avl_height = 0; + new->vm_avl_left = NULL; + new->vm_avl_right = NULL; + new->vm_next_share = NULL; + new->vm_pprev_share = NULL; + if(new->vm_file != NULL) + atomic_inc(&new->vm_file->f_count); + insert_vm_struct(task->mm, new); + } + } +} + +static void check_vma(unsigned long addr) +{ + struct vm_area_struct *vma; + + vma = find_vma(current->mm, addr); + if((vma == NULL) || (vma->vm_start > addr)) force_sigbus(); +} + +static int exec_tramp(void *sig_stack) +{ + block_signals(); + init_new_thread(sig_stack, NULL); + kill(getpid(), SIGSTOP); + return(0); +} + +void flush_thread(void) +{ + unsigned long stack; + int new_pid; + + stack = alloc_stack(); + new_pid = start_fork_tramp(current->thread.kernel_stack, + stack, 0, exec_tramp); + if(new_pid < 0) do_exit(SIGKILL); + forward_interrupts(new_pid); + current->thread.request.op = OP_EXEC; + current->thread.request.u.exec.pid = new_pid; + unprotect_stack((unsigned long) current); + usr1_pid(getpid()); + + current->thread.extern_pid = new_pid; + free_page(stack); + protect(physmem, high_physmem - physmem, 1, 1, 0); + stack = (unsigned long) current; + protect(stack + PAGE_SIZE, PAGE_SIZE, 0, 0, 0); + stack_protections(stack + 2 * PAGE_SIZE, 2 * PAGE_SIZE); + force_flush_all(); + check_brk(brk_start); + current->thread.mm_changes = 0; + unblock_signals(); +} + +void start_thread(struct pt_regs * regs, unsigned long eip, unsigned long esp) +{ + check_vma(current->mm->brk - 1); + check_vma(eip); + current->addr_limit.seg = STACK_TOP; + add_kernel_vma(current); + flush_tlb_mm(current->mm); + if(UM_SP(¤t->thread.process_regs) == 0) + current->thread.process_regs = current->thread.syscall_regs; + UM_IP(¤t->thread.process_regs) = eip; + UM_SP(¤t->thread.process_regs) = esp; + UM_ELF_ZERO(¤t->thread.process_regs) = 0; +} + +static int execve1(char *file, char **argv, char **env) +{ + int error; + + error = do_execve(file, argv, env, NULL); + if (error == 0){ + current->ptrace &= ~PT_DTRACE; + set_cmdline(current_cmd()); + } + return(error); +} + +int um_execve(char *file, char **argv, char **env) +{ + if(execve1(file, argv, env) == 0) set_user_thread(current, 1, 1); + return(-1); +} + +int sys_execve(char *file, char **argv, char **env) +{ + int error; + char *filename; + + lock_kernel(); + filename = getname((char *) file); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; + error = execve1(filename, argv, env); + putname(filename); + out: + unlock_kernel(); + return(error); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/exec_user.c linux.ac/arch/um/kernel/exec_user.c --- linux.vanilla/arch/um/kernel/exec_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/exec_user.c Sun May 13 19:41:45 2001 @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" + +void do_exec(int old_pid, int new_pid) +{ + struct sys_pt_regs regs; + + if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) || + (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0) || + (waitpid(new_pid, 0, WUNTRACED) < 0)) + tracer_panic("do_exec failed to attach proc"); + + if(ptrace_getregs(old_pid, ®s) < 0) + tracer_panic("do_exec failed to get registers"); + + kill(old_pid, SIGKILL); + + if((ptrace_setregs(new_pid, ®s) < 0) || + (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0)) + tracer_panic("do_exec failed to start new proc"); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/init_task.c linux.ac/arch/um/kernel/init_task.c --- linux.vanilla/arch/um/kernel/init_task.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/init_task.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/mm.h" +#include "linux/sched.h" +#include "asm/uaccess.h" +#include "asm/pgtable.h" +#include "user_util.h" + +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial task structure. + * + * We need to make sure that this is 16384-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ + +union task_union init_task_union +__attribute__((__section__(".data.init_task"))) = +{ INIT_TASK(init_task_union.task) }; + +struct task_struct *alloc_task_struct(void){ + struct task_struct *task; + + task = (struct task_struct *) __get_free_pages(GFP_KERNEL,2); + if(task == NULL) return(NULL); + return(task); +} + +void unprotect_stack(unsigned long stack) +{ + protect(stack, 4 * PAGE_SIZE, 1, 1, 0); +} + +void free_task_struct(struct task_struct *task) +{ + free_pages((unsigned long) task, 2); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/irq.c linux.ac/arch/um/kernel/irq.c --- linux.vanilla/arch/um/kernel/irq.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/irq.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,808 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + * Derived (i.e. mostly copied) from arch/i386/kernel/irq.c: + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + */ + +#include "linux/kernel.h" +#include "linux/smp.h" +#include "linux/irq.h" +#include "linux/kernel_stat.h" +#include "linux/interrupt.h" +#include "linux/random.h" +#include "linux/slab.h" +#include "linux/file.h" +#include "linux/proc_fs.h" +#include "linux/init.h" +#include "asm/irq.h" +#include "asm/hw_irq.h" +#include "asm/hardirq.h" +#include "asm/atomic.h" +#include "asm/signal.h" +#include "asm/system.h" +#include "asm/errno.h" +#include "asm/uaccess.h" +#include "user_util.h" + +static void register_irq_proc (unsigned int irq); + +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = + { [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}}; + +/* + * Generic no controller code + */ + +static void enable_none(unsigned int irq) { } +static unsigned int startup_none(unsigned int irq) { return 0; } +static void disable_none(unsigned int irq) { } +static void ack_none(unsigned int irq) +{ +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves, it doesnt deserve + * a generic callback i think. + */ +#if CONFIG_X86 + printk("unexpected IRQ trap at vector %02x\n", irq); +#ifdef CONFIG_X86_LOCAL_APIC + /* + * Currently unexpected vectors happen only on SMP and APIC. + * We _must_ ack these because every local APIC has only N + * irq slots per priority level, and a 'hanging, unacked' IRQ + * holds up an irq slot - in excessive cases (when multiple + * unexpected vectors occur) that might lock up the APIC + * completely. + */ + ack_APIC_irq(); +#endif +#endif +} + +/* startup is the same as "enable", shutdown is same as "disable" */ +#define shutdown_none disable_none +#define end_none enable_none + +struct hw_interrupt_type no_irq_type = { + "none", + startup_none, + shutdown_none, + enable_none, + disable_none, + ack_none, + end_none +}; + +volatile unsigned long irq_err_count; + +/* + * Generic, controller-independent functions: + */ + +int get_irq_list(char *buf) +{ + int i, j; + struct irqaction * action; + char *p = buf; + + p += sprintf(p, " "); + for (j=0; jtypename); + p += sprintf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + p += sprintf(p, ", %s", action->name); + *p++ = '\n'; + } + p += sprintf(p, "\n"); +#if CONFIG_SMP + p += sprintf(p, "LOC: "); + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + apic_timer_irqs[cpu_logical_map(j)]); + p += sprintf(p, "\n"); +#endif + p += sprintf(p, "ERR: %10lu\n", irq_err_count); + return p - buf; +} + + +/* + * This should really return information about whether + * we should do bottom half handling etc. Right now we + * end up _always_ checking the bottom half, which is a + * waste of time and is not what some drivers would + * prefer. + */ +int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, + struct irqaction * action) +{ + int status; + int cpu = smp_processor_id(); + + irq_enter(cpu, irq); + + status = 1; /* Force the "do bottom halves" bit */ + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + + irq_exit(cpu, irq); + + return status; +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ + +/** + * disable_irq_nosync - disable an irq without waiting + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. Unlike disable_irq(), this function does not ensure existing + * instances of the IRQ handler have completed before returning. + * + * This function may be called from IRQ context. + */ + +void inline disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * disable_irq - disable an irq and wait for completion + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. That is for two disables you need two enables. This + * function waits for any pending IRQ handlers for this interrupt + * to complete before returning. If you use this function while + * holding a resource the IRQ handler may need you will deadlock. + * + * This function may be called - with care - from IRQ context. + */ + +void disable_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + + if (!local_irq_count(smp_processor_id())) { + do { + barrier(); + } while (irq_desc[irq].status & IRQ_INPROGRESS); + } +} + +/** + * enable_irq - enable interrupt handling on an irq + * @irq: Interrupt to enable + * + * Re-enables the processing of interrupts on this IRQ line + * providing no disable_irq calls are now in effect. + * + * This function may be called from IRQ context. + */ + +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + desc->handler->enable(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk("enable_irq() unbalanced from %p\n", + __builtin_return_address(0)); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +unsigned int do_IRQ(int irq, int user_mode) +{ + /* + * 0 return value means that this irq is already being + * handled by some other CPU. (or is disabled) + */ + int cpu = smp_processor_id(); + irq_desc_t *desc = irq_desc + irq; + struct irqaction * action; + struct pt_regs regs; + unsigned int status; + + regs.user_mode = user_mode; + kstat.irqs[cpu][irq]++; + spin_lock(&desc->lock); + desc->handler->ack(irq); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + action = desc->action; + status &= ~IRQ_PENDING; /* we commit to handling */ + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (!action) + goto out; + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + spin_unlock(&desc->lock); + handle_IRQ_event(irq, ®s, action); + spin_lock(&desc->lock); + + if (!(desc->status & IRQ_PENDING)) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + desc->handler->end(irq); + spin_unlock(&desc->lock); + + if (softirq_active(cpu) & softirq_mask(cpu)) + do_softirq(); + return 1; +} + +/** + * request_irq - allocate an interrupt line + * @irq: Interrupt line to allocate + * @handler: Function to be called when the IRQ occurs + * @irqflags: Interrupt type flags + * @devname: An ascii name for the claiming device + * @dev_id: A cookie passed back to the handler function + * + * This call allocates interrupt resources and enables the + * interrupt line and IRQ handling. From the point this + * call is made your handler function may be invoked. Since + * your handler function must clear any interrupt the board + * raises, you must take care both to initialise your hardware + * and to set up the interrupt handler in the right order. + * + * Dev_id must be globally unique. Normally the address of the + * device data structure is used as the cookie. Since the handler + * receives this value it makes sense to use it. + * + * If your interrupt is shared you must pass a non NULL dev_id + * as this is required when freeing the interrupt. + * + * Flags: + * + * SA_SHIRQ Interrupt is shared + * + * SA_INTERRUPT Disable local interrupts while processing + * + * SA_SAMPLE_RANDOM The interrupt can be used for entropy + * + */ + +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + int retval; + struct irqaction * action; + +#if 1 + /* + * Sanity-check: shared interrupts should REALLY pass in + * a real dev-ID, otherwise we'll have trouble later trying + * to figure out which interrupt is which (messes up the + * interrupt freeing logic etc). + */ + if (irqflags & SA_SHIRQ) { + if (!dev_id) + printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]); + } +#endif + + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + return -EINVAL; + + action = (struct irqaction *) + kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + return retval; +} + +int um_request_irq(unsigned int irq, int fd, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, + void *dev_id) +{ + int retval; + + retval = request_irq(irq, handler, irqflags, devname, dev_id); + if(retval) return(retval); + return(activate_fd(irq, fd, dev_id)); +} + +/* this was setup_x86_irq but it seems pretty generic */ +int setup_irq(unsigned int irq, struct irqaction * new) +{ + int shared = 0; + unsigned long flags; + struct irqaction *old, **p; + irq_desc_t *desc = irq_desc + irq; + + /* + * Some drivers like serial.c use request_irq() heavily, + * so we have to be careful not to interfere with a + * running system. + */ + if (new->flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + desc->depth = 0; + desc->status &= ~IRQ_DISABLED; + desc->handler->startup(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + register_irq_proc(irq); + return 0; +} + +/** + * free_irq - free an interrupt + * @irq: Interrupt line to free + * @dev_id: Device identity to free + * + * Remove an interrupt handler. The handler is removed and if the + * interrupt line is no longer in use by any driver it is disabled. + * On a shared IRQ the caller must ensure the interrupt is disabled + * on the card it drives before calling this function. The function + * does not return until any executing interrupts for this IRQ + * have completed. + * + * This function may be called from interrupt context. + * + * Bugs: Attempting to free an irq in a handler for the same irq hangs + * the machine. + */ + +void free_irq(unsigned int irq, void *dev_id) +{ + irq_desc_t *desc; + struct irqaction **p; + unsigned long flags; + + if (irq >= NR_IRQS) + return; + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + for (;;) { + struct irqaction * action = *p; + if (action) { + struct irqaction **pp = p; + p = &action->next; + if (action->dev_id != dev_id) + continue; + + /* Found it - now remove it from the list of entries */ + *pp = action->next; + if (!desc->action) { + desc->status |= IRQ_DISABLED; + desc->handler->shutdown(irq); + } + free_irq_fd(dev_id); + spin_unlock_irqrestore(&desc->lock,flags); + +#ifdef CONFIG_SMP + /* Wait to make sure it's not being used on another CPU */ + while (desc->status & IRQ_INPROGRESS) + barrier(); +#endif + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + return; + } +} + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; + +#define HEX_DIGITS 8 + +static int irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", irq_affinity[(long)data]); +} + +static unsigned int parse_hex_value (const char *buffer, + unsigned long count, unsigned long *ret) +{ + unsigned char hexnum [HEX_DIGITS]; + unsigned long value; + int i; + + if (!count) + return -EINVAL; + if (count > HEX_DIGITS) + count = HEX_DIGITS; + if (copy_from_user(hexnum, buffer, count)) + return -EFAULT; + + /* + * Parse the first 8 characters as a hex string, any non-hex char + * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. + */ + value = 0; + + for (i = 0; i < count; i++) { + unsigned int c = hexnum[i]; + + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + goto out; + } + value = (value << 4) | c; + } +out: + *ret = value; + return 0; +} + +static int irq_affinity_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int irq = (long) data, full_count = count, err; + unsigned long new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = parse_hex_value(buffer, count, &new_value); + +#if CONFIG_SMP + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!(new_value & cpu_online_map)) + return -EINVAL; +#endif + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, new_value); + + return full_count; +} + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long *mask = (unsigned long *) data; + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", *mask); +} + +static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long *mask = (unsigned long *) data, full_count = count, err; + unsigned long new_value; + + err = parse_hex_value(buffer, count, &new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + struct proc_dir_entry *entry; + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || + irq_dir[irq]) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + + smp_affinity_entry[irq] = entry; +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) + register_irq_proc(i); +} + +unsigned long probe_irq_on(void) +{ + return(0); +} + +int probe_irq_off(unsigned long val) +{ + return(0); +} + +static unsigned int startup_SIGIO_irq(unsigned int irq) +{ + return(0); +} + +static void shutdown_SIGIO_irq(unsigned int irq) +{ +} + +static void enable_SIGIO_irq(unsigned int irq) +{ +} + +static void disable_SIGIO_irq(unsigned int irq) +{ +} + +static void mask_and_ack_SIGIO(unsigned int irq) +{ +} + +static void end_SIGIO_irq(unsigned int irq) +{ +} + +static unsigned int startup_SIGVTALRM_irq(unsigned int irq) +{ + return(0); +} + +static void shutdown_SIGVTALRM_irq(unsigned int irq) +{ +} + +static void enable_SIGVTALRM_irq(unsigned int irq) +{ +} + +static void disable_SIGVTALRM_irq(unsigned int irq) +{ +} + +static void mask_and_ack_SIGVTALRM(unsigned int irq) +{ +} + +static void end_SIGVTALRM_irq(unsigned int irq) +{ +} + +static struct hw_interrupt_type SIGIO_irq_type = { + "SIGIO", + startup_SIGIO_irq, + shutdown_SIGIO_irq, + enable_SIGIO_irq, + disable_SIGIO_irq, + mask_and_ack_SIGIO, + end_SIGIO_irq, + NULL +}; + +static struct hw_interrupt_type SIGVTALRM_irq_type = { + "SIGVTALRM", + startup_SIGVTALRM_irq, + shutdown_SIGVTALRM_irq, + enable_SIGVTALRM_irq, + disable_SIGVTALRM_irq, + mask_and_ack_SIGVTALRM, + end_SIGVTALRM_irq, + NULL +}; + +void __init init_IRQ(void) +{ + int i; + + irq_desc[TIMER_IRQ].status = IRQ_DISABLED; + irq_desc[TIMER_IRQ].action = 0; + irq_desc[TIMER_IRQ].depth = 1; + irq_desc[TIMER_IRQ].handler = &SIGVTALRM_irq_type; + enable_irq(TIMER_IRQ); + for(i=1;i +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" + +struct irq_fd { + struct irq_fd *next; + void *id; + int fd; + int irq; + int pid; +}; + +static struct irq_fd *active_fds = NULL; +static fd_set active_fd_mask; +static int max_fd = -1; + +extern int io_count, intr_count; + +void sigio_handler(int sig, void *sc, int usermode) +{ + struct irq_fd *irq_fd, *next; + struct timeval tv; + fd_set fds; + int i, n, fd; + + while(1){ + tv.tv_sec = 0; + tv.tv_usec = 0; + fds = active_fd_mask; + if((n = select(max_fd + 1, &fds, NULL, NULL, &tv)) < 0){ + if(errno == EINTR) continue; + printk("sigio_handler : select returned %d, " + "errno = %d\n", n, errno); + break; + } + if(n == 0) break; + for(i=0;i<=max_fd;i++){ + if(FD_ISSET(i, &fds)) FD_CLR(i, &active_fd_mask); + } + for(irq_fd=active_fds;irq_fd != NULL;irq_fd = next){ + /* These mysterious assignments protect us against + * the irq handler freeing the irq from under us. + */ + next = irq_fd->next; + fd = irq_fd->fd; + if(FD_ISSET(irq_fd->fd, &fds)) + do_IRQ(irq_fd->irq, usermode); + } + } +} + +int activate_fd(int irq, int fd, void *dev_id) +{ + struct irq_fd *new_fd; + int pid, retval; + + for(new_fd = active_fds;new_fd;new_fd = new_fd->next){ + if(new_fd->fd == fd){ + printk("Registering fd %d twice\n", fd); + printk("Irqs : %d, %d\n", new_fd->irq, irq); + printk("Ids : 0x%x, 0x%x\n", new_fd->id, dev_id); + return(-EIO); + } + } + if((retval = fcntl(fd, F_SETFL, O_ASYNC | O_NONBLOCK)) < 0){ + printk("Failed to set O_ASYNC and O_NONBLOCK on fd # %d, " + "errno = %d\n", fd, errno); + return(-retval); + } + pid = external_pid(NULL); + if((retval = fcntl(fd, F_SETOWN, pid)) < 0){ + printk("Failed to fcntl F_SETOWN fd %d to pid %d, " + "errno = %d\n", pid, errno); + return(-retval); + } + new_fd = um_kmalloc(sizeof(*new_fd)); + if(new_fd == NULL) return(-ENOMEM); + new_fd->next = active_fds; + new_fd->id = dev_id; + new_fd->irq = irq; + new_fd->fd = fd; + new_fd->pid = external_pid(NULL); + active_fds = new_fd; + if(fd > max_fd) max_fd = fd; + FD_SET(fd, &active_fd_mask); + return(0); +} + +void free_irq_fd(void *dev_id) +{ + struct irq_fd **prev; + + prev = &active_fds; + while(*prev != NULL){ + if((*prev)->id == dev_id){ + struct irq_fd *old_fd = *prev; + *prev = (*prev)->next; + FD_CLR(old_fd->fd, &active_fd_mask); + kfree(old_fd); + return; + } + prev = &(*prev)->next; + } +} + +void reactivate_fd(int fd) +{ + FD_SET(fd, &active_fd_mask); +} + +void forward_interrupts(int pid) +{ + struct irq_fd *irq; + + for(irq=active_fds;irq != NULL;irq = irq->next){ + if(fcntl(irq->fd, F_SETOWN, pid) < 0){ + int save_errno = errno; + if(fcntl(irq->fd, F_GETOWN, 0) != pid){ + printk("Failed to forward %d to pid %d, " + "errno = %d\n", irq->fd, pid, + save_errno); + } + } + irq->pid = pid; + } +} + +void init_irq_signals(int on_sigstack) +{ + int flags; + + flags = on_sigstack ? SA_ONSTACK : 0; + set_handler(SIGVTALRM, (__sighandler_t) irq_handler, flags, SIGUSR1, + SIGVTALRM, SIGALRM, SIGIO, SIGUSR2, -1); + set_handler(SIGIO, (__sighandler_t) irq_handler, flags, SIGUSR1, + SIGVTALRM, SIGALRM, SIGIO, SIGUSR2, -1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/ksyms.c linux.ac/arch/um/kernel/ksyms.c --- linux.vanilla/arch/um/kernel/ksyms.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/ksyms.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,17 @@ +#include "linux/module.h" +#include "linux/string.h" +#include "asm/current.h" +#include "asm/delay.h" +#include "asm/processor.h" +#include "asm/unistd.h" +#include "kern_util.h" +#include "user_util.h" + +EXPORT_SYMBOL(stop); +EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(physmem); +EXPORT_SYMBOL(current_task); +EXPORT_SYMBOL(set_signals); +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(__const_udelay); +EXPORT_SYMBOL(sys_waitpid); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/mem.c linux.ac/arch/um/kernel/mem.c --- linux.vanilla/arch/um/kernel/mem.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/mem.c Wed May 23 23:52:07 2001 @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/types.h" +#include "linux/mm.h" +#include "linux/fs.h" +#include "linux/init.h" +#include "linux/bootmem.h" +#include "linux/swap.h" +#include "asm/page.h" +#include "asm/pgtable.h" +#include "asm/pgalloc.h" +#include "asm/bitops.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +unsigned long high_physmem; + +unsigned long low_physmem; + +unsigned long vm_start; + +unsigned long vm_end; + +pgd_t swapper_pg_dir[1024]; + +unsigned long *empty_zero_page = NULL; + +unsigned long *empty_bad_page = NULL; + +const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n"; + +static unsigned long totalram_pages = 0; + +extern char __init_begin, __init_end; + +void mem_init(void) +{ + max_mapnr = num_physpages = max_low_pfn; + + /* clear the zero-page */ + memset((void *) empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + totalram_pages += free_all_bootmem(); + printk("Memory: %luk available\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10)); +} + +void paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES]; + int i; + + empty_zero_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); + empty_bad_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); + for(i=0;i> PAGE_SHIFT) - + (physmem >> PAGE_SHIFT) - zones_size[0]; + free_area_init(zones_size); +} + +static int meminfo_22 = 0; + +static int meminfo_compat(char *str) +{ + meminfo_22 = 1; + return(1); +} + +__setup("22_meminfo", meminfo_compat); + +void si_meminfo(struct sysinfo *val) +{ + val->totalram = totalram_pages; + val->sharedram = atomic_read(&shmem_nrpages); + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; + val->mem_unit = PAGE_SIZE; + if(meminfo_22){ + val->freeram <<= PAGE_SHIFT; + val->bufferram <<= PAGE_SHIFT; + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; + } +} + +pte_t __bad_page(void) +{ + clear_page(empty_bad_page); + return pte_mkdirty(mk_pte((struct page *) empty_bad_page, + PAGE_SHARED)); +} + +/* This can't do anything because nothing in the kernel image can be freed + * since it's not in kernel physical memory. + */ + +void free_initmem(void) +{ +} + +#ifdef CONFIG_BLK_DEV_INITRD + +void free_initrd_mem(unsigned long start, unsigned long end) +{ + if (start < end) + printk ("Freeing initrd memory: %ldk freed\n", + (end - start) >> 10); + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(virt_to_page(start)); + set_page_count(virt_to_page(start), 1); + free_page(start); + totalram_pages++; + } +} + +#endif + +int do_check_pgt_cache(int low, int high) +{ + int freed = 0; + if(pgtable_cache_size > high) { + do { + if (pgd_quicklist) { + free_pgd_slow(get_pgd_fast()); + freed++; + } + if (pmd_quicklist) { + pmd_free_slow(pmd_alloc_one_fast(NULL, 0)); + freed++; + } + if (pte_quicklist) { + pte_free_slow(pte_alloc_one_fast(NULL, 0)); + freed++; + } + } while(pgtable_cache_size > low); + } + return freed; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/mprot.h linux.ac/arch/um/kernel/mprot.h --- linux.vanilla/arch/um/kernel/mprot.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/mprot.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,6 @@ +#ifndef __MPROT_H__ +#define __MPROT_H__ + +extern void no_access(unsigned long addr, unsigned int len); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/process.c linux.ac/arch/um/kernel/process.c --- linux.vanilla/arch/um/kernel/process.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/process.c Sun May 13 19:44:10 2001 @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef PROFILING +#include +#endif +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" +#include "sysdep/ptrace.h" + +void stop_pid(int pid) +{ + kill(pid, SIGSTOP); +} + +void kill_pid(int pid) +{ + kill(pid, SIGKILL); +} + +void usr1_pid(int pid) +{ + kill(pid, SIGUSR1); +} + +void cont_pid(int pid) +{ + kill(pid, SIGCONT); +} + +void set_sigstack(void *sig_stack) +{ + stack_t stack; + + stack.ss_sp = (__ptr_t) sig_stack; + stack.ss_flags = 0; + stack.ss_size = 2 * page_size() - sizeof(void *); + if(sigaltstack(&stack, NULL) != 0) + panic("sigaltstack failed"); +} + +void set_handler(int sig, void (*handler)(int), int flags, ...) +{ + struct sigaction action; + va_list ap; + int mask; + + va_start(ap, flags); + action.sa_handler = handler; + sigemptyset(&action.sa_mask); + while((mask = va_arg(ap, int)) != -1){ + sigaddset(&action.sa_mask, mask); + } + action.sa_flags = flags; + action.sa_restorer = NULL; + if(sigaction(sig, &action, NULL) < 0) + panic("sigaction failed"); +} + +void init_new_thread(void *sig_stack, void (*usr1_handler)(int)) +{ + int flags = 0; + + if(sig_stack != NULL){ + set_sigstack(sig_stack); + flags = SA_ONSTACK; + } + set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags, SIGVTALRM, + SIGUSR1, SIGALRM, SIGIO, -1); + set_handler(SIGTRAP, (__sighandler_t) sig_handler, flags, SIGVTALRM, + SIGUSR1, SIGALRM, SIGIO, -1); + set_handler(SIGFPE, (__sighandler_t) sig_handler, flags, SIGVTALRM, + SIGUSR1, SIGALRM, SIGIO, -1); + set_handler(SIGUSR2, process_stack_handler, SA_NODEFER, SIGUSR1, -1); + change_sig(SIGUSR2, 1); + if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1); + signal(SIGCHLD, SIG_IGN); + set_timers(1); + init_irq_signals(sig_stack != NULL); +} + +int fork_tramp(void *sig_stack) +{ + block_signals(); + init_new_thread(sig_stack, finish_fork_handler); + kill(getpid(), SIGSTOP); + return(0); +} + +struct tramp { + int (*tramp)(void *); + unsigned long temp_stack; + unsigned long stack; + int flags; + int pid; +}; + +int outer_tramp(void *arg) +{ + struct tramp *t; + + t = arg; + t->pid = clone(t->tramp, (void *) t->temp_stack + page_size()/2, + t->flags, (void *) t->stack); + if(t->pid > 0) wait_for_stop(t->pid, SIGSTOP, PTRACE_CONT); + kill(getpid(), SIGKILL); + exit(0); +} + +int start_fork_tramp(unsigned long sig_stack, unsigned long temp_stack, + int clone_vm, int (*tramp)(void *)) +{ + struct tramp arg; + unsigned long sp; + int new_pid, flags, status, err; + + /* The trampoline will run on the temporary stack */ + sp = stack_sp(temp_stack); + + flags = CLONE_FILES | SIGCHLD; + if(clone_vm) flags |= CLONE_VM; + + arg.tramp = tramp; + arg.temp_stack = temp_stack; + arg.stack = sig_stack; + arg.flags = flags; + + /* Start the process and wait for it to stop itself */ + new_pid = clone(outer_tramp, (void *) sp, flags, &arg); + if(new_pid < 0) return(-errno); + while((err = waitpid(new_pid, &status, 0) < 0) && (errno == EINTR)) ; + if(err < 0) panic("Waiting for outer trampoline failed - errno = %d", + errno); + if(!WIFSIGNALED(status) || (WTERMSIG(status) != SIGKILL)) + panic("outer trampoline didn't exit 0"); + + return(arg.pid); +} + +void trace_myself(void) +{ + if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) + panic("ptrace failed in trace_myself"); +} + +void signal_handler(void *task, unsigned long h, int sig) +{ + void (*handler)(int, struct sigcontext); + struct sigcontext sc; + void *regs; + unsigned long cr2; + int err; + + regs = process_state(task, &cr2, &err); + fill_in_sigcontext(&sc, regs, cr2, err); + handler = (void (*)(int, struct sigcontext)) h; + (*handler)(sig, sc); +} + +void continue_fork(void *task, int pid, struct sys_pt_regs *regs) +{ + if(ptrace(PTRACE_CONT, pid, 0, SIGUSR1) < 0) + tracer_panic("Couldn't continue forked process"); + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); + if(ptrace_setregs(pid, regs) < 0) + tracer_panic("Couldn't restore registers"); +} + +int setup_altstack(unsigned long stack, int stack_size) +{ + struct sys_pt_regs *regs; + unsigned long sp; + int pid; + + sp = stack + stack_size - sizeof(void *); + pid = clone(alt_stack_handler, (void *) sp, + CLONE_VM | CLONE_FILES | SIGCHLD, NULL); + if(pid < 0) return(-errno); + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); + regs = altstack_state(NULL, NULL, NULL); + if(ptrace_getregs(pid, regs) < 0) + panic("Couldn't get altstack state"); + save_altstack(NULL, UM_SP(regs)); + kill(pid, SIGKILL); + if(waitpid(pid, NULL, WNOHANG) < 0) + printk("setup_altstack : waitpid failed, errno = %d\n", + errno); + return(0); +} + +void change_sig(int signal, int on) +{ + sigset_t sigset; + + sigemptyset(&sigset); + sigaddset(&sigset, signal); + sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, NULL); +} + +void *last_brk; + +void *update_brk(void) +{ + void *current_brk; + int len, off; + + current_brk = sbrk(0); + if(current_brk == last_brk) return(current_brk); + off = last_brk - brk_start; + if(lseek(brk_fd, off, SEEK_SET) != off){ + panic("update_brk : lseek failed - errno = %d", errno); + } + len = current_brk - last_brk; + if(write(brk_fd, last_brk, len) != len){ + panic("update_brk : write failed - errno = %d", errno); + } + last_brk = current_brk; + return(current_brk); +} + +void check_brk(void *process_brk) +{ + if(last_brk == process_brk) return; + if(mmap(process_brk, last_brk - process_brk, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, + brk_fd, process_brk - brk_start) != process_brk){ + panic("check_brk : mmap failed - errno = %d", errno); + } +} + +int get_one_stack(char **stack_out, struct sys_pt_regs *regs_out) +{ + void *stack; + unsigned long sp; + int pid, n; + + if((stack = mmap(NULL, page_size(), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED){ + perror("get_one_stack : couldn't mmap stack"); + exit(1); + } + sp = stack_sp((unsigned long) stack); + pid = clone(syscall_handler, (void *) sp, SIGCHLD, NULL); + if(pid < 0){ + perror("get_one_stack : couldn't start thread"); + exit(1); + } + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); + if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){ + perror("get_one_stack : couldn't attach to child"); + exit(1); + } + if(ptrace_getregs(pid, regs_out) < 0){ + perror("get_one_stack : couldn't get registers"); + exit(1); + } + sp = UM_SP(regs_out); + sp &= ~sizeof(unsigned long); + while(sp < (unsigned long) stack + page_size()){ + *((unsigned long *) sp) = ptrace(PTRACE_PEEKDATA, pid, + (void *) sp, NULL); + sp += sizeof(unsigned long); + } + ptrace(PTRACE_KILL, pid, 0, 0); + waitpid(pid, NULL, 0); + n = page_size() - (UM_SP(regs_out) & ~page_mask()); + *stack_out = stack; + + return(n); +} + +void attach_process(int pid) +{ + if((ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) || + (ptrace(PTRACE_CONT, pid, 0, 0) < 0)) + tracer_panic("OP_FORK failed to attach pid"); + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); +} + +int signal_frame_size; +static void *local_addr; + +static void frame_size(int sig) +{ + int n; + + signal_frame_size = (unsigned long) local_addr - (unsigned long) &n; +} + +void calc_sigframe_size(void) +{ + int n; + + signal(SIGUSR1, frame_size); + local_addr = &n; + usr1_pid(getpid()); +} + +void tracer_panic(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vprintf(format, ap); + while(1) sleep(10); +} + +#ifdef PROFILING +void remap_profiling_buffers(void) +{ + unsigned long low = 1 << 31, high = 0; + + if((unsigned long) _gmonparam.kcount < low) + low = (unsigned long) _gmonparam.kcount; + if((unsigned long) _gmonparam.froms < low) + low = (unsigned long) _gmonparam.froms; + if((unsigned long) _gmonparam.tos < low) + low = (unsigned long) _gmonparam.tos; + + if((unsigned long) _gmonparam.kcount + _gmonparam.kcountsize > high) + high = (unsigned long) _gmonparam.kcount + + _gmonparam.kcountsize; + if((unsigned long) _gmonparam.froms + _gmonparam.fromssize > high) + high = (unsigned long) _gmonparam.froms + _gmonparam.fromssize; + if((unsigned long) _gmonparam.tos + _gmonparam.tossize > high) + high = (unsigned long) _gmonparam.tos + _gmonparam.tossize; + + remap_data((void *) round_down(low), (void *) round_up(high)); +} +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/process_kern.c linux.ac/arch/um/kernel/process_kern.c --- linux.vanilla/arch/um/kernel/process_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/process_kern.c Sun May 13 19:41:45 2001 @@ -0,0 +1,810 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/interrupt.h" +#include "linux/mm.h" +#include "linux/malloc.h" +#include "linux/utsname.h" +#include "linux/fs.h" +#include "linux/utime.h" +#include "linux/smp_lock.h" +#include "linux/module.h" +#include "linux/init.h" +#include "asm/unistd.h" +#include "asm/mman.h" +#include "asm/segment.h" +#include "asm/stat.h" +#include "asm/pgtable.h" +#include "asm/pgalloc.h" +#include "asm/spinlock.h" +#include "asm/uaccess.h" +#include "asm/user.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +struct cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { -1, NULL } }; + +static struct task_struct *get_task(int pid, int require) +{ + struct task_struct *task, *ret; + + ret = NULL; + read_lock(&tasklist_lock); + for_each_task(task){ + if(task->pid == pid){ + ret = task; + break; + } + } + read_unlock(&tasklist_lock); + if(require && (ret == NULL)) panic("get_task couldn't find a task\n"); + return(ret); +} + +int external_pid(void *t) +{ + struct task_struct *task = t; + if(task == NULL) task = current; + return(task->thread.extern_pid); +} + +void free_stack(unsigned long stack) +{ + free_page(stack); +} + +void set_init_pid(int pid) +{ + init_task.thread.extern_pid = pid; +} + +int set_user_thread(void *t, int on, int restore_regs) +{ + struct task_struct *task; + int ret; + + if(t == NULL) task = current; + else task = t; + if(on == task->thread.tracing) return(on); + ret = task->thread.tracing; + task->thread.request.op = on ? OP_TRACE_ON : OP_TRACE_OFF; + task->thread.request.u.tracing.restore_regs = restore_regs; + usr1_pid(getpid()); + return(ret); +} + +void set_tracing(void *task, int tracing) +{ + ((struct task_struct *) task)->thread.tracing = tracing; +} + +int is_tracing(void *t) +{ + struct task_struct *task; + + if(t == NULL) task = current; + else task = t; + return(task->thread.tracing); +} + +extern void schedule_tail(struct task_struct *prev); + +static int new_thread_proc(void *t) +{ + struct task_struct *task; + int (*fn)(void *), pid; + void *arg; + + task = t; + trace_myself(); + init_new_thread(NULL, NULL); + pid = getpid(); + fn = task->thread.request.u.thread.proc; + arg = task->thread.request.u.thread.arg; + task->thread.extern_pid = pid; + stop_pid(pid); + set_cmdline("(kernel thread)"); + init_flush_vm(); + if(current->thread.request.u.cswitch.from != NULL) + schedule_tail(current->thread.request.u.cswitch.from); + (*fn)(arg); + do_exit(0); + return(0); +} + +unsigned long alloc_stack(void) +{ + unsigned long page; + + if((page = __get_free_page(GFP_KERNEL)) == 0) + panic("Couldn't allocate new stack"); + stack_protections(page, PAGE_SIZE); + return(page); +} + +extern int inited_cpus; + +static int start_kernel_thread(struct task_struct *task, int (*fn)(void *), + void *arg, int cpu) +{ + int extern_pid; + unsigned long sp; + + sp = ((unsigned long) task) + 4 * PAGE_SIZE - sizeof(void *); + task->thread.request.u.thread.proc = fn; + task->thread.request.u.thread.arg = arg; + task->thread.extern_pid = -1; + extern_pid = clone_and_wait(new_thread_proc, task, (void *) sp, + CLONE_FILES | SIGCHLD); + if(task->thread.extern_pid == -1) + tracer_panic("task didn't set its pid"); + task->mm = NULL; + task->active_mm = NULL; +#ifdef __SMP__ + if(cpu != -1){ + cpu_tasks[cpu].pid = extern_pid; + cpu_tasks[cpu].task = task; + inited_cpus++; + init_tasks[cpu] = task; + cpu_number_map[cpu] = cpu; + task->processor = cpu; + cont_pid(extern_pid); + } +#endif + return(extern_pid); +} + +static int kernel_thread1(int (*fn)(void *), void * arg, unsigned long flags, + int cpu, int *extern_pid_out) +{ + struct task_struct *new_task; + int pid, extern_pid; + + pid = do_fork(CLONE_VM | flags, 0, NULL, 0); + if(pid < 0) panic("do_fork failed in kernel_thread"); + new_task = get_task(pid, 1); + current->thread.request.op = OP_THREAD; + current->thread.request.u.thread.proc = fn; + current->thread.request.u.thread.arg = arg; + current->thread.request.u.thread.flags = flags; + current->thread.request.u.thread.new_task = new_task; + current->thread.request.u.thread.cpu = cpu; + usr1_pid(getpid()); + extern_pid = current->thread.request.u.thread.new_pid; + if(extern_pid < 0){ + printk("Kernel thread failed : errno = %d", -extern_pid); + return(extern_pid); + } + if(extern_pid_out != NULL) *extern_pid_out = extern_pid; + current->thread.request.u.cswitch.from = NULL; + return(pid); +} + +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + return(kernel_thread1(fn, arg, flags, -1, NULL)); +} + +void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk, unsigned cpu) +{ + if (prev != next) + clear_bit(cpu, &prev->cpu_vm_mask); + set_bit(cpu, &next->cpu_vm_mask); +} + +void *_switch_to(void *prev, void *next) +{ + struct task_struct *from, *to; + + from = prev; + to = next; + current->thread.request.op = OP_SWITCH; + current->thread.request.u.cswitch.to = next; + current->thread.brk = update_brk(); + forward_interrupts(to->thread.extern_pid); + block_signals(); + usr1_pid(getpid()); + flush_tlb_kernel_vm(); + check_brk(current->thread.brk); + unblock_signals(); + return(current->thread.request.u.cswitch.from); +} + +void do_bh(void) +{ +#ifndef __SMP__ + if (softirq_active(0) & softirq_mask(0)){ + do_softirq(); + unblock_signals(); + } +#else +#error Need to update do_bh +#endif +} + +void ret_from_sys_call(void *t) +{ + struct task_struct *task; + + task = t; + if(task == NULL) task = current; + do_bh(); + if(task->need_resched) schedule(); + if((task->sigpending != 0) && (task->thread.npending == 0)){ + block_signals(); + do_signal(task, NULL, NULL); + unblock_signals(); + } +} + +void release_thread(struct task_struct *task) +{ +} + +void exit_thread(void) +{ + unprotect_stack((unsigned long) current); +} + +char *generic_stack = NULL; +int generic_stack_size = 0; +struct sys_pt_regs generic_regs; +struct sys_pt_regs generic_regs_mask; + +void set_syscall_regs(void *t) +{ + struct sys_pt_regs *regs; + int i; + struct task_struct *task = t; + + regs = &task->thread.syscall_regs; + for(i = 0; i < sizeof(regs->regs)/sizeof(regs->regs[0]); i++){ + task->thread.syscall_regs.regs[i] = generic_regs.regs[i]; + if(generic_regs_mask.regs[i]) + task->thread.syscall_regs.regs[i] += + task->thread.kernel_stack + PAGE_SIZE; + } +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, + unsigned long stack_top, struct task_struct * p, + struct pt_regs * regs) +{ + int new_pid, clone_vm; + unsigned long stack; + + p->thread = (struct thread_struct) INIT_THREAD; + p->thread.kernel_stack = (unsigned long) p + 2 * PAGE_SIZE; + p->thread.tracing = current->thread.forking; + set_syscall_regs(p); + if(current->thread.forking){ + p->thread.tracing = 0; + stack = alloc_stack(); + clone_vm = (p->mm == current->mm); + new_pid = start_fork_tramp(p->thread.kernel_stack, stack, + clone_vm, fork_tramp); + if(new_pid < 0){ + printk("copy_thread : clone failed with errno = %d", + -new_pid); + return(new_pid); + } + current->thread.request.op = OP_FORK; + current->thread.request.u.fork.pid = new_pid; + usr1_pid(getpid()); + p->thread.process_regs = current->thread.process_regs; + UM_SYSCALL_RET(&p->thread.process_regs) = 0; + if(sp != 0) UM_SP(&p->thread.process_regs) = sp; + p->thread.extern_pid = new_pid; + p->thread.request.op = OP_FORK_FINISH; + p->thread.request.u.fork_finish.stack = stack; + } + current->need_resched = 1; + return(0); +} + +void tracing_reboot(void) +{ + current->thread.request.op = OP_REBOOT; + usr1_pid(getpid()); +} + +void tracing_halt(void) +{ + current->thread.request.op = OP_HALT; + usr1_pid(getpid()); +} + +void tracing_cb(void (*proc)(void *), void *arg) +{ + if(getpid() == tracing_pid){ + (*proc)(arg); + } + else { + current->thread.request.op = OP_CB; + current->thread.request.u.cb.proc = proc; + current->thread.request.u.cb.arg = arg; + usr1_pid(getpid()); + } +} + +struct { + struct task_struct *from; + struct task_struct *to; + int processor; +} switch_record[1024]; + +int switch_index = 0; + +int do_proc_op(void *t, int proc_id) +{ + struct task_struct *task, *to; + struct thread_struct *thread; + int op, pid; + + task = t; + thread = &task->thread; + op = thread->request.op; + switch(op){ + case OP_NONE: + case OP_TRACE_ON: + case OP_TRACE_OFF: + break; + case OP_EXEC: + do_exec(thread->extern_pid, thread->request.u.exec.pid); + break; + case OP_SWITCH: + if((task->state == TASK_ZOMBIE) && + (thread->extern_pid != -1)) + kill_pid(thread->extern_pid); + to = thread->request.u.cswitch.to; + switch_record[switch_index].from = task; + switch_record[switch_index].to = to; + switch_record[switch_index++].processor = to->processor; + if(switch_index == 1024) switch_index = 0; +#ifdef __SMP__ + cpu_tasks[proc_id].task = to; + cpu_tasks[proc_id].pid = to->thread.extern_pid; + if(cpu_tasks[0].pid == cpu_tasks[1].pid) + tracer_panic("Scheduled a process on two processors"); +#else + current = to; +#endif + if(to->thread.request.op == OP_FORK_FINISH){ + to->thread.request.u.fork_finish.from = task; + continue_fork(to, to->thread.extern_pid, + &to->thread.process_regs); + to->thread.request.op = OP_NONE; + set_tracing(to, 1); + } + else to->thread.request.u.cswitch.from = task; + cont_pid(to->thread.extern_pid); + break; + case OP_THREAD: + pid = start_kernel_thread(thread->request.u.thread.new_task, + thread->request.u.thread.proc, + thread->request.u.thread.arg, + thread->request.u.thread.cpu); + thread->request.u.thread.new_pid = pid; + break; + case OP_FORK: + attach_process(thread->request.u.fork.pid); + break; + case OP_CB: + (*thread->request.u.cb.proc)(thread->request.u.cb.arg); + break; + case OP_REBOOT: + case OP_HALT: + break; + default: + tracer_panic("Bad op in do_proc_op"); + break; + } + thread->request.op = OP_NONE; + return(op); +} + +unsigned long stack_sp(unsigned long page) +{ + return(page + PAGE_SIZE - sizeof(void *)); +} + +int current_pid(void) +{ + return(current->pid); +} + +static void do_idle(void) +{ + idle_timer(); + + while(1){ + /* endless idle loop with no priority at all */ + current->nice = 20; + current->counter = -100; + + /* + * although we are an idle CPU, we do not want to + * get into the scheduler unnecessarily. + */ + if (current->need_resched) { + schedule(); + check_pgt_cache(); + } + idle_sleep(10); + } +} + +static int idle_proc(void *unused) +{ + del_from_runqueue(current); + init_idle(); +#ifdef __SMP__ + smp_num_cpus++; +#endif + do_idle(); + return(0); +} + +int cpu_idle(void) +{ + int i, pid; + + if(ncpus > 1){ + printk("Starting up other processors:\n"); + for(i=1;imm == NULL) return(0xffffffff); + pgd = pgd_offset(task->mm, addr); + pmd = pmd_offset(pgd, addr); + if(!pmd_present(*pmd)) return(0xffffffff); + pte = pte_offset(pmd, addr); + if(!pte_present(*pte)) return(0xffffffff); + return((pte_val(*pte) & PAGE_MASK) + (addr & ~PAGE_MASK)); +} + +char *current_cmd(void) +{ +#ifdef __SMP__ + return("(Unknown)"); +#else + unsigned long addr; + + if((addr = um_virt_to_phys(current, + current->mm->arg_start)) == 0xffffffff) + return("(Unknown)"); + else return((char *) addr); +#endif +} + +void force_sigbus(void) +{ + printk("Killing pid %d because of a lack of memory\n", current->pid); + lock_kernel(); + sigaddset(¤t->pending.signal, SIGBUS); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(SIGBUS | 0x80); +} + +void finish_fork_handler(int sig) +{ + unsigned long stack; + + init_flush_vm(); + check_brk(brk_start); + if(current->mm != current->p_pptr->mm) + protect(physmem, high_physmem - physmem, 1, 1, 0); + stack = (unsigned long) current; + protect(stack + PAGE_SIZE, PAGE_SIZE, 0, 0, 0); + stack_protections(stack + 2 * PAGE_SIZE, 2 * PAGE_SIZE); + if(current->thread.request.u.fork_finish.from) + schedule_tail(current->thread.request.u.fork_finish.from); + free_page(current->thread.request.u.fork_finish.stack); + change_sig(SIGUSR1, 1); + unblock_signals(); + kill(getpid(), SIGSTOP); +} + +void *get_current_task(void) +{ + return(current); +} + +void *process_state(void *t, unsigned long *cr2_out, int *err_out) +{ + struct task_struct *task; + + if(t == NULL) task = current; + else task = t; + if(cr2_out) *cr2_out = task->thread.cr2; + if(err_out) *err_out = task->thread.err; + return(&task->thread.process_regs); +} + +struct sys_pt_regs *syscall_state(void *t, void **stack_out, int *size_out) +{ + struct task_struct *task; + + task = t; + *stack_out = generic_stack; + *size_out = generic_stack_size; + return(&task->thread.syscall_regs); +} + +int get_repeat_syscall(void *t) +{ + struct task_struct *task; + + if(t == NULL) task = current; + else task = t; + return(task->thread.repeat_syscall); +} + +void set_repeat_syscall(int again) +{ + current->thread.repeat_syscall = again; +} + +void save_altstack(void *t, unsigned long sp) +{ + struct task_struct *task; + void *stack; + int n; + + if(t == NULL) task = current; + else task = t; + if(task->thread.altstack != NULL) kfree(task->thread.altstack); + n = PAGE_SIZE - (sp & ~PAGE_MASK); + stack = kmalloc(n, GFP_KERNEL); + if(stack == NULL) panic("save_altstack : kmalloc failed"); + memcpy(stack, (void *) sp, n); + task->thread.altstack = stack; + task->thread.altstack_size = n; +} + +struct sys_pt_regs *altstack_state(void *t, void **stack_out, int *size_out) +{ + struct task_struct *task; + + if(t == NULL) task = current; + else task = t; + if(stack_out != NULL) *stack_out = task->thread.altstack; + if(size_out != NULL) *size_out = task->thread.altstack_size; + return(&task->thread.altstack_regs); +} + +extern int signal_frame_size; + +void probe_stack(unsigned long sp) +{ + int n, delta; + + delta = signal_frame_size; + get_user(n, (int *) sp); + put_user(n, (int *) sp); + get_user(n, (int *) (sp - delta)); + put_user(n, (int *) (sp - delta)); +} + +void dump_thread(struct pt_regs *regs, struct user *u) +{ +} + +int have_signals(void *t, int altstack) +{ + struct task_struct *task; + + if(t == NULL) task = current; + else task = t; + if(task->thread.npending == 1){ + if((altstack && (task->thread.signal.sp != 0)) || + (!altstack && (task->thread.signal.sp == 0))) + return(1); + } + return(0); +} + +int switching_modes(void *t) +{ + struct task_struct *task; + + task = t; + return(task->thread.request.op == OP_TRACE_OFF); +} + +void enable_hlt(void) +{ + panic("enable_hlt"); +} + +void disable_hlt(void) +{ + panic("disable_hlt"); +} + +void interrupt_end(void) +{ + if(current->need_resched) schedule(); + if(current->thread.npending == 0) do_signal(current, NULL, NULL); + if(current->thread.npending > 0) + probe_stack(UM_SP(¤t->thread.process_regs)); +} + +void *um_kmalloc(int size) +{ + return(kmalloc(size, GFP_KERNEL)); +} + +void *get_fault_addr(void) +{ + return(current->thread.fault_addr); +} + +EXPORT_SYMBOL(get_fault_addr); + +void set_fault_addr(void *addr) +{ + current->thread.fault_addr = addr; +} + +EXPORT_SYMBOL(set_fault_addr); + +int singlestepping(void *t) +{ + struct task_struct *task; + int ret; + + task = (struct task_struct *) t; + ret = (task->ptrace & PT_DTRACE); + task->ptrace &= ~PT_DTRACE; + return(ret); +} + +void not_implemented(void) +{ + printk("Something isn't implemented in here\n"); +} + +EXPORT_SYMBOL(not_implemented); + +void setup_kernel_stack(void) +{ + struct sys_pt_regs regs1, regs2; + char *stack1, *stack2; + unsigned long s1, s2, n1, n2, off1, off2; + int size, i; + + size = get_one_stack(&stack1, ®s1); + if(get_one_stack(&stack2, ®s2) != size){ + printf("setup_kernel_stack : differing stack sizes\n"); + exit(1); + } + s1 = (unsigned long) stack1; + s2 = (unsigned long) stack2; + generic_stack = malloc(size); + generic_stack_size = size; + if(generic_stack == NULL){ + perror("setup_kernel_stack : allocating new stack"); + exit(1); + } + for(i = PAGE_SIZE - size; i < PAGE_SIZE - sizeof(void *); i++){ + /* This is horribly word-length- and byte-order-dependent */ + n1 = stack1[i] | (stack1[i + 1] << 8) | + (stack1[i + 2] << 16) | stack1[i + 3] << 24; + n2 = stack2[i] | (stack2[i + 1] << 8) | + (stack2[i + 2] << 16) | stack2[i + 3] << 24; + + /* Internal pointers have to be different on different + * stacks, and they have to point to the stack page. If not, + * then keep looking. + */ + if((n1 == n2) || ((n1 & PAGE_MASK) != s1) || + ((n2 & PAGE_MASK) != s2)) continue; + + /* The offsets have to be the same and they have to point + * within the used area of the stack. + */ + off1 = n1 & ~PAGE_MASK; + off2 = n2 & ~PAGE_MASK; + if((off1 == off2) && (off1 > PAGE_SIZE - size)){ + printf("Found a frame pointer on the stack\n"); + exit(1); + } + } + memcpy(generic_stack, &stack1[PAGE_SIZE - size], size); + if((munmap(stack1, PAGE_SIZE) < 0) || + (munmap(stack2, PAGE_SIZE) < 0)){ + perror("setup_kernel_stack : unmapping temp stacks"); + exit(1); + } + for(i = 0; i < sizeof(regs1.regs)/sizeof(regs1.regs[0]); i++){ + generic_regs.regs[i] = regs1.regs[i]; + generic_regs_mask.regs[i] = 0; + if((regs1.regs[i] == regs2.regs[i]) || + ((regs1.regs[i] & PAGE_MASK) != s1) || + ((regs2.regs[i] & PAGE_MASK) != s2)) continue; + off1 = regs1.regs[i] & ~PAGE_MASK; + off2 = regs2.regs[i] & ~PAGE_MASK; + if((off1 == off2) && (off1 >= PAGE_SIZE - size)){ + generic_regs.regs[i] = regs1.regs[i] - s1; + generic_regs_mask.regs[i] = 1; + } + } +} + +int user_context(unsigned long sp) +{ + return((sp & (PAGE_MASK << 1)) != current->thread.kernel_stack); +} + +int get_restore_regs(void *t) +{ + struct task_struct *task = t; + + return(task->thread.request.u.tracing.restore_regs); +} + +extern void remove_pid_file(void); +__exitcall(remove_pid_file); + +extern exitcall_t __exitcall_begin, __exitcall_end; + +void do_exitcalls(void) +{ + exitcall_t *call; + + call = &__exitcall_end; + while (--call >= &__exitcall_begin) + (*call)(); +} + +void *round_up(unsigned long addr) +{ + return(ROUND_UP(addr)); +} + +void *round_down(unsigned long addr) +{ + return(ROUND_DOWN(addr)); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/ptrace.c linux.ac/arch/um/kernel/ptrace.c --- linux.vanilla/arch/um/kernel/ptrace.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/ptrace.c Sun May 13 19:41:45 2001 @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/mm.h" +#include "linux/errno.h" +#include "linux/smp_lock.h" +#include "asm/ptrace.h" +#include "asm/uaccess.h" +#include "kern_util.h" + +int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int i, ret; + + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + if (child == current) + goto out_tsk; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) + goto out_tsk; + /* the same process cannot be attached many times */ + if (child->ptrace & PT_PTRACED) + goto out_tsk; + child->ptrace |= PT_PTRACED; + + write_lock_irq(&tasklist_lock); + if (child->p_pptr != current) { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + write_unlock_irq(&tasklist_lock); + + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + goto out_tsk; + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + ret = -EIO; + if(overlaps_kernel(addr, sizeof(tmp))) break; + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0) + break; + + tmp = 0; /* Default return condition */ + if(addr < 17*sizeof(long)){ + tmp = getreg(child, addr); + ret = put_user(tmp,(unsigned long *) data); + } + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = -EIO; + if (access_process_vm(child, addr, &data, sizeof(data), + 1) != sizeof(data)) + break; + ret = 0; + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret = -EIO; + if ((addr & 3) || addr < 0) + break; + + if (addr < 17*sizeof(long)) { + ret = putreg(child, addr, data); + break; + } + + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + child->ptrace |= PT_DTRACE; + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: { /* detach a process that was attached. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); + child->exit_code = data; + write_lock_irq(&tasklist_lock); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irq(&tasklist_lock); + wake_up_process(child); + ret = 0; + break; + } + +#ifdef PTRACE_GETREGS + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + if (!access_ok(VERIFY_WRITE, (unsigned *)data, 17*sizeof(long))) { + ret = -EIO; + break; + } + for ( i = 0; i < 17*sizeof(long); i += sizeof(long) ) { + __put_user(getreg(child, i),(unsigned long *) data); + data += sizeof(long); + } + ret = 0; + break; + } +#endif +#ifdef PTRACE_SETREGS + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp = 0; + if (!access_ok(VERIFY_READ, (unsigned *)data, 17*sizeof(long))) { + ret = -EIO; + break; + } + for ( i = 0; i < 17*sizeof(long); i += sizeof(long) ) { + __get_user(tmp, (unsigned long *) data); + putreg(child, i, tmp); + data += sizeof(long); + } + ret = 0; + break; + } +#endif +#ifdef PTRACE_GETFPREGS + case PTRACE_GETFPREGS: { /* Get the child FPU state. */ + ret = -EIO; + break; + } +#endif +#ifdef PTRACE_SETFPREGS + case PTRACE_SETFPREGS: { /* Set the child FPU state. */ + ret = -EIO; + break; + } +#endif + default: + ret = -EIO; + break; + } + out_tsk: + free_task_struct(child); + out: + unlock_kernel(); + return ret; +} + +void syscall_trace(void) +{ + if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) + != (PT_PTRACED|PT_TRACESYS)) + return; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/reboot.c linux.ac/arch/um/kernel/reboot.c --- linux.vanilla/arch/um/kernel/reboot.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/reboot.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +static void kill_off_processes(void) +{ + struct task_struct *p; + int me; + + me = getpid(); + for_each_task(p){ + if(p->thread.extern_pid != me) kill_pid(p->thread.extern_pid); + } + kill_pid(init_task.thread.extern_pid); +} + +void machine_restart(char * __unused) +{ + kill_off_processes(); + do_exitcalls(); + tracing_reboot(); + kill_pid(getpid()); +} + +void machine_power_off(void) +{ + kill_off_processes(); + do_exitcalls(); + tracing_halt(); + kill_pid(getpid()); +} + +void machine_halt(void) +{ + machine_power_off(); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/resource.c linux.ac/arch/um/kernel/resource.c --- linux.vanilla/arch/um/kernel/resource.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/resource.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/pci.h" + +unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, + unsigned long start, unsigned long size) +{ + return start; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/segment.c linux.ac/arch/um/kernel/segment.c --- linux.vanilla/arch/um/kernel/segment.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/segment.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/mm.h" +#include "asm/mmu_context.h" + +void release_segments(struct mm_struct *mm) +{ + struct mm_changes *changes = mm->context.segments; + + if(changes != NULL) free_page((unsigned long) changes); + mm->context.segments = NULL; +} + +void activate_mm(struct mm_struct *old_mm, struct mm_struct *new_mm) +{ + struct mm_changes *new; + + new = NULL; /* (struct mm_changes *) get_free_page(GFP_KERNEL); */ + new_mm->context.segments = new; + if(new == NULL) return; + new->count = 0; + new->barrier = -1; +} + +void copy_segments(struct task_struct *p, struct mm_struct *new_mm) +{ + activate_mm(p->mm, new_mm); + p->thread.mm_changes = 0; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/setup.c linux.ac/arch/um/kernel/setup.c --- linux.vanilla/arch/um/kernel/setup.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/setup.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "asm/processor.h" + +struct cpuinfo_um boot_cpu_data = { 0, 0, 0, 0 }; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/signal_kern.c linux.ac/arch/um/kernel/signal_kern.c --- linux.vanilla/arch/um/kernel/signal_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/signal_kern.c Thu Apr 12 17:43:20 2001 @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/stddef.h" +#include "linux/sys.h" +#include "linux/sched.h" +#include "linux/wait.h" +#include "linux/kernel.h" +#include "linux/smp_lock.h" +#include "linux/module.h" +#include "asm/signal.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +EXPORT_SYMBOL(block_signals); +EXPORT_SYMBOL(unblock_signals); + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +#define _S(nr) (1<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +/* + * OK, we're invoking a handler + */ +static int handle_signal(struct task_struct *task, unsigned long signr, + struct k_sigaction *ka, sigset_t *oldset, + unsigned long *error) +{ + __sighandler_t handler; + sigset_t save; + int ret = 0; + + if((error != NULL) && (*error != 0)){ + switch (*error) { + case -ERESTARTNOHAND: + *error = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + *error = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + ret = 1; + break; + } + } + handler = ka->sa.sa_handler; + + save = *oldset; + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(&task->sigmask_lock); + sigorsets(&task->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(&task->blocked,signr); + recalc_sigpending(task); + spin_unlock_irq(&task->sigmask_lock); + } + + if(task->thread.npending == 1) panic("Too many queued signals"); + task->thread.signal.signal = signr; + task->thread.signal.sp = 0; + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! on_sig_stack(UM_SP(¤t->thread.process_regs))) + task->thread.signal.sp = + current->sas_ss_sp + current->sas_ss_size; + } + + task->thread.signal.handler = (unsigned long) handler; + task->thread.npending = 1; + task->thread.saved_sigs = save; + + return(ret); +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Note that we go through the signals twice: once to check the signals that + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + */ + +static int kern_do_signal(void *t, sigset_t *oldset, unsigned long *error, + int *again_out) +{ + struct task_struct *task; + siginfo_t info; + struct k_sigaction *ka; + int again; + + task = t; + if(task == NULL) task = current; + if (!oldset) + oldset = ¤t->blocked; + + if(again_out) *again_out = 0; + for (;;) { + unsigned long signr; + + spin_lock_irq(&task->sigmask_lock); + signr = dequeue_signal(&task->blocked, &info); + spin_unlock_irq(&task->sigmask_lock); + + if (!signr) + break; + + if ((task->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + task->exit_code = signr; + task->state = TASK_STOPPED; + notify_parent(task, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = task->exit_code)) + continue; + task->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = task->p_pptr->pid; + info.si_uid = task->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(&task->blocked, signr)) { + send_sig_info(signr, &info, task); + continue; + } + } + + ka = &task->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (task->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(task->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + task->state = TASK_STOPPED; + task->exit_code = signr; + if (!(task->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(task, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if(do_coredump(signr, NULL)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + sigaddset(&task->pending.signal, signr); + recalc_sigpending(task); + task->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + /* Whee! Actually deliver the signal. */ + again = handle_signal(task, signr, ka, oldset, error); + if(again_out && (*again_out == 0)) *again_out = again; + return(1); + } + return(0); +} + +int do_signal(void *t, unsigned long *error, int *again_out) +{ + return(kern_do_signal(t, NULL, error, again_out)); +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +int +sys_sigsuspend(int history0, int history1, old_sigset_t mask) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (kern_do_signal(current, &saveset, NULL, NULL)) + return -EINTR; + } +} + +int +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (kern_do_signal(current, &saveset, NULL, NULL)) + return -EINTR; + } +} + +void signal_deliverer(void) +{ + struct task_struct *task; + struct sys_pt_regs regs; + sigset_t sigs; + unsigned long handler; + int repeat, signal; + +#ifdef __SMP__ +#error signal_deliverer needs some SMP work +#else + task = current; +#endif + regs = task->thread.process_regs; + repeat = task->thread.repeat_syscall; + sigs = task->thread.saved_sigs; + signal = task->thread.signal.signal; + handler = task->thread.signal.handler; + task->thread.npending = 0; + signal_handler(task, handler, signal); + task->thread.process_regs = regs; + task->thread.repeat_syscall = repeat; + task->thread.saved_sigs = sigs; +} + +void set_sigreturn_syscall(int syscall) +{ + current->thread.sigreturn_syscall = syscall; +} + +int sys_sigreturn(struct sys_pt_regs regs) +{ + sigdelsetmask(¤t->thread.saved_sigs, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = current->thread.saved_sigs; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + UM_SYSCALL_NR(¤t->thread.process_regs) = + current->thread.sigreturn_syscall; + return(UM_SYSCALL_RET(®s)); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/signal_user.c linux.ac/arch/um/kernel/signal_user.c --- linux.vanilla/arch/um/kernel/signal_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/signal_user.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include "user_util.h" +#include "user.h" + +static void change_signals(int type) +{ + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, SIGVTALRM); + sigaddset(&mask, SIGALRM); + sigaddset(&mask, SIGIO); + if(sigprocmask(type, &mask, NULL) < 0) + panic("Failed to change signal mask"); +} + +void block_signals(void) +{ + change_signals(SIG_BLOCK); +} + +void unblock_signals(void) +{ + change_signals(SIG_UNBLOCK); +} + +#define SIGIO_BIT 0 +#define SIGVTALRM_BIT 1 + +static int enable_mask(sigset_t *mask) +{ + int sigs; + + sigs = sigismember(mask, SIGIO) ? 0 : 1 << SIGIO_BIT; + sigs |= sigismember(mask, SIGVTALRM) ? 0 : 1 << SIGVTALRM_BIT; + sigs |= sigismember(mask, SIGALRM) ? 0 : 1 << SIGVTALRM_BIT; + return(sigs); +} + +int set_signals(int enable) +{ + sigset_t mask; + int ret; + + sigemptyset(&mask); + if(enable & (1 << SIGIO_BIT)) sigaddset(&mask, SIGIO); + if(enable & (1 << SIGVTALRM_BIT)){ + sigaddset(&mask, SIGVTALRM); + sigaddset(&mask, SIGALRM); + } + if(sigprocmask(SIG_UNBLOCK, &mask, &mask) < 0) + panic("Failed to enable signals"); + ret = enable_mask(&mask); + sigemptyset(&mask); + if((enable & (1 << SIGIO_BIT)) == 0) sigaddset(&mask, SIGIO); + if((enable & (1 << SIGVTALRM_BIT)) == 0){ + sigaddset(&mask, SIGVTALRM); + sigaddset(&mask, SIGALRM); + } + if(sigprocmask(SIG_BLOCK, &mask, NULL) < 0) + panic("Failed to block signals"); + return(ret); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/smp.c linux.ac/arch/um/kernel/smp.c --- linux.vanilla/arch/um/kernel/smp.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/smp.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/threads.h" +#include "asm/smp.h" +#include "asm/processor.h" +#include "asm/spinlock.h" +#include "asm/softirq.h" +#include "asm/hardirq.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" + +#ifdef __SMP__ + +/* Total count of live CPUs */ +int smp_num_cpus = 0; + +/* The 'big kernel lock' */ +spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; + +/* Per CPU bogomips and other parameters */ +struct cpuinfo_um cpu_data[NR_CPUS]; + +/* which CPU maps to which logical number */ +int cpu_number_map[NR_CPUS]; + +spinlock_t um_bh_lock = SPIN_LOCK_UNLOCKED; + +atomic_t global_bh_count; +atomic_t global_bh_lock; + +unsigned char global_irq_holder = NO_PROC_ID; +unsigned volatile int global_irq_lock; + +/* Set when the idlers are all forked */ +int smp_threads_ready = 0; +int num_reschedules_sent = 0; + +void smp_send_reschedule(int cpu) +{ + num_reschedules_sent++; +} + +static void show(char * str) +{ + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d:\n", str, cpu); +} + +#define MAXCOUNT 100000000 + +static inline void wait_on_bh(void) +{ + int count = MAXCOUNT; + do { + if (!--count) { + show("wait_on_bh"); + count = ~0; + } + /* nothing .. wait for the other bh's to go away */ + } while (atomic_read(&global_bh_count) != 0); +} + +/* + * This is called when we want to synchronize with + * bottom half handlers. We need to wait until + * no other CPU is executing any bottom half handler. + * + * Don't wait if we're already running in an interrupt + * context or are inside a bh handler. + */ +void synchronize_bh(void) +{ + if (atomic_read(&global_bh_count) && !in_interrupt()) + wait_on_bh(); +} + +void smp_send_stop(void) +{ + printk("Stopping all CPUs\n"); +} + +void smp_commence(void) +{ +} + +void smp_boot_cpus(void) +{ + if(ncpus < 1){ + printk("ncpus set to 1\n"); + ncpus = 1; + } + else if(ncpus > NR_CPUS){ + printk("ncpus can't be greater than NR_CPUS, set to %d\n", NR_CPUS); + ncpus = NR_CPUS; + } +} + +int setup_profiling_timer(unsigned int multiplier) +{ + printk("setup_profiling_timer\n"); + return(0); +} + +#endif + +int inited_cpus = 1; + +int pid_to_processor_id(int pid) +{ + int i; + + for(i=0;ithread.forking = 1; + ret = do_fork(SIGCHLD, 0, NULL, 0); + current->thread.forking = 0; + return(ret); +} + +long sys_clone(unsigned long clone_flags, unsigned long newsp) +{ + long ret; + + current->thread.forking = 1; + ret = do_fork(clone_flags, newsp, NULL, 0); + current->thread.forking = 0; + return(ret); +} + +long sys_vfork(void) +{ + long ret; + + current->thread.forking = 1; + ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, NULL, 0); + current->thread.forking = 0; + return(ret); +} + +/* common code for old and new mmaps */ +static inline long do_mmap2( + unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + int error = -EBADF; + struct file * file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); + out: + return error; +} + +long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * calls. Linux/i386 didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +int old_mmap(struct mmap_arg_struct *arg) +{ + struct mmap_arg_struct a; + int err = -EFAULT; + + if (copy_from_user(&a, arg, sizeof(a))) + goto out; + + err = -EINVAL; + if (a.offset & ~PAGE_MASK) + goto out; + + err = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT); + out: + return err; +} +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way unix traditionally does this, though. + */ +int sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +int sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +struct sel_arg_struct { + unsigned long n; + fd_set *inp, *outp, *exp; + struct timeval *tvp; +}; + +int old_select(struct sel_arg_struct *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + /* sys_select() does the appropriate kernel locking */ + return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +int sys_ipc (uint call, int first, int second, + int third, void *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + return sys_semop (first, (struct sembuf *)ptr, second); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + + case MSGSND: + return sys_msgsnd (first, (struct msgbuf *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + panic("msgrcv with version != 0"); + return sys_msgrcv (first, + (struct msgbuf *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, (struct msqid_ds *) ptr); + + case SHMAT: + switch (version) { + default: { + ulong raddr; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong *) third); + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + return -EINVAL; + return sys_shmat (first, (char *) ptr, second, (ulong *) third); + } + case SHMDT: + return sys_shmdt ((char *)ptr); + case SHMGET: + return sys_shmget (first, second, third); + case SHMCTL: + return sys_shmctl (first, second, + (struct shmid_ds *) ptr); + default: + return -EINVAL; + } +} + +int sys_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down_read(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up_read(&uts_sem); + return err?-EFAULT:0; +} + +int sys_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + down_read(&uts_sem); + + error = __copy_to_user(&name->sysname,&system_utsname.sysname, + __OLD_UTS_LEN); + error |= __put_user(0,name->sysname+__OLD_UTS_LEN); + error |= __copy_to_user(&name->nodename,&system_utsname.nodename, + __OLD_UTS_LEN); + error |= __put_user(0,name->nodename+__OLD_UTS_LEN); + error |= __copy_to_user(&name->release,&system_utsname.release, + __OLD_UTS_LEN); + error |= __put_user(0,name->release+__OLD_UTS_LEN); + error |= __copy_to_user(&name->version,&system_utsname.version, + __OLD_UTS_LEN); + error |= __put_user(0,name->version+__OLD_UTS_LEN); + error |= __copy_to_user(&name->machine,&system_utsname.machine, + __OLD_UTS_LEN); + error |= __put_user(0,name->machine+__OLD_UTS_LEN); + + up_read(&uts_sem); + + error = error ? -EFAULT : 0; + + return error; +} + +int sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + unsigned long old_sp = current->sas_ss_sp; + int old_size = current->sas_ss_size, res; + + res = do_sigaltstack(uss, uoss, UM_SP(¤t->thread.process_regs)); + if((res == 0) || (current->sas_ss_sp != old_sp) || + (current->sas_ss_size != old_size)){ + res = setup_altstack(current->sas_ss_sp, + current->sas_ss_size); + } + return(res); +} + +int nsyscalls = 0; + +long execute_syscall(struct sys_pt_regs regs) +{ + long res; + int syscall; + + current->thread.nsyscalls++; + nsyscalls++; + syscall = UM_SYSCALL_NR(®s); + if(syscall == -1) + panic("syscall thread activated without a system call"); + if((syscall >= NR_syscalls) || (syscall < 0)) + return(-ENOSYS); + res = (*sys_call_table[syscall])(regs); + return(res); +} + +spinlock_t syscall_lock = SPIN_LOCK_UNLOCKED; + +void lock_syscall(void) +{ + spin_lock(&syscall_lock); +} + +void unlock_syscall(void) +{ + spin_unlock(&syscall_lock); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/syscall_user.c linux.ac/arch/um/kernel/syscall_user.c --- linux.vanilla/arch/um/kernel/syscall_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/syscall_user.c Sun May 13 20:23:29 2001 @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +/* XXX FIXME : Ensure that SIGIO and SIGVTALRM can't happen immediately + * after setting up syscall stack + * SIGIO and SIGVTALRM should block SIGUSR2 + * block SIGVTALRM in any code that's under wait_for_stop + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "sysdep/ptrace.h" + +/* XXX Bogus */ +#define ERESTARTSYS 512 +#define ERESTARTNOINTR 513 +#define ERESTARTNOHAND 514 + +struct { + int syscall; + int pid; + int result; + struct timeval start; + struct timeval end; +} syscall_record[1024]; + +int syscall_index = 0; + +void process_stack_handler(int sig) +{ + signal_deliverer(); +} + +int alt_stack_handler(void *arg) +{ + ptrace(PTRACE_TRACEME, 0, 0, 0); + kill(getpid(), SIGSTOP); + signal_deliverer(); + return(0); +} + +int syscall_handler(void *unused) +{ + struct sys_pt_regs *regs; + long result; + int index, syscall, again; + + kill(getpid(), SIGSTOP); + syscall_trace(); + lock_syscall(); + if(syscall_index == 1024) syscall_index = 0; + index = syscall_index; + syscall_index++; + unlock_syscall(); + regs = process_state(NULL, NULL, NULL); + syscall = UM_SYSCALL_NR(regs); + syscall_record[index].syscall = UM_SYSCALL_NR(regs); + syscall_record[index].pid = current_pid(); + syscall_record[index].result = 0xdeadbeef; + gettimeofday(&syscall_record[index].start, NULL); + result = execute_syscall(*regs); + again = 0; + if((result == -ERESTARTNOHAND) || (result == -ERESTARTSYS) || + (result == -ERESTARTNOINTR)) + do_signal(NULL, &result, &again); + UM_SYSCALL_RET(regs) = result; + set_repeat_syscall(again); + syscall_trace(); + syscall_record[index].result = UM_SYSCALL_RET(regs); + gettimeofday(&syscall_record[index].end, NULL); + ret_from_sys_call(NULL); + if(have_signals(NULL, 0)) probe_stack(UM_SP(regs)); + /* XXX + * This is a race, set_user_thread has to be called with signals off + */ + set_user_thread(NULL, 1, 1); + return(0); +} + +int exit_kernel(int pid, void *task, int *signal_out) +{ + void *stack; + struct sys_pt_regs *regs; + unsigned long sp; + int tracing, again, n, restore; + + tracing = 1; + *signal_out = 0; + again = get_repeat_syscall(task); + set_repeat_syscall(0); + restore = get_restore_regs(task); + if(restore){ + regs = process_state(task, NULL, NULL); + if(ptrace_setregs(pid, regs) < 0) + tracer_panic("Couldn't restore registers"); + } + if(have_signals(task, 0)) + *signal_out = SIGUSR2; + else if(have_signals(task, 1)){ + regs = altstack_state(task, &stack, &n); + if(ptrace_setregs(pid, regs) < 0) + panic("Couldn't set alternate stack state"); + sp = um_virt_to_phys(task, UM_SP(regs)); + memcpy((void *) sp, stack, n); + } + else if(again){ + regs = syscall_state(task, &stack, &n); + if(ptrace_setregs(pid, regs) < 0) + panic("Couldn't restart system call"); + memcpy((void *) UM_SP(regs), stack, n); + tracing = 0; + } + return(tracing); +} + +extern unsigned long _stext, _etext; + +int do_syscall(void *task, int pid) +{ + void *stack; + struct sys_pt_regs *regs, proc_regs; + int syscall, n; + + if(ptrace_getregs(pid, &proc_regs) < 0) + tracer_panic("Couldn't read registers"); + + syscall = UM_SYSCALL_NR(&proc_regs); + if(syscall < 1) return(0); + + if((syscall != __NR_sigreturn) && + ((unsigned long *) UM_IP(&proc_regs) >= &_stext) && + ((unsigned long *) UM_IP(&proc_regs) <= &_etext)) + tracer_panic("I'm tracing myself and I can't get out"); + regs = process_state(task, NULL, NULL); + if(syscall == __NR_sigreturn){ + set_sigreturn_syscall(UM_SYSCALL_NR(regs)); + UM_SYSCALL_NR(regs) = __NR_sigreturn; + } + else *regs = proc_regs; + set_tracing(task, 0); + regs = syscall_state(task, &stack, &n); + if(ptrace_setregs(pid, regs) < 0) + tracer_panic("Couldn't set system call state"); + memcpy((void *) UM_SP(regs), stack, n); + + if((ptrace(PTRACE_POKEUSER, pid, UM_SYSCALL_NR_OFFSET, + __NR_getpid) < 0) || + (ptrace(PTRACE_CONT, pid, 0, 0) < 0)){ + printk("Failed to change syscall number to __NR_getpid\n"); + if(errno == EIO){ + printk("You probably didn't apply the ptrace patch " + "to your hosting kernel\n"); + } + else printk("errno = %d\n", errno); + tracer_panic("do_syscall : Couldn't force getpid"); + } + return(1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/time.c linux.ac/arch/um/kernel/time.c --- linux.vanilla/arch/um/kernel/time.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/time.c Sun May 13 19:44:45 2001 @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#define _GNU_SOURCE /* to get timeradd and timersub */ + +#include +#include + +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" + +void timer_handler(int sig, void *sc, int usermode) +{ + timer_irq(usermode); +} + +static struct itimerval profile_interval; + +void get_profile_timer(void) +{ + getitimer(ITIMER_PROF, &profile_interval); +} + +static void set_interval(int timer_type) +{ + struct itimerval interval; + + interval.it_interval.tv_sec = 0; + interval.it_interval.tv_usec = 1000000/hz(); + interval.it_value.tv_sec = 0; + interval.it_value.tv_usec = 1000000/hz(); + if(setitimer(timer_type, &interval, NULL) == -1) + panic("setitimer failed - errno = %d\n", errno); +} + +void idle_timer(void) +{ + if(signal(SIGVTALRM, SIG_IGN) == SIG_ERR) + panic("Couldn't unset SIGVTALRM handler"); + set_handler(SIGALRM, (__sighandler_t) irq_handler, 0, SIGUSR1, + SIGVTALRM, SIGALRM, SIGIO, SIGUSR2, -1); + set_interval(ITIMER_REAL); +} + +void time_init(void) +{ + if(signal(SIGVTALRM, boot_timer_handler) == SIG_ERR) + panic("Couldn't set SIGVTALRM handler"); + set_interval(ITIMER_VIRTUAL); +} + +void set_timers(int set_signal) +{ + if(set_signal){ + if(signal(SIGVTALRM, (__sighandler_t) irq_handler) == SIG_ERR) + panic("Couldn't set SIGVTALRM handler"); + set_interval(ITIMER_VIRTUAL); + } + if(setitimer(ITIMER_PROF, &profile_interval, NULL) == -1) + panic("setitimer ITIMER_PROF failed - errno = %d\n", errno); +} + +struct timeval local_offset = { 0, 0 }; + +void do_gettimeofday(struct timeval *tv) +{ + gettimeofday(tv, NULL); + timeradd(tv, &local_offset, tv); +} + +void do_settimeofday(struct timeval *tv) +{ + struct timeval now; + + gettimeofday(&now, NULL); + timersub(tv, &now, &local_offset); +} + +void idle_sleep(int secs) +{ + struct timespec ts; + + ts.tv_sec = secs; + ts.tv_nsec = 0; + nanosleep(&ts, &ts); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/time_kern.c linux.ac/arch/um/kernel/time_kern.c --- linux.vanilla/arch/um/kernel/time_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/time_kern.c Sun May 13 19:41:45 2001 @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/unistd.h" +#include "linux/stddef.h" +#include "linux/spinlock.h" +#include "linux/sched.h" +#include "linux/interrupt.h" +#include "linux/init.h" +#include "linux/delay.h" +#include "asm/param.h" +#include "asm/current.h" +#include "kern_util.h" +#include "user_util.h" + +extern rwlock_t xtime_lock; + +extern struct timeval xtime; + +int hz(void) +{ + return(HZ); +} + +int timer_irq_inited = 0; + +void timer_irq(int user_mode) +{ + if(!timer_irq_inited) return; + do_IRQ(TIMER_IRQ, user_mode); +} + +void boot_timer_handler(int sig) +{ + struct pt_regs regs; + + regs.user_mode = 0; + do_timer(®s); +} + +void timer(int irq, void *dev, struct pt_regs *regs) +{ + do_timer(regs); + write_lock(&xtime_lock); + gettimeofday(&xtime, NULL); + write_unlock(&xtime_lock); +} + +void __delay(um_udelay_t time) +{ + int i; + + for(i=0;ithread.extern_pid != -1) && + (current->thread.extern_pid != getpid())) + panic("fix_range fixing wrong address space"); + mm = proc_mm; + if(mm == NULL){ + start_addr = start_vm; + end_addr = end_vm; + } + for(addr=start_addr;addrvm_start <= addr)){ + addr = vma->vm_end; + continue; + } + } + + if((addr >= start_vm) && (addr < end_vm)) mm = &init_mm; + else mm = proc_mm; + npgd = pgd_offset(mm, addr); + npmd = pmd_offset(npgd, addr); + if(pmd_present(*npmd)){ + npte = pte_offset(npmd, addr); + r = pte_read(*npte); + w = pte_write(*npte); + x = pte_exec(*npte); + if(!pte_dirty(*npte)) w = 0; + if(!pte_young(*npte)){ + r = 0; + w = 0; + } + if(mm == &init_mm){ + unsigned long mask; + + mask = PAGE_MASK | _PAGE_NEWPAGE | + _PAGE_NEWPROT; + if((pte_val(*npte) & ~mask) == + pgprot_val(PAGE_KERNEL)){ + r = 1; + w = 1; + x = 1; + } + else if((pte_val(*npte) & ~mask) == + pgprot_val(PAGE_KERNEL_RO)){ + r = 1; + w = 0; + x = 1; + } + } + if(force || !pte_present(*npte) || pte_newpage(*npte)){ + munmap((void *) addr, PAGE_SIZE); + if(pte_present(*npte)) + map(addr, + page_address(pte_page(*npte)), + PAGE_SIZE, r, w, x); + } + else if(pte_newprot(*npte)) + protect(addr, PAGE_SIZE, r, w, x); + if((mm == proc_mm) && pte_present(*npte)) + *npte = pte_mkuptodate(*npte); + addr += PAGE_SIZE; + } + else { + unsigned long end, len; + + end = (addr + PMD_SIZE) & PMD_MASK; + len = end - addr; + if(force || pmd_newpage(*npmd)){ + munmap((void *) addr, len); + if(mm == proc_mm) + pmd_mkuptodate(*npmd); + } + addr += len; + } + } +} + +void init_flush_vm(void) +{ + pgd_t *npgd; + pmd_t *npmd; + pte_t *npte; + unsigned long addr; + int r, w, x; + + for(addr=start_vm;addrmm) || + (atomic_read(&mm->mm_count) > 1))){ + struct mm_changes *changes = mm->context.segments; + if(changes != NULL){ + changes->barrier = changes->count; + barriers++; + } + } + if(mm == current->mm) fix_range(mm, start, end, 0); +} + +void flush_tlb_mm(struct mm_struct *mm) +{ + flush_tlb_range(mm, 0, STACK_TOP); +} + +void flush_tlb_kernel_vm(void) +{ + if(current->mm != NULL){ + struct mm_changes *changes = current->mm->context.segments; + if((changes == NULL) || + (current->thread.mm_changes < changes->barrier) || + (changes->count - current->thread.mm_changes > + CHANGES_PAGES(changes))){ + range_flushes++; + fix_range(current->mm, 0, STACK_TOP, 0); + if(changes != NULL) + current->thread.mm_changes = changes->count; + } + else { + unsigned long page; + int i; + + for(i=current->thread.mm_changes;icount;i++){ + page_flushes++; + page = CHANGES_PAGE(changes, i); + fix_range(current->mm, page, + page + PAGE_SIZE, 0); + } + current->thread.mm_changes = changes->count; + } + } + fix_range(NULL, start_vm, end_vm, 0); +} + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long address) +{ + address &= PAGE_MASK; +#ifdef notdef + if((vma->vm_mm != current->mm) || + (atomic_read(&vma->vm_mm->mm_count) > 1)){ + struct mm_changes *changes = vma->vm_mm->segments; + if(changes != NULL){ + ADD_CHANGE(changes, address); + page_changes++; + } + } +#endif + if(vma->vm_mm == current->mm) + fix_range(current->mm, address, address + PAGE_SIZE, 0); +} + +void flush_tlb_all(void) +{ + flush_tlb_range(current->mm, 0, STACK_TOP); +} + +void force_flush_all(void) +{ + fix_range(current->mm, 0, STACK_TOP, 1); +} + +static pgprot_t vm_prot(char r, char w, char x, char p) +{ + if((r == '-') && (w == '-') && (x == '-')) return(PAGE_NONE); + else if(w == '-') return(PAGE_READONLY); + else if(p == 'p') return(PAGE_COPY); + else return(PAGE_SHARED); +} + +static unsigned short vm_flags(char r, char w, char x, char p) +{ + unsigned short flags; + + flags = 0; + if(r == 'r') flags |= VM_READ; + if(w == 'w') flags |= VM_WRITE; + if(x == 'x') flags |= VM_EXEC; + if(p == '-') flags |= VM_SHARED; + return(flags); +} + +static struct vm_area_struct *process_vmas; +static int num_process_vmas = 0; + +void add_perm_vma(unsigned long start, unsigned long end, char rperm, + char wperm, char xperm, char private) +{ + struct vm_area_struct *vma; + + vma = &process_vmas[num_process_vmas++]; + *vma = ((struct vm_area_struct) { + &kernel_maps, start, end, NULL, + vm_prot(rperm, wperm, xperm, private), + vm_flags(rperm, wperm, xperm, private), 0, NULL, NULL, NULL, + NULL, NULL, 0, NULL, 0 + }); + insert_vm_struct(&kernel_maps, vma); +} + +void add_process_vmas(void) +{ + unsigned long start, end; + void *maps; + char rperm, wperm, xperm, private; + + if(process_vmas == NULL){ + int count = 0; + + maps = open_maps(); + while(read_map(maps, NULL, NULL, NULL, NULL, NULL, NULL)) + count++; + close_maps(maps); + process_vmas = malloc((count + 1) * sizeof(*process_vmas)); + if(process_vmas == NULL) + panic("Couldn't allocate process_vmas"); + } + maps = open_maps(); + while(read_map(maps, &start, &end, &rperm, &wperm, &xperm, &private)){ + if(start == 0x40000000) continue; + add_perm_vma(start, end, rperm, wperm, xperm, private); + } + close_maps(maps); +} + +pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address) +{ + return(pgd_offset(mm, address)); +} + +pmd_t *pmd_offset_proc(pgd_t *pgd, unsigned long address) +{ + return(pmd_offset(pgd, address)); +} + +pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address) +{ + return(pte_offset(pmd, address)); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/trap_kern.c linux.ac/arch/um/kernel/trap_kern.c --- linux.vanilla/arch/um/kernel/trap_kern.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/trap_kern.c Sun May 13 19:43:32 2001 @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/sched.h" +#include "linux/mm.h" +#include "linux/spinlock.h" +#include "linux/config.h" +#include "linux/init.h" +#include "asm/semaphore.h" +#include "asm/pgtable.h" +#include "asm/pgalloc.h" +#include "asm/a.out.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "chan.h" +#include "debug.h" + +extern int nsyscalls; + +unsigned long segv(unsigned long address, unsigned long ip, int is_write, + int is_user) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + struct siginfo si; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned long page; + int ok; + + if(mm == NULL) panic("Segfault with no mm"); + down_read(&mm->mmap_sem); + vma = find_vma(mm, address); + ok = 1; + if(!vma) ok = 0; + else if(vma->vm_start > address){ + if((vma->vm_flags & VM_STACK_FLAGS) != VM_STACK_FLAGS) ok = 0; + else if(expand_stack(vma, address)) ok = 0; + } + if(!ok){ + if(current->thread.fault_addr != NULL){ + unsigned long new_ip = + (unsigned long) current->thread.fault_addr; + current->thread.fault_addr = (void *) address; + up_read(&mm->mmap_sem); + return(new_ip); + } + if(!is_user) + panic("Kernel mode fault at addr 0x%lx, ip 0x%lx", + address, ip); + si.si_signo = SIGSEGV; + si.si_code = SEGV_ACCERR; + si.si_addr = (void *) address; + current->thread.cr2 = address; + current->thread.err = is_write; + force_sig_info(SIGSEGV, &si, current); + up_read(&mm->mmap_sem); + return(0); + } + page = address & PAGE_MASK; + if(page == (unsigned long) current + PAGE_SIZE) + panic("Kernel stack overflow"); + pgd = pgd_offset(mm, page); + pmd = pmd_offset(pgd, page); + do { + switch (handle_mm_fault(mm, vma, address, is_write)) { + case 1: + current->min_flt++; + break; + case 2: + current->maj_flt++; + break; + case 0: + up_read(&mm->mmap_sem); + force_sigbus(); + return(0); + default: + up_read(&mm->mmap_sem); + force_sig(SIGBUS, current); + return(0); + } + pte = pte_offset(pmd, page); + pte_mkyoung(*pte); + if(is_write) pte_mkdirty(*pte); + } while(is_write && !pte_write(*pte)); + if(is_write && !pte_write(*pte)) panic("page not writeable"); + if(!is_write && !pte_present(*pte)){ + printk("Page disappeared while handling fault"); + force_sigbus(); + } + flush_tlb_page(vma, page); + up_read(&mm->mmap_sem); + return(0); +} + +void relay_signal(int sig, void *sc, int usermode) +{ + force_sig(sig, current); +} + +void trap_init(void) +{ +} + +spinlock_t trap_lock = SPIN_LOCK_UNLOCKED; + +void lock_trap(void) +{ + spin_lock(&trap_lock); +} + +void unlock_trap(void) +{ + spin_unlock(&trap_lock); +} + +extern int debugger_pid; +extern int debugger_fd; + +#ifdef CONFIG_PT_PROXY + +void debugger_signal(int status, pid_t pid) +{ + debugger_proxy(status, pid); +} + +void child_signal(pid_t pid, int status) +{ + child_proxy(pid, status); +} + +struct io_chan gdb_chan = XTERM_IO_CHAN_INIT(0, "", 1, INIT_STATIC); +char *gdb_init = "xterm"; + +static void gdb_announce(char *dev_name, int dev) +{ + printf("gdb assigned device '%s'\n", dev_name); +} + +static struct chan_opts opts = { + announce: gdb_announce, + dev: -1, + xterm_title: "UML kernel debugger", + raw_pty: 0 +}; + +void signal_usr1(int sig) +{ + if(debugger_pid != -1){ + printk("The debugger is already running\n"); + return; + } + if(parse_chan_pair(gdb_init, 0, &gdb_chan, INIT_ONE, &opts)) return; + debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd); + init_proxy(debugger_pid, 0, 0); +} + +int init_ptrace_proxy(int idle_pid, int startup, int stop) +{ + int pid, status; + + if(parse_chan_pair(gdb_init, 0, &gdb_chan, INIT_ONE, &opts)){ + ptrace(PTRACE_CONT, idle_pid, 0, 0); + return(-1); + } + pid = start_debugger(linux_prog, startup, stop, &debugger_fd); + status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT); + if(pid < 0){ + ptrace(PTRACE_CONT, idle_pid, 0, 0); + return(-1); + } + init_proxy(pid, 1, status); + return(pid); +} + +int attach_debugger(int idle_pid, int pid) +{ + int status; + + if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){ + printf("Failed to attach pid %d, errno = %d\n", pid, errno); + return(-1); + } + status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT); + init_proxy(pid, 1, status); + return(pid); +} + +void exit_debugger(void) +{ + close_chan_pair(&gdb_chan); +} + +__exitcall(exit_debugger); + +#else + +void debugger_signal(int status, pid_t pid){ } +void child_signal(pid_t pid, int status){ } +int init_ptrace_proxy(int idle_pid, int startup, int stop) +{ + printk("debug requested when CONFIG_PT_PROXY is off\n"); + wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT); + ptrace(PTRACE_CONT, idle_pid, 0, 0); + return(-1); +} + +void signal_usr1(int sig) +{ + printk("debug requested when CONFIG_PT_PROXY is off\n"); +} + +int attach_debugger(int idle_pid, int pid) +{ + printk("attach_debugger called when CONFIG_PT_PROXY is off\n"); + return(-1); +} +#endif +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/trap_user.c linux.ac/arch/um/kernel/trap_user.c --- linux.vanilla/arch/um/kernel/trap_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/trap_user.c Sun May 13 19:41:45 2001 @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" +#include "sysdep/sigcontext.h" + +static void signal_segv(int sig) +{ + write(2, "Seg fault in signals\n", strlen("Seg fault in signals\n")); + for(;;) ; +} + +int detach(int pid) +{ + return(ptrace(PTRACE_DETACH, pid, 0, SIGSTOP)); +} + +int debug = 0; +int debug_stop = 1; + +static int signal_tramp(void *arg) +{ + int (*proc)(void *); + + if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) + panic("ptrace PTRACE_TRACEME failed"); + signal(SIGUSR1, SIG_IGN); + signal(SIGSEGV, (__sighandler_t) sig_handler); + set_timers(0); + set_cmdline("(idle thread)"); + set_init_pid(getpid()); + proc = arg; + return((*proc)(NULL)); +} + +#ifdef __SMP__ +#error need to make these arrays +#endif + +int debugger_pid = -1; +int debugger_fd = -1; +int gdb_pid = -1; + +struct { + unsigned long address; + int is_write; + int pid; + unsigned long sp; + int is_user; +} segfault_record[1024]; + +int segfault_index = 0; + +struct { + int pid; + int signal; + unsigned long addr; + struct timeval time; +} signal_record[1024]; + +int signal_index = 0; +int nsignals = 0; +int debug_trace = 0; +extern int io_nsignals, io_count, intr_count; + +extern void signal_usr1(int sig); + +struct timeval last_exit; +int last_status; +int last_pid; + +int tracing_pid; + +int signals(int (*init_proc)(void *), void *sp) +{ + void *task = NULL; + unsigned long eip = 0; + int status, pid = 0, sig, cont_type, tracing = 0, op = 0; + int last_index, proc_id, save_errno = 0; + + setup_kernel_stack(); + calc_sigframe_size(); + signal(SIGSEGV, signal_segv); + signal(SIGUSR1, signal_usr1); + signal(SIGPIPE, SIG_IGN); + tracing_pid = getpid(); + printk("tracing thread pid = %d\n", tracing_pid); + pid = clone(signal_tramp, sp, CLONE_FILES | SIGCHLD, init_proc); + if(debug){ + if(gdb_pid != -1) + debugger_pid = attach_debugger(pid, gdb_pid); + else debugger_pid = init_ptrace_proxy(pid, 1, debug_stop); + } + set_cmdline("(tracing thread)"); + if(debug_trace){ + printk("Tracing thread pausing to be attached\n"); + stop(); + } + while(1){ + if((pid = waitpid(-1, &status, WUNTRACED)) <= 0){ + if(errno != ECHILD){ + printk("wait failed - errno = %d\n", errno); + } + continue; + } + save_errno = errno; + if(pid == debugger_pid){ + if(WIFEXITED(status) || WIFSIGNALED(status)) + debugger_pid = -1; + debugger_signal(status, external_pid(NULL)); + continue; + } + nsignals++; + if(WIFEXITED(status)){ + gettimeofday(&last_exit, NULL); + last_status = WIFEXITED(status); + last_pid = pid; + } +#ifdef notdef + { + printk("Child %d exited with status %d\n", pid, + WEXITSTATUS(status)); + } +#endif + else if(WIFSIGNALED(status)){ + sig = WTERMSIG(status); + if(sig != 9){ + printk("Child %d exited with signal %d\n", pid, + sig); + } + } + else if(WIFSTOPPED(status)){ + sig = WSTOPSIG(status); + if(signal_index == 1024){ + signal_index = 0; + last_index = 1023; + } + else last_index = signal_index - 1; + if(((sig == SIGPROF) || (sig == SIGVTALRM) || + (sig == SIGALRM)) && + (signal_record[last_index].signal == sig) && + (signal_record[last_index].pid == pid)) + signal_index = last_index; + signal_record[signal_index].pid = pid; + gettimeofday(&signal_record[signal_index].time, NULL); + eip = ptrace(PTRACE_PEEKUSER, pid, UM_IP_OFFSET, 0); + signal_record[signal_index].addr = eip; + signal_record[signal_index++].signal = sig; +#ifdef __SMP__ + proc_id = pid_to_processor_id(pid); + task = cpu_tasks[proc_id].task; +#else + proc_id = 0; + task = get_current_task(); +#endif + tracing = is_tracing(task); + switch(sig){ + case SIGUSR1: + sig = 0; + op = do_proc_op(task, proc_id); + switch(op){ + case OP_EXEC: + case OP_SWITCH: + continue; + case OP_TRACE_ON: + tracing = exit_kernel(pid, task, &sig); + break; + case OP_TRACE_OFF: + tracing = 0; + break; + case OP_REBOOT: + case OP_HALT: + ptrace(PTRACE_KILL, pid, 0, 0); + if(debugger_pid != -1){ + kill(debugger_pid, SIGKILL); + close(debugger_fd); + } + return(op == OP_REBOOT); + case OP_NONE: + printk("Detaching pid %d\n", pid); + detach(pid); + continue; + default: + break; + } + break; + case SIGSTOP: + if(debugger_pid != -1) + child_signal(pid, status); + continue; + case SIGBUS: + case SIGILL: + tracer_panic("Unexpectedly got signal %d in " + "signals", sig); + break; + case SIGTRAP: + sig = 0; + if(switching_modes(task)) tracing = 0; + else { + if(!tracing && (debugger_pid != -1)) + child_signal(pid, status); + else if(!do_syscall(task, pid)){ + tracing = 0; + sig = SIGTRAP; + break; + } + continue; + } + break; + case SIGCONT: + break; + case SIGSEGV: + tracing = 0; + break; + case SIGIO: + case SIGALRM: + case SIGVTALRM: + case SIGFPE: + if(!tracing && debugger_pid != -1){ + child_signal(pid, status); + continue; + } + tracing = 0; + break; + case SIGPROF: + if(tracing) sig = 0; + break; + case SIGCHLD: + sig = 0; + break; + default: + if(debugger_pid != -1){ + child_signal(pid, status); + continue; + } + break; + } + set_tracing(task, tracing); + if(tracing != 0){ + if(singlestepping(task)) + cont_type = PTRACE_SINGLESTEP; + else cont_type = PTRACE_SYSCALL; + } + else cont_type = PTRACE_CONT; + + /* XXX This doesn't close the hole totally - there + * is still a race between the ptrace and the child + * continuing. This thread needs separate data from + * the children. + */ + errno = save_errno; + if(ptrace(cont_type, pid, 0, sig) != 0){ + sleep(1); + if(ptrace(cont_type, pid, 0, sig) != 0){ + tracer_panic("ptrace failed to " + "continue process - " + "errno = %d\n", + errno); + } + } + } + } + return(0); +} + +int nsegfaults = 0; + +void segv_handler(int sig, void *sc, int usermode) +{ + struct sigcontext_struct *context = sc; + unsigned long new_ip; + int index; + + lock_trap(); + index = segfault_index++; + if(segfault_index == 1024) segfault_index = 0; + unlock_trap(); + nsegfaults++; + segfault_record[index].address = SC_FAULT_ADDR(context); + segfault_record[index].pid = getpid(); + segfault_record[index].is_write = SC_FAULT_WRITE(context); + segfault_record[index].sp = SC_SP(context); + segfault_record[index].is_user = usermode; + new_ip = segv(SC_FAULT_ADDR(context), SC_IP(context), + SC_FAULT_WRITE(context), usermode); + if(new_ip != 0) SC_IP(context) = new_ip; +} + +static void (*handlers[])(int, void *, int) = { + [ SIGTRAP ] relay_signal, + [ SIGFPE ] relay_signal, + [ SIGSEGV] segv_handler, + [ SIGIO ] sigio_handler, + [ SIGVTALRM ] timer_handler, + [ SIGALRM ] timer_handler +}; + +void irq_handler(int sig, struct sigcontext sc) +{ + int user_mode, save_errno = errno; + + user_mode = user_context(SC_SP(&sc)); + change_sig(SIGUSR1, 1); + (*handlers[sig])(sig, &sc, user_mode); + if(user_mode) interrupt_end(); + block_signals(); + change_sig(SIGUSR1, 0); + set_user_thread(NULL, user_mode, 0); + errno = save_errno; +} + +void sig_handler(int sig, struct sigcontext sc) +{ + int user_mode, save_errno = errno; + + user_mode = user_context(SC_SP(&sc)); + change_sig(SIGUSR1, 1); + unblock_signals(); + (*handlers[sig])(sig, &sc, user_mode); + if(user_mode){ + interrupt_end(); + block_signals(); + } + change_sig(SIGUSR1, 0); + set_user_thread(NULL, user_mode, 0); + errno = save_errno; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/um_arch.c linux.ac/arch/um/kernel/um_arch.c --- linux.vanilla/arch/um/kernel/um_arch.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/um_arch.c Sun May 13 19:41:45 2001 @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/mm.h" +#include "linux/types.h" +#include "linux/tty.h" +#include "linux/init.h" +#include "linux/bootmem.h" +#include "linux/spinlock.h" +#include "linux/utsname.h" +#ifdef CONFIG_BLK_DEV_INITRD +#include "linux/blk.h" +#endif +#include "asm/page.h" +#include "asm/pgtable.h" +#include "asm/ptrace.h" +#include "asm/elf.h" +#include "asm/user.h" +#include "asm/delay.h" +#include "ubd_user.h" +#include "asm/current.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "mprot.h" + +unsigned long _stext; + +#define DEFAULT_COMMAND_LINE "root=/dev/ubd0" + +unsigned long thread_saved_pc(struct thread_struct *thread) +{ + panic("Someone should implement thread_saved_pc"); + return(0); +} + +int get_cpuinfo(char * buffer){ + char *p = buffer; + p += sprintf(p, "processor\t: user-mode\n"); + p += sprintf(p, "bogomips\t: %lu.%02lu\n", + loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ)) % 100); + p += sprintf(p, "host\t\t: %s\n", host_info); + + return(strlen(buffer)); +} + +pte_t * __bad_pagetable(void) +{ + panic("Someone should implement __bad_pagetable"); + return(NULL); +} + +extern void start_kernel(void); + +extern int debug; +extern int debug_stop; + +static int start_kernel_proc(void *unused) +{ + int pid; + + block_signals(); + pid = getpid(); +#ifdef __SMP__ + cpu_tasks[0].pid = pid; + cpu_tasks[0].task = current; + smp_num_cpus = 1; +#else + current_task = &init_task_union.task; +#endif + if(debug) stop_pid(getpid()); + start_kernel(); + return(0); +} + +#define XSTRING(s) #s +#define STRING(s) XSTRING(s) + +int physmem_fd = -1; + +extern unsigned long high_physmem; + +unsigned long physmem; +unsigned long start_vm; +unsigned long end_vm; +int physmem_inode; + +int ncpus = 1; + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + +static char *argv1_begin = NULL; +static char *argv1_end = NULL; + +void set_cmdline(char *cmd) +{ + strcpy(argv1_begin, "["); + strncat(argv1_begin, cmd, argv1_end - argv1_begin - strlen("[]")); + strcat(argv1_begin, "]"); + memset(argv1_begin + strlen(argv1_begin), '\0', + argv1_end - argv1_begin - strlen(argv1_begin)); +} + +static char *usage_string = +"User Mode Linux v%s" +" available at http://user-mode-linux.sourceforge.net/\n\n" +"--help\n Prints this message\n" +"--version\n Gives the version number of the kernel\n" +"root=\n" +" This is actually used by the generic kernel in exactly the same\n" +" way as in any other kernel. If you configure a number of block\n" +" devices and want to boot off something other than ubd0, you \n" +" would use something like:\n" +" root=/dev/ubd5\n\n" +"mem=\n" +" This controls how much \"physical\" memory the kernel allocates\n" +" for the system. The size is specified as a number followed by\n" +" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n" +" This is not related to the amount of memory in the physical\n" +" machine. It can be more, and the excess, if it's ever used, will\n" +" just be swapped out.\n Example: mem=64M\n\n" +#ifdef CONFIG_SMP +"ncpus=<# of desired CPUs>\n" +" This tells an SMP kernel how many virtual processors to start.\n" +" Currently, this has no effect because SMP isn't enabled.\n\n" +#endif +"debugtrace\n" +" Causes the tracing thread to pause until it is attached by a\n" +" debugger and continued. This is mostly for debugging crashes\n" +" early during boot, and should be pretty much obsoleted by\n" +" the debug switch.\n\n" +"debug\n" +" Starts up the kernel under the control of gdb. See the \n" +" kernel debugging tutorial and the debugging session pages\n" +" at http://user-mode-linux.sourceforge.net/ for more information\n\n" +"umn=\n" +" This sets the ip address of the host side of the slip device \n" +" that the umn device configures. This is necessary if you want\n" +" to set up networking, but your local net isn't 192.168.0.x,\n" +" or you want to run multiple virtual machines on a network,\n" +" in which case, you need to assign different ip addresses to the\n" +" different machines.\n\n" +"ubd=\n" +" This is used to associate a device with a file in the underlying\n" +" filesystem. Usually, there is a filesystem in the file, but \n" +" that's not required. Swap devices containing swap files can be\n" +" specified like this. Also, a file which doesn't contain a\n" +" filesystem can have its contents read in the virtual \n" +" machine by running dd on the device. n must be in the range\n" +" 0 to 7. Appending an 'r' to the number will cause that device\n" +" to be mounted read-only. For example ubd1r=./ext_fs\n\n"; + +static void Usage(void) +{ + printf(usage_string, system_utsname.release); + exit(0); +} + +#ifdef CONFIG_BLK_DEV_INITRD +static void read_initrd(char *initrd) +{ + void *area; + int size; + + size = file_size(initrd); + if(size == -1) return; + area = alloc_bootmem(size); + if(area == NULL) return; + if(load_initrd(initrd, area, size) == -1) return; + initrd_start = (unsigned long) area; + initrd_end = initrd_start + size; +} +#endif + +extern unsigned long _stext, _etext, _sdata, _edata, __bss_start, _end; +extern int debug_trace; + +void *brk_start; +int brk_fd; + +int linux_main(int argc, char **argv) +{ + unsigned long start_pfn, end_pfn, bootmap_size; + unsigned long physmem_size, virtmem_size; + unsigned int i, have_root, add; + char *retptr; + void *sp; +#ifdef CONFIG_BLK_DEV_INITRD + char *initrd = NULL; +#endif + + remap_data(ROUND_DOWN(&_stext), ROUND_UP(&_etext)); + remap_data(ROUND_DOWN(&_sdata), ROUND_UP(&_edata)); + brk_start = sbrk(0); + remap_data(ROUND_DOWN(&__bss_start), ROUND_UP(brk_start)); + last_brk = (void *) brk_start; + brk_fd = create_vm_file(0); +#ifdef CONFIG_GPROF + remap_profiling_buffers(); +#endif + + /* Create fake command line from argv[]. */ + have_root = 0; + physmem_size = 16 * 1024 * 1024; + virtmem_size = physmem_size; + for (i = 1; i < argc; i++){ + if((i == 1) && (argv[i][0] == ' ')) continue; + add = 1; + if(!strncmp(argv[i], "root=", strlen("root="))) have_root = 1; + else if(!strncmp(argv[i], "mem=", strlen("mem="))) + physmem_size = memparse(argv[i] + strlen("mem="), + &retptr); +#ifdef __SMP__ + else if(!strncmp(argv[i], "ncpus=", strlen("ncpus="))) + ncpus = strtoul(argv[i] + strlen("ncpus="), NULL, 10); +#endif + else if(!strcmp(argv[i], "debugtrace")) debug_trace = 1; + else if(!strncmp(argv[i], "debug", strlen("debug"))){ + debug = 1; + debug_stop = 1; + if(!strcmp(argv[i], "debug=go")){ + debug_stop = 0; + add = 0; + } + } +#ifdef CONFIG_PT_PROXY + else if(!strncmp(argv[i], "gdb=", strlen("gdb="))) + gdb_init = &argv[i][strlen("gdb=")]; + else if(!strncmp(argv[i], "gdb-pid=", strlen("gdb-pid="))) + gdb_pid = simple_strtoul(&argv[i][strlen("gdb-pid=")], + NULL, 0); +#endif + else if(!strncmp(argv[i], "umid=", strlen("umid="))){ + create_pid_file(&argv[i][strlen("umid=")]); + } + else if(!strcmp(argv[i], "--version")){ + printf("%s\n", system_utsname.release); + exit(0); + } + else if(!strcmp(argv[i], "--help")){ + Usage(); + } +#ifdef CONFIG_BLK_DEV_INITRD + else if(!strncmp(argv[i], "initrd=", strlen("initrd="))) + initrd = &argv[i][strlen("initrd=")]; +#endif + if(add) add_arg(saved_command_line, argv[i]); + } + if(have_root == 0) add_arg(saved_command_line, DEFAULT_COMMAND_LINE); + + setup_machinename(system_utsname.machine); + + argv1_begin = argv[1]; + argv1_end = &argv[1][strlen(argv[1])]; + + add_process_vmas(); + unblock_shlib_mem(); + physmem = setup_memory(physmem_size + VMALLOC_OFFSET + virtmem_size, + physmem_size, &physmem_fd, &physmem_inode); + high_physmem = physmem + physmem_size; + start_vm = physmem + physmem_size + VMALLOC_OFFSET; + end_vm = start_vm + virtmem_size; + add_perm_vma(physmem, end_vm, 'r', 'w', 'x', '-'); + + start_pfn = PFN_UP(__pa(physmem)); + end_pfn = PFN_DOWN(__pa(high_physmem)); + bootmap_size = init_bootmem(start_pfn, end_pfn - start_pfn); + free_bootmem(__pa(physmem) + bootmap_size, + high_physmem - physmem - bootmap_size); + +#ifdef CONFIG_BLK_DEV_INITRD + if(initrd != NULL) read_initrd(initrd); +#endif + init_task.thread.kernel_stack = (unsigned long) &init_task + + 2 * PAGE_SIZE; +#ifndef CONFIG_SMP + current = &init_task; +#endif + protect(((unsigned long) &init_task) + PAGE_SIZE, PAGE_SIZE, 0, 0, 0); + stack_protections(init_task.thread.kernel_stack, 2 * PAGE_SIZE); + sp = (void *) init_task.thread.kernel_stack + 2 * PAGE_SIZE - + sizeof(unsigned long); + return(signals(start_kernel_proc, sp)); +} + +void setup_arch(char **cmdline_p) +{ + paging_init(); + strcpy(command_line, saved_command_line); + *cmdline_p = command_line; + setup_hostinfo(); +} + +void check_bugs(void) +{ + return; +} + +spinlock_t pid_lock = SPIN_LOCK_UNLOCKED; + +void lock_pid(void) +{ + spin_lock(&pid_lock); +} + +void unlock_pid(void) +{ + spin_unlock(&pid_lock); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/unmap.c linux.ac/arch/um/kernel/unmap.c --- linux.vanilla/arch/um/kernel/unmap.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/unmap.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include "user.h" + +int switcheroo(int fd, int prot, void *from, void *to, int size) +{ + if(munmap(to, size) < 0){ + return(-1); + } + if(mmap(to, size, prot, MAP_SHARED | MAP_FIXED, fd, 0) != to){ + return(-1); + } + if(munmap(from, size) < 0){ + return(-1); + } + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/user_syms.c linux.ac/arch/um/kernel/user_syms.c --- linux.vanilla/arch/um/kernel/user_syms.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/user_syms.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" + +/* Had to steal this from linux/module.h because that file can't be included + * since this includes various user-level headers. + */ + +struct module_symbol +{ + unsigned long value; + const char *name; +}; + +/* Indirect stringification. */ + +#define __MODULE_STRING_1(x) #x +#define __MODULE_STRING(x) __MODULE_STRING_1(x) + +#if !defined(AUTOCONF_INCLUDED) + +#define __EXPORT_SYMBOL(sym,str) error config_must_be_included_before_module +#define EXPORT_SYMBOL(var) error config_must_be_included_before_module +#define EXPORT_SYMBOL_NOVERS(var) error config_must_be_included_before_module + +#elif !defined(CONFIG_MODULES) + +#define __EXPORT_SYMBOL(sym,str) +#define EXPORT_SYMBOL(var) +#define EXPORT_SYMBOL_NOVERS(var) + +#else + +#define __EXPORT_SYMBOL(sym, str) \ +const char __kstrtab_##sym[] \ +__attribute__((section(".kstrtab"))) = str; \ +const struct module_symbol __ksymtab_##sym \ +__attribute__((section("__ksymtab"))) = \ +{ (unsigned long)&sym, __kstrtab_##sym } + +#if defined(MODVERSIONS) || !defined(CONFIG_MODVERSIONS) +#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var)) +#else +#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var))) +#endif + +#define EXPORT_SYMBOL_NOVERS(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var)) + +#endif + +EXPORT_SYMBOL(__errno_location); + +EXPORT_SYMBOL(access); +EXPORT_SYMBOL(open); +EXPORT_SYMBOL(close); +EXPORT_SYMBOL(read); +EXPORT_SYMBOL(write); +EXPORT_SYMBOL(__xstat); +EXPORT_SYMBOL(__lxstat); +EXPORT_SYMBOL(lseek); +EXPORT_SYMBOL(chown); +EXPORT_SYMBOL(truncate); +EXPORT_SYMBOL(utime); +EXPORT_SYMBOL(chmod); +EXPORT_SYMBOL(rename); + +EXPORT_SYMBOL(symlink); +EXPORT_SYMBOL(link); +EXPORT_SYMBOL(unlink); +EXPORT_SYMBOL(readlink); + +EXPORT_SYMBOL(mkdir); +EXPORT_SYMBOL(rmdir); +EXPORT_SYMBOL(opendir); +EXPORT_SYMBOL(readdir); +EXPORT_SYMBOL(closedir); +EXPORT_SYMBOL(seekdir); +EXPORT_SYMBOL(telldir); + +EXPORT_SYMBOL(statfs); + +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(getuid); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/kernel/user_util.c linux.ac/arch/um/kernel/user_util.c --- linux.vanilla/arch/um/kernel/user_util.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/kernel/user_util.c Tue Apr 24 17:52:45 2001 @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "user.h" + +#define COMMAND_LINE_SIZE _POSIX_ARG_MAX + +char saved_command_line[COMMAND_LINE_SIZE] = { 0 }; +char command_line[COMMAND_LINE_SIZE] = { 0 }; + +void add_arg(char *cmd_line, char *arg) +{ + if (strlen(cmd_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) { + printf("add_arg: Too much command line!\n"); + exit(1); + } + if(strlen(cmd_line) > 0) strcat(cmd_line, " "); + strcat(cmd_line, arg); +} + +void *open_maps(void) +{ + void *maps; + + maps = fopen("/proc/self/maps", "r"); + if(!maps){ + perror("open_maps"); + exit(1); + } + return(maps); +} + +int read_map(void *maps, unsigned long *start_out, unsigned long *end_out, + char *r_out, char *w_out, char *x_out, char *p_out) +{ + unsigned int inode; + unsigned long long offset; + unsigned long start, end, major, minor; + int ret; + char r, w, x, p; + + ret = fscanf(maps, "%lx-%lx %c%c%c%c %Lx %lx:%lx %d%*[^\n]", &start, + &end, &r, &w, &x, &p, &offset, &major, &minor, + &inode); + if (ret == EOF) return 0; + else if (ret != 10){ + perror("Scanning a map line"); + exit(1); + } + if(start_out != NULL) *start_out = start; + if(end_out != NULL) *end_out = end; + if(r_out != NULL) *r_out = r; + if(w_out != NULL) *w_out = w; + if(x_out != NULL) *x_out = x; + if(p_out != NULL) *p_out = p; + return(1); +} + +void close_maps(void *maps) +{ + fclose(maps); +} + +int create_vm_file(unsigned long len) +{ + char tempname[32]; + int fd; + char zero; + + strcpy(tempname, "/tmp/vm_file-XXXXXX"); + if ((fd = mkstemp(tempname)) < 0) { + perror("open - cannot create /tmp/vm_file-XXXXXX"); + exit(1); + } + if(unlink(tempname) < 0){ + perror("unlink"); + exit(1); + } + if (fchmod(fd, 0777) < 0){ + perror("fchmod"); + exit(1); + } + if(len > 0){ + if(lseek(fd, len, SEEK_SET) < 0){ + perror("lseek"); + exit(1); + } + zero = 0; + if(write(fd, &zero, 1) != 1){ + perror("write"); + exit(1); + } + } + return(fd); +} + +void remap_data(void *segment_start, void *segment_end) +{ + void *addr; + unsigned long start, end, size; + void *maps; + int data, prot; + char r, w, x; + + maps= open_maps(); + while(read_map(maps, &start, &end, &r, &w, &x, NULL) != 0){ + if(((unsigned long) segment_start >= start) && + ((unsigned long) segment_end <= end)){ + prot = PROT_READ | PROT_WRITE | PROT_EXEC; + size = (unsigned long) segment_end - + (unsigned long) segment_start; + data = create_vm_file(size); + if((addr = mmap(NULL, size, PROT_WRITE | PROT_READ, + MAP_SHARED, data, 0)) < 0){ + perror("mapping new data segment"); + exit(1); + } + memcpy(addr, segment_start, size); + if(switcheroo(data, prot, addr, segment_start, + size) < 0){ + printf("switcheroo failed\n"); + exit(1); + } + close_maps(maps); + return; + } + } + fprintf(stderr, "remap_data couldn't find data segment 0x%lx - 0x%lx " + "in /proc/self/maps\n", (unsigned long) segment_start, + (unsigned long) segment_end); + exit(1); +} + +__u64 file_size(char *file) +{ + struct stat buf; + + if(stat(file, &buf) == -1){ + printk("Couldn't stat \"%s\" : errno = %d\n", file, errno); + return(-1); + } + if(S_ISBLK(buf.st_mode)){ + int size, fd; + + if((fd = open(file, O_RDONLY)) < 0){ + printk("Couldn't open \"%s\", errno = %d\n", file, + errno); + return(-1); + } + if(ioctl(fd, BLKGETSIZE, &size) < 0){ + printk("Couldn't get the block size of \"%s\", " + "errno = %d\n", file, errno); + close(fd); + return(-1); + } + size *= 512; + close(fd); + return(size); + } + return(buf.st_size); +} + +int load_initrd(char *filename, void *buf, int size) +{ + int fd, n; + + if((fd = open(filename, O_RDONLY)) == -1){ + printk("Opening '%s' failed - errno = %d\n", filename, errno); + return(-1); + } + if((n = read(fd, buf, size)) != size){ + printk("Read of %d bytes from '%s' returned %d, errno = %d\n", + size, filename, n, errno); + return(-1); + } + return(0); +} + +void stop(void) +{ + while(1) sleep(1000000); +} + +void stack_protections(unsigned long address, int len) +{ + mprotect((void *) address, len, PROT_READ | PROT_WRITE | PROT_EXEC); +} + +static unsigned long physmem_start = 0x50000000 + NESTING * 0x10000000; + +unsigned long setup_memory(unsigned long total_size, + unsigned long physmem_size, int *fd_out, + int *inode_out) +{ + void *mem; + struct stat buf; + + mem = mmap((void *) physmem_start, total_size, PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if(mem < 0){ + perror("Mapping all of memory"); + exit(1); + } + munmap(mem, total_size); + *fd_out = create_vm_file(physmem_size); + if(mmap(mem, physmem_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, *fd_out, 0) != mem){ + perror("Mapping physical memory"); + exit(1); + } + if(fstat(*fd_out, &buf) < 0){ + perror("Stat-ing phymem_fd"); + exit(1); + } + *inode_out = buf.st_ino; + return((unsigned long) mem); +} + +void map(unsigned long virt, void *p, unsigned long len, int r, + int w, int x) +{ + unsigned long phys = (unsigned long) p; + int prot; + + prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | + (x ? PROT_EXEC : 0); + if((phys < physmem) || (phys > high_physmem)) + panic("map : physmem out of range"); + phys -= physmem; + if(mmap((void *) virt, len, prot, MAP_SHARED | MAP_FIXED, physmem_fd, + phys) != (void *) virt){ + panic("Error mapping a page - errno = %d", errno); + } +} + +void protect(unsigned long addr, unsigned long len, int r, int w, int x) +{ + int prot; + + prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | + (x ? PROT_EXEC : 0); + if(mprotect((void *) addr, len, prot) == -1){ + perror("Protecting a page"); + panic("protect failed"); + } +} + +int wait_for_stop(int pid, int sig, int cont_type) +{ + int status, ret; + + while(1){ + if(((ret = waitpid(pid, &status, WUNTRACED)) < 0) || + !WIFSTOPPED(status) || (WSTOPSIG(status) != sig)){ + if(ret < 0){ + if(errno == EINTR) continue; + printk("wait failed, errno = %d\n", errno); + } + else if(WIFEXITED(status)) + printk("process exited with status %d\n", + WEXITSTATUS(status)); + else if(WIFSIGNALED(status)) + printk("process exited with signal %d\n", + WTERMSIG(status)); + else if((WSTOPSIG(status) == SIGVTALRM) || + (WSTOPSIG(status) == SIGALRM) || + (WSTOPSIG(status) == SIGIO) || + (WSTOPSIG(status) == SIGCHLD)){ + ptrace(cont_type, pid, 0, WSTOPSIG(status)); + continue; + } + else printk("process stopped with signal %d\n", + WSTOPSIG(status)); + panic("wait_for_stop failed to wait for %d to stop " + "with %d\n", pid, sig); + } + return(status); + } +} + +static int mem_blocked = 0; + +void block_shlib_mem(void) +{ + if(mem_blocked || (NESTING > 0)) return; + mem_blocked = 1; + if(mmap((void *) 0x40000000, 0x20000000, PROT_NONE, + MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, -1, + 0) != (void *) 0x40000000){ + printf("mmap failed in linux_main\n"); + exit(1); + } +} + +void unblock_shlib_mem(void) +{ + if(NESTING == 0){ + mem_blocked = 0; + munmap((void *) 0x40000000, 0x20000000); + } +} + +int clone_and_wait(int (*fn)(void *), void *arg, void *sp, int flags) +{ + int pid; + + pid = clone(fn, sp, flags, arg); + if(pid < 0) return(-1); + wait_for_stop(pid, SIGSTOP, PTRACE_CONT); + return(pid); +} + +struct grantpt_info { + int fd; + int res; + int err; +}; + +static void grantpt_cb(void *arg) +{ + struct grantpt_info *info = arg; + + info->res = grantpt(info->fd); + info->err = errno; +} + +int get_pty(void) +{ + struct grantpt_info info; + int fd; + + if((fd = open("/dev/ptmx", O_RDWR)) < 0){ + printk("get_pty : Couldn't open /dev/ptmx - errno = %d\n", + errno); + return(-1); + } + info.fd = fd; + tracing_cb(grantpt_cb, &info); + if(info.res < 0){ + printk("get_ptr : Couldn't grant pty - errno = %d\n", + info.err); + return(-1); + } + if(unlockpt(fd) < 0){ + printk("get_ptr : Couldn't unlock pty - errno = %d\n", errno); + return(-1); + } + return(fd); +} + +int raw(int fd, int complain) +{ + struct termios tt; + int err; + + tcgetattr(fd, &tt); + cfmakeraw(&tt); + err = tcsetattr(fd, TCSADRAIN, &tt); + if((err < 0) && complain){ + printk("tcsetattr failed, errno = %d\n", errno); + return(-errno); + } + return(0); +} + +void setup_machinename(char *machine_out) +{ + struct utsname host; + + uname(&host); + strcpy(machine_out, host.machine); +} + +char host_info[(_UTSNAME_LENGTH + 1) * 4 + _UTSNAME_NODENAME_LENGTH + 1]; + +void setup_hostinfo(void) +{ + struct utsname host; + + uname(&host); + sprintf(host_info, "%s %s %s %s %s", host.sysname, host.nodename, + host.release, host.version, host.machine); +} + +void close_fd(int fd) +{ + close(fd); +} + +#define UMID_LEN 64 +char umid[UMID_LEN] = { 0 }; + +void create_pid_file(char *name) +{ + char tmp[sizeof("/tmp/uml/") + UMID_LEN + 1]; + int fd; + static int umid_inited = 0; + + if(umid_inited){ + printk("Unique machine name can't be set twice\n"); + return; + } + if((mkdir("/tmp/uml", 0777) < 0) && + (errno != EEXIST)){ + printk("Failed to create /tmp/uml - errno = %s\n", errno); + return; + } + umid_inited = 1; + if(strlen(name) > UMID_LEN - 1) + printk("Unique machine name is being truncated to %s " + "characters\n", UMID_LEN); + strncpy(umid, name, UMID_LEN - 1); + umid[UMID_LEN - 1] = '\0'; + sprintf(tmp, "/tmp/uml/%s", umid); + if((fd = open(tmp, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0){ + printk("Open of machine pid file \"%s\" failed - " + "errno = %d\n", umid, errno); + return; + } + sprintf(tmp, "%d\n", getpid()); + if(write(fd, tmp, strlen(tmp)) != strlen(tmp)) + printk("Write of pid file failed - errno = %d\n", errno); + close(fd); +} + +void remove_pid_file(void) +{ + char tmp[sizeof("/tmp/uml/") + UMID_LEN + 1]; + + if(umid[0] == '\0') return; + sprintf(tmp, "/tmp/uml/%s", umid); + unlink(tmp); + return; +} + +char *get_umid(void) +{ + return(umid); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/link.ld.in linux.ac/arch/um/link.ld.in --- linux.vanilla/arch/um/link.ld.in Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/link.ld.in Sun May 13 19:41:45 2001 @@ -0,0 +1,166 @@ +OUTPUT_FORMAT("elf32-ELF_SUBARCH") +OUTPUT_ARCH(ELF_SUBARCH) +ENTRY(_start) + +SECTIONS +{ + . = START() + SIZEOF_HEADERS; + /* Read-only sections, merged into text segment: */ + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.text : + { *(.rel.text) *(.rel.gnu.linkonce.t*) } + .rela.text : + { *(.rela.text) *(.rela.gnu.linkonce.t*) } + .rel.data : + { *(.rel.data) *(.rel.gnu.linkonce.d*) } + .rela.data : + { *(.rela.data) *(.rela.gnu.linkonce.d*) } + .rel.rodata : + { *(.rel.rodata) *(.rel.gnu.linkonce.r*) } + .rela.rodata : + { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .plt : { *(.plt) } + .remap : { arch/um/kernel/unmap_fin.o (.text) } + + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + . = ALIGN(4096); + __init_end = .; + __exitcall_begin = .; + .exitcall : { *(.exitcall.exit) } + __exitcall_end = .; + + _stext = .; + .text : + { + *(.text) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + } =0x9090 + .kstrtab : { *(.kstrtab) } + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + .fini : { *(.fini) } =0x9090 + .rodata : { *(.rodata) *(.gnu.linkonce.r*) } + .rodata1 : { *(.rodata1) } + _etext = .; + PROVIDE (etext = .); + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(0x1000) + (. & (0x1000 - 1)); + .data : + { + _sdata = .; + PROVIDE (sdata = .); + . = ALIGN(16384); /* init_task */ + *(.data.init_task) + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } + .data1 : { *(.data1) } + .ctors : + { + *(.ctors) + } + .dtors : + { + *(.dtors) + } + + .got : { *(.got.plt) *(.got) } + .dynamic : { *(.dynamic) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + . = ALIGN(0x1000); + .sbss : + { + __bss_start = .; + PROVIDE(_bss_start = .); + *(.sbss) + *(.scommon) + } + .bss : + { + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* These must appear regardless of . */ +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/main.c linux.ac/arch/um/main.c --- linux.vanilla/arch/um/main.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/main.c Sun May 13 19:44:45 2001 @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include "include/user_util.h" + +unsigned long stacksizelim; + +char *linux_prog; + +#define PGD_BOUND (4 * 1024 * 1024) +#define STACKSIZE (8 * 1024 * 1024) +#define THREAD_NAME_LEN (256) + +char padding[THREAD_NAME_LEN] = { [ 0 ... THREAD_NAME_LEN - 2] = ' ', '\0' }; + +int main(int argc, char **argv, char **envp) +{ + struct termios tt; + struct rlimit lim; + int ret, i; + char **new_argv; + + /* Allocate memory for thread command lines */ + if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){ + new_argv = malloc((argc + 2) * sizeof(char*)); + if(!new_argv) { + perror("Allocating extended argv"); + exit(1); + } + + new_argv[0] = argv[0]; + new_argv[1] = padding; + + for(i = 2; i <= argc; i++) + new_argv[i] = argv[i - 1]; + new_argv[argc + 1] = NULL; + + execvp(new_argv[0], new_argv); + perror("execing with extended args"); + exit(1); + } + + linux_prog = argv[0]; + block_shlib_mem(); + if(getrlimit(RLIMIT_STACK, &lim) < 0){ + perror("getrlimit"); + exit(1); + } + if((lim.rlim_cur == RLIM_INFINITY) || (lim.rlim_cur > STACKSIZE)){ + lim.rlim_cur = STACKSIZE; + if(setrlimit(RLIMIT_STACK, &lim) < 0){ + perror("setrlimit"); + exit(1); + } + } + stacksizelim = (lim.rlim_cur + PGD_BOUND - 1) & ~(PGD_BOUND - 1); + get_profile_timer(); + if(isatty(0)) tcgetattr(0, &tt); + else if(isatty(1)) tcgetattr(1, &tt); + else tcgetattr(2, &tt); + + if((new_argv = malloc((argc + 1) * sizeof(char *))) == NULL){ + perror("Mallocing argv"); + exit(1); + } + for(i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ptproxy.h" +#include "sysdep.h" +#include "wait.h" + +#include "user_util.h" +#include "chan.h" +#include "user.h" + +static void debugger_normal_return (debugger_state *, pid_t); + +/* + * Handle debugger trap, i.e. syscall. + */ + +void +debugger_syscall (debugger_state *debugger, pid_t child) +{ + int cancel = 0; + long arg1, arg2, arg3, arg4, arg5; + int syscall; + + syscall = syscall_get_number (debugger->pid); + syscall_get_args (debugger->pid, &arg1, &arg2, &arg3, &arg4, &arg5); + + switch (syscall) + { + case __NR_execve: + debugger->handle_trace = debugger_syscall; /* execve never returns */ + break; + case __NR_ptrace: + if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid; + proxy_ptrace (debugger, arg1, arg2, arg3, arg4, child); + cancel = 1; + debugger->handle_trace = debugger_cancelled_return; + break; + case __NR_waitpid: + case __NR_wait4: + proxy_wait (debugger, + syscall == __NR_waitpid ? WAIT_WAITPID : + syscall == __NR_wait4 ? WAIT_WAIT4 : -1, + debugger->debugee->pid, (int *)arg2, arg3, + (void *)arg4); + cancel = 1; + debugger->handle_trace = proxy_wait_return; + break; + case __NR_kill: + if(arg1 == debugger->debugee->pid){ + debugger->result = kill(child, arg2); + cancel = 1; + debugger->handle_trace = debugger_cancelled_return; + } + else { + debugger->handle_trace = debugger_normal_return; + } + break; + default: + debugger->handle_trace = debugger_normal_return; + } + + if (cancel) + syscall_cancel (debugger->pid); + + syscall_continue (debugger->pid); +} + +void +debugger_normal_return (debugger_state *debugger, pid_t unused) +{ + debugger->handle_trace = debugger_syscall; + syscall_continue (debugger->pid); +} + +void +debugger_cancelled_return (debugger_state *debugger, pid_t unused) +{ + syscall_set_result (debugger->pid, debugger->result); + debugger->handle_trace = debugger_syscall; + syscall_continue (debugger->pid); +} + +#ifdef __SMP__ +#error need to make these arrays +#endif + +static debugger_state debugger; +static debugee_state debugee; + +void +init_proxy (pid_t debugger_pid, int stopped, int status) +{ + debugger.pid = debugger_pid; + debugger.handle_trace = debugger_syscall; + debugger.debugee = &debugee; + debugger.stopped = 0; + + debugee.pid = 0; + debugee.traced = 0; + debugee.stopped = stopped; + debugee.event = 0; + debugee.zombie = 0; + debugee.died = 0; + debugee.debugger = &debugger; + debugee.wait_status = status; +} + +void debugger_proxy(int status, int pid) +{ + if (WIFSTOPPED (status)) + { + /* debugger got a signal */ + + if (WSTOPSIG (status) == SIGTRAP) + { + debugger.handle_trace (&debugger, pid); + } + else + { + /* go on as usual */ + ptrace (PTRACE_SYSCALL, debugger.pid, + 0, WSTOPSIG (status)); + } + } + else if (WIFEXITED (status)) + { + /* debugger exited with status + WEXITSTATUS (status) */ + } + else if (WIFSIGNALED (status)) + { + /* debugger died from signal + WTERMSIG (status) */ + } + else + { + /* unknown event */ + panic("proxy got unknown status (0x%x) on debugger " + "(pid %d)", status, debugger.pid); + exit (1); + } +} + +void child_proxy(pid_t pid, int status) +{ + debugee.stopped = 1; + debugee.event = 1; + debugee.wait_status = status; + + if (WIFSTOPPED (status)) + { + /* child got a signal */ + } + else if (WIFEXITED (status)) + { + /* child exited with status WEXITSTATUS + (status) */ + debugee.zombie = 1; + } + else if (WIFSIGNALED (status)) + { + /* child died from signal WTERMSIG + (status) */ + debugee.zombie = 1; + } + else + { + /* unknown event */ + panic("proxy got unknown status (0x%x) on child " + "(pid %d)", status, pid); + } + + if (debugee.stopped) + { + int r; + + r = kill (debugger.pid, SIGCHLD); + if (debugger.stopped) + proxy_wait_return (&debugger, -1); + } +} + +extern struct io_chan gdb_chan; + +int start_debugger(char *prog, int startup, int stop, int *fd_out) +{ + int err, slave, child; + + err = open_chan_pair(&gdb_chan, NULL, NULL); + if(err) return(err); + slave = CHAN_OUT_FD(gdb_chan); + if((child = fork()) == 0){ + char tempname[sizeof("/tmp/gdb_init-XXXXXX")]; + char arg[sizeof("--command=/tmp/gdb_init-XXXXXX")]; + int fd; + + if(setsid() < 0) perror("setsid"); + if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) || + (dup2(slave, 2) < 0)){ + printk("start_debugger : dup2 failed, errno = %d\n", + errno); + exit(1); + } + if(ioctl(0, TIOCSCTTY, 0) < 0){ + printk("start_debugger : TIOCSCTTY failed, " + "errno = %d\n", errno); + exit(1); + } + if(tcsetpgrp (1, getpid()) < 0){ + printk("start_debugger : tcsetpgrp failed, " + "errno = %d\n", errno); +#ifdef notdef + exit(1); +#endif + } + strcpy(tempname, "/tmp/gdb_init-XXXXXX"); + if((fd = mkstemp(tempname)) < 0){ + printk("start_debugger : mkstmp failed, errno = %d\n", + errno); + exit(1); + } + sprintf(arg, "--command=%s", tempname); + write(fd, "att 1\n", strlen("att 1\n")); + write(fd, "b panic\n", strlen("b panic\n")); + write(fd, "b stop\n", strlen("b stop\n")); + write(fd, "handle SIGWINCH nostop noprint\n", + strlen("handle SIGWINCH nostop noprint\n")); + if(startup){ + if(stop){ + write(fd, "b start_kernel\n", + strlen("b start_kernel\n")); + } + write(fd, "c\n", strlen("c\n")); + } + if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ + printk("start_debugger : PTRACE_TRACEME failed, " + "errno = %d\n", errno); + exit(1); + } + execlp("gdb", "gdb", arg, prog, NULL); + printk("start_debugger : exec of gdb failed, errno = %d\n", + errno); + } + if(child < 0){ + printk("start_debugger : fork for gdb failed, errno = %d\n", + errno); + return(-1); + } + *fd_out = slave; + return(child); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/ptproxy.h linux.ac/arch/um/ptproxy/ptproxy.h --- linux.vanilla/arch/um/ptproxy/ptproxy.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/ptproxy.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,42 @@ +/********************************************************************** +ptproxy.h + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +#include + +typedef struct debugger debugger_state; +typedef struct debugee debugee_state; + +struct debugger +{ + pid_t pid; + long result; + int wait_options; + int *wait_status_ptr; + unsigned stopped : 1; + void (*handle_trace) (debugger_state *, pid_t); + + debugee_state *debugee; +}; + +struct debugee +{ + pid_t pid; + int wait_status; + unsigned died : 1; + unsigned event : 1; + unsigned stopped : 1; + unsigned trace_singlestep : 1; + unsigned trace_syscall : 1; + unsigned traced : 1; + unsigned zombie : 1; + + debugger_state *debugger; +}; + +extern void debugger_syscall (debugger_state *, pid_t); +extern void debugger_cancelled_return (debugger_state *, pid_t); +extern void proxy_ptrace (struct debugger *, int, pid_t, long, long, pid_t); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/ptrace.c linux.ac/arch/um/ptproxy/ptrace.c --- linux.vanilla/arch/um/ptproxy/ptrace.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/ptrace.c Sun May 13 19:44:45 2001 @@ -0,0 +1,210 @@ +/********************************************************************** +ptrace.c + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. + +Jeff Dike (jdike@karaya.com) : Modified for integration into uml +**********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "ptproxy.h" +#include "user_util.h" +#include "debug.h" + +struct { + int op; + pid_t pid; + long addr; + long data; + pid_t child; + struct timeval tv; +} ptrace_record[1024]; + +int ptrace_index = 0; + +void +proxy_ptrace (struct debugger *debugger, int arg1, pid_t arg2, + long arg3, long arg4, pid_t child) +{ + int status; + + if(ptrace_index == 1024) ptrace_index = 0; + ptrace_record[ptrace_index].op = arg1; + ptrace_record[ptrace_index].pid = arg2; + ptrace_record[ptrace_index].data = arg3; + ptrace_record[ptrace_index].addr = arg4; + ptrace_record[ptrace_index].child = child; + gettimeofday(&ptrace_record[ptrace_index++].tv, NULL); + if (debugger->debugee->died) + { + debugger->result = -ESRCH; + debugger->handle_trace = debugger_cancelled_return; + } + + switch (arg1) + { + case PTRACE_ATTACH: + if (debugger->debugee->traced) + debugger->result = -EPERM; + else + { + debugger->result = 0; + debugger->debugee->pid = arg2; + debugger->debugee->traced = 1; + if(!debugger->debugee->stopped) kill(child, SIGSTOP); + else { + child_proxy(child, + debugger->debugee->wait_status); + } + } + break; + + case PTRACE_CONT: + debugger->result = ptrace (PTRACE_CONT, child, arg3, arg4); + break; + + case PTRACE_DETACH: + if (debugger->debugee->traced) + { + debugger->result = 0; + debugger->debugee->traced = 0; + kill(child, SIGCONT); + } + else + debugger->result = -EPERM; + break; + + case PTRACE_GETFPREGS: + { + long regs[27]; + int i; + + debugger->result = ptrace (PTRACE_GETFPREGS, child, 0, regs); + if (debugger->result == -1) + debugger->result = -errno; + else + { + for (i = 0; i < 27; i++) + ptrace (PTRACE_POKEDATA, debugger->pid, + arg4 + 4 * i, regs[i]); + } + } + break; + + case PTRACE_GETREGS: + { + long regs[17]; + int i; + + debugger->result = ptrace (PTRACE_GETREGS, child, 0, regs); + if (debugger->result == -1) + debugger->result = -errno; + else + { + for (i = 0; i < 17; i++) + ptrace (PTRACE_POKEDATA, debugger->pid, + arg4 + 4 * i, regs[i]); + } + } + break; + + case PTRACE_KILL: + debugger->result = ptrace (PTRACE_KILL, child, arg3, arg4); + if (debugger->result == -1) + debugger->result = -errno; + break; + + case PTRACE_PEEKDATA: + case PTRACE_PEEKTEXT: + case PTRACE_PEEKUSER: + errno = 0; + debugger->result = ptrace (arg1, child, arg3, 0); + if (debugger->result == -1 && errno != 0) + debugger->result = -errno; + else + { + ptrace (PTRACE_POKEDATA, debugger->pid, arg4, + debugger->result); + debugger->result = 0; + } + break; + case PTRACE_POKEDATA: + case PTRACE_POKETEXT: + case PTRACE_POKEUSER: + debugger->result = ptrace (arg1, child, arg3, arg4); + if (debugger->result == -1) + debugger->result = -errno; + break; + + case PTRACE_SETFPREGS: + { + long regs[27]; + int i; + + for (i = 0; i < 27; i++) + regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid, + arg4 + 4 * i, 0); + debugger->result = ptrace (PTRACE_SETFPREGS, child, 0, regs); + if (debugger->result == -1) + debugger->result = -errno; + } + break; + + case PTRACE_SETREGS: + { + long regs[17]; + int i; + + for (i = 0; i < 17; i++) + regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid, + arg4 + 4 * i, 0); + debugger->result = ptrace (PTRACE_SETREGS, child, 0, regs); + if (debugger->result == -1) + debugger->result = -errno; + } + break; + + case PTRACE_SINGLESTEP: + debugger->result = ptrace (PTRACE_SINGLESTEP, child, arg3, + arg4); + if (debugger->result == -1) + debugger->result = -errno; + else { + status = wait_for_stop(child, SIGTRAP, + PTRACE_SINGLESTEP); + child_proxy(child, status); + } + break; + + case PTRACE_SYSCALL: + debugger->result = ptrace (PTRACE_SYSCALL, child, arg3, arg4); + if (debugger->result == -1) + debugger->result = -errno; + break; + + case PTRACE_TRACEME: + debugger->result = -EINVAL; + break; + + default: + debugger->result = -EINVAL; + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/sysdep.c linux.ac/arch/um/ptproxy/sysdep.c --- linux.vanilla/arch/um/ptproxy/sysdep.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/sysdep.c Sun May 13 19:41:45 2001 @@ -0,0 +1,69 @@ +/********************************************************************** +sysdep.c + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include + +int +syscall_get_number (pid_t pid) +{ + return ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_NR_OFFSET, 0); +} + +void +syscall_cancel (pid_t pid) +{ + int err; + + err = ptrace (PTRACE_POKEUSER, pid, UM_SYSCALL_NR_OFFSET, __NR_getpid); + if (err == -1) + { + fprintf (stderr, "ptproxy: couldn't cancel syscall: " + "errno = %d\n", errno); + exit (1); + } +} + +void +syscall_get_args (pid_t pid, long *arg1, long *arg2, + long *arg3, long *arg4, long *arg5) +{ + *arg1 = ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG1_OFFSET, 0); + *arg2 = ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG2_OFFSET, 0); + *arg3 = ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG3_OFFSET, 0); + *arg4 = ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG4_OFFSET, 0); + *arg5 = ptrace (PTRACE_PEEKUSER, pid, UM_SYSCALL_ARG5_OFFSET, 0); +} + +void +syscall_set_result (pid_t pid, long result) +{ + ptrace (PTRACE_POKEUSER, pid, UM_SYSCALL_RET_OFFSET, result); +} + +void +syscall_continue (pid_t pid) +{ + ptrace (PTRACE_SYSCALL, pid, 0, 0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/sysdep.h linux.ac/arch/um/ptproxy/sysdep.h --- linux.vanilla/arch/um/ptproxy/sysdep.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/sysdep.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,13 @@ +/********************************************************************** +sysdep.h + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +extern int syscall_get_number (pid_t pid); +extern void syscall_cancel (pid_t pid); +extern void syscall_get_args (pid_t pid, long *arg1, long *arg2, + long *arg3, long *arg4, long *arg5); +extern void syscall_set_result (pid_t pid, long result); +extern void syscall_continue (pid_t pid); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/wait.c linux.ac/arch/um/ptproxy/wait.c --- linux.vanilla/arch/um/ptproxy/wait.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/wait.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,79 @@ +/********************************************************************** +wait.c + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. + +**********************************************************************/ + +#include +#include +#include + +#include "ptproxy.h" +#include "sysdep.h" +#include "wait.h" + +void +proxy_wait (struct debugger *debugger, enum wait_type type, pid_t pid, + int *status, int options, void *rusage) +{ + debugger->wait_status_ptr = status; + debugger->wait_options = options; +} + +void +proxy_wait_return (struct debugger *debugger, pid_t unused) +{ + debugger->stopped = 0; + + if (debugger->debugee->died) + { + debugger->result = -ECHILD; + debugger_cancelled_return (debugger, -1); + return; + } + + if (debugger->wait_options & __WCLONE) + { + debugger->result = -ECHILD; + debugger_cancelled_return (debugger, -1); + return; + } + + if (debugger->debugee->zombie && debugger->debugee->event) + debugger->debugee->died = 1; + + if (debugger->wait_options & WNOHANG) + { + if (debugger->debugee->event) + { + debugger->debugee->event = 0; + ptrace (PTRACE_POKEDATA, debugger->pid, + debugger->wait_status_ptr, debugger->debugee->wait_status); + /* if (wait4) + ptrace (PTRACE_POKEDATA, pid, rusage_ptr, ...); */ + debugger->result = debugger->debugee->pid; + } + else + { + debugger->result = 0; + } + debugger_cancelled_return (debugger, -1); + return; + } + + if (debugger->debugee->event) + { + debugger->debugee->event = 0; + ptrace (PTRACE_POKEDATA, debugger->pid, + debugger->wait_status_ptr, debugger->debugee->wait_status); + /* if (wait4) + ptrace (PTRACE_POKEDATA, pid, rusage_ptr, ...); */ + debugger->result = debugger->debugee->pid; + debugger_cancelled_return (debugger, -1); + return; + } + + debugger->stopped = 1; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/ptproxy/wait.h linux.ac/arch/um/ptproxy/wait.h --- linux.vanilla/arch/um/ptproxy/wait.h Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/ptproxy/wait.h Mon Apr 9 23:25:02 2001 @@ -0,0 +1,18 @@ +/********************************************************************** +wait.h + +Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +enum wait_type +{ + WAIT_WAIT, + WAIT_WAITPID, + WAIT_WAIT3, + WAIT_WAIT4 +}; + +extern void proxy_wait (struct debugger *debugger, enum wait_type type, + pid_t arg1, int *arg2, int arg3, void *arg4); +extern void proxy_wait_return (struct debugger *debugger, pid_t unused); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/Makefile linux.ac/arch/um/sys-i386/Makefile --- linux.vanilla/arch/um/sys-i386/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/Makefile Thu May 17 21:06:02 2001 @@ -0,0 +1,54 @@ +OBJ = sys.o + +OBJS = checksum.o ldt.o old-checksum.o ptrace.o ptrace_user.o semaphore.o \ + sigcontext.o +OX_OBJS = ksyms.o + +EXTRA_CFLAGS = -I../include + +USER_CFLAGS := $(patsubst -I%,,$(CFLAGS)) +USER_CFLAGS := $(patsubst -D__KERNEL__,,$(USER_CFLAGS)) $(EXTRA_CFLAGS) + +SYMLINKS = semaphore.c old-checksum.c checksum.S + +all: $(OBJ) + +$(OBJ): $(OBJS) $(OX_OBJS) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ + +sigcontext.o: sigcontext.c + $(CC) $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +ldt.o: ldt.c + $(CC) $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +ptrace_user.o: ptrace_user.c + $(CC) -D__KERNEL__ $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +checksum.S old-checksum.c: + -rm -f $@ + -ln -s $(TOPDIR)/arch/i386/lib/$@ $@ + +semaphore.c: + -rm -f $@ + -ln -s $(TOPDIR)/arch/i386/kernel/$@ $@ + +clean: + rm -f $(OBJS) $(OX_OBJS) + +fastdep: + +archmrproper: + rm -f $(SYMLINKS) + +archclean: + rm -f link.ld + @$(MAKEBOOT) clean + +archdep: + @$(MAKEBOOT) dep + +modules: + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/ksyms.c linux.ac/arch/um/sys-i386/ksyms.c --- linux.vanilla/arch/um/sys-i386/ksyms.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/ksyms.c Tue Apr 24 17:54:11 2001 @@ -0,0 +1,16 @@ +#include "linux/module.h" +#include "linux/in6.h" +#include "linux/rwsem.h" +#include "asm/byteorder.h" +#include "asm/semaphore.h" +#include "asm/uaccess.h" +#include "asm/checksum.h" +#include "asm/errno.h" + +EXPORT_SYMBOL(__down_failed); +EXPORT_SYMBOL(__down_failed_interruptible); +EXPORT_SYMBOL(__down_failed_trylock); +EXPORT_SYMBOL(__up_wakeup); + +/* Networking helper routines. */ +EXPORT_SYMBOL(csum_partial_copy_generic); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/ldt.c linux.ac/arch/um/sys-i386/ldt.c --- linux.vanilla/arch/um/sys-i386/ldt.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/ldt.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +int sys_modify_ldt(int func, void *ptr, unsigned long bytecount) +{ + return modify_ldt(func, ptr, bytecount); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/ptrace.c linux.ac/arch/um/sys-i386/ptrace.c --- linux.vanilla/arch/um/sys-i386/ptrace.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/ptrace.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "asm/ptrace.h" + +/* determines which flags the user has access to. */ +/* 1 = access 0 = no access */ +#define FLAG_MASK 0x00044dd5 + +int putreg(struct task_struct *child, unsigned long regno, + unsigned long value) +{ + switch (regno >> 2) { + case FS: + if (value && (value & 3) != 3) + return -EIO; + child->thread.process_regs.regs[FS] = value; + return 0; + case GS: + if (value && (value & 3) != 3) + return -EIO; + child->thread.process_regs.regs[GS] = value; + return 0; + case DS: + case ES: + if (value && (value & 3) != 3) + return -EIO; + value &= 0xffff; + break; + case SS: + case CS: + if ((value & 3) != 3) + return -EIO; + value &= 0xffff; + break; + case EFL: + value &= FLAG_MASK; + value |= child->thread.process_regs.regs[EFL]; + break; + } + child->thread.process_regs.regs[regno >> 2] = value; + return 0; +} + +unsigned long getreg(struct task_struct *child, unsigned long regno) +{ + unsigned long retval = ~0UL; + + switch (regno >> 2) { + case FS: + case GS: + case DS: + case ES: + case SS: + case CS: + retval = 0xffff; + /* fall through */ + default: + retval &= child->thread.process_regs.regs[regno >> 2]; + } + return retval; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/ptrace_user.c linux.ac/arch/um/sys-i386/ptrace_user.c --- linux.vanilla/arch/um/sys-i386/ptrace_user.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/ptrace_user.c Sun May 13 19:41:45 2001 @@ -0,0 +1,25 @@ +#include +#include +#include +#include "sysdep/ptrace.h" + +int ptrace_getregs(long pid, struct sys_pt_regs *regs_out) +{ + return(ptrace(PTRACE_GETREGS, pid, 0, regs_out)); +} + +int ptrace_setregs(long pid, struct sys_pt_regs *regs) +{ + return(ptrace(PTRACE_SETREGS, pid, 0, regs)); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-i386/sigcontext.c linux.ac/arch/um/sys-i386/sigcontext.c --- linux.vanilla/arch/um/sys-i386/sigcontext.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-i386/sigcontext.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include "sysdep/ptrace.h" +#include "user_util.h" + +void fill_in_sigcontext(void *sc_ptr, struct sys_pt_regs *regs, + unsigned long cr2, int err) +{ + struct sigcontext *sc; + + sc = sc_ptr; + sc->ebx = regs->regs[EBX]; + sc->ecx = regs->regs[ECX]; + sc->edx = regs->regs[EDX]; + sc->esi = regs->regs[ESI]; + sc->edi = regs->regs[EDI]; + sc->ebp = regs->regs[EBP]; + sc->eax = regs->regs[EAX]; + sc->ds = regs->regs[DS]; + sc->es = regs->regs[ES]; + sc->fs = regs->regs[FS]; + sc->gs = regs->regs[GS]; + sc->eip = regs->regs[EIP]; + sc->cs = regs->regs[CS]; + sc->eflags = regs->regs[EFL]; + sc->esp_at_signal = regs->regs[UESP]; + sc->ss = regs->regs[SS]; + sc->err = err; + sc->cr2 = cr2; +} + +void fill_in_regs(struct sys_pt_regs *regs, void *sc_ptr) +{ + struct sigcontext *sc; + + sc = sc_ptr; + regs->regs[EBX] = sc->ebx; + regs->regs[ECX] = sc->ecx; + regs->regs[EDX] = sc->edx; + regs->regs[ESI] = sc->esi; + regs->regs[EDI] = sc->edi; + regs->regs[EBP] = sc->ebp; + regs->regs[EAX] = sc->eax; + regs->regs[DS] = sc->ds; + regs->regs[ES] = sc->es; + regs->regs[FS] = sc->fs; + regs->regs[GS] = sc->gs; + regs->regs[EIP] = sc->eip; + regs->regs[CS] = sc->cs; + regs->regs[EFL] = sc->eflags; + regs->regs[UESP] = sc->esp_at_signal; + regs->regs[SS] = sc->ss; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ia64/Makefile linux.ac/arch/um/sys-ia64/Makefile --- linux.vanilla/arch/um/sys-ia64/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ia64/Makefile Mon Apr 9 23:25:02 2001 @@ -0,0 +1,26 @@ +OBJ = sys.o + +OBJS = + +all: $(OBJ) + +$(OBJ): $(OBJS) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ +clean: + rm -f $(OBJS) + +fastdep: + +archmrproper: + +archclean: + rm -f link.ld + @$(MAKEBOOT) clean + +archdep: + @$(MAKEBOOT) dep + +modules: + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ppc/Makefile linux.ac/arch/um/sys-ppc/Makefile --- linux.vanilla/arch/um/sys-ppc/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ppc/Makefile Mon Apr 9 23:25:02 2001 @@ -0,0 +1,27 @@ +OBJ = sys.o + +OBJS = ptrace.o sigcontext.o checksum.o miscthings.o bitops.o + +USER_CFLAGS = $(patsubst -I%,,$(CFLAGS)) +USER_CFLAGS += -I../include -I$(TOPDIR)/include + +all: $(OBJ) + +$(OBJ): $(OBJS) + rm -f $@ + $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ + +ptrace.o: ptrace.c + $(CC) -D__KERNEL__ $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +sigcontext.o: sigcontext.c + $(CC) -D__KERNEL__ $(USER_CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + +clean: + rm -f $(OBJS) + +fastdep: + +modules: + +include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ppc/miscthings.c linux.ac/arch/um/sys-ppc/miscthings.c --- linux.vanilla/arch/um/sys-ppc/miscthings.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ppc/miscthings.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,16 @@ +#include +#include + +unsigned int local_bh_count[NR_CPUS]; +unsigned long isa_io_base = 0; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ppc/ptrace.c linux.ac/arch/um/sys-ppc/ptrace.c --- linux.vanilla/arch/um/sys-ppc/ptrace.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ppc/ptrace.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,28 @@ +#include "linux/sched.h" +#include "asm/ptrace.h" + +int putreg(struct task_struct *child, unsigned long regno, + unsigned long value) +{ + child->thread.process_regs.u.regs[regno >> 2] = value; + return 0; +} + +unsigned long getreg(struct task_struct *child, unsigned long regno) +{ + unsigned long retval = ~0UL; + + retval &= child->thread.process_regs.u.regs[regno >> 2]; + return retval; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/arch/um/sys-ppc/sigcontext.c linux.ac/arch/um/sys-ppc/sigcontext.c --- linux.vanilla/arch/um/sys-ppc/sigcontext.c Thu Jan 1 01:00:00 1970 +++ linux.ac/arch/um/sys-ppc/sigcontext.c Mon Apr 9 23:25:02 2001 @@ -0,0 +1,48 @@ +#include +#include +#include "sysdep/ptrace.h" +#include "user_util.h" + +void fill_in_sigcontext(struct sigcontext *sc, struct sys_pt_regs *regs, + unsigned long cr2, int err) +{ +#if 0 + int i; + // general purpose regs + for (i=0; i<32; ++i) { + sc->regs->gpr[i] = regs->u.regs[PT_R0 + i]; + } + sc->regs->nip = regs->u.regs[PT_NIP]; + sc->regs->msr = regs->u.regs[PT_MSR]; + sc->regs->orig_gpr3 = regs->u.regs[PT_ORIG_R3]; + sc->regs->ctr = regs->u.regs[PT_CTR]; + sc->regs->link = regs->u.regs[PT_LNK]; + sc->regs->xer = regs->u.regs[PT_XER]; + sc->regs->ccr = regs->u.regs[PT_CCR]; + sc->regs->mq = regs->u.regs[PT_MQ]; + sc->regs->trap = err; + sc->regs->dar = cr2; +#endif + *(sc->regs) = *regs; + // DAR, DSISR, RESULT? +} + +void fill_in_regs(struct sys_pt_regs *regs, void *sc_ptr) +{ + struct sigcontext *sc; + + sc = sc_ptr; + + *(sc->regs) = *regs; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/Makefile linux.ac/drivers/Makefile --- linux.vanilla/drivers/Makefile Sat May 26 16:53:00 2001 +++ linux.ac/drivers/Makefile Thu May 17 14:04:24 2001 @@ -6,8 +6,9 @@ # -mod-subdirs := dio mtd sbus video macintosh usb input telephony sgi i2o ide \ - scsi md ieee1394 pnp isdn atm fc4 net/hamradio i2c acpi +mod-subdirs := dio mtd sbus video macintosh usb input telephony sgi ide \ + message/i2o message/fusion scsi md ieee1394 pnp isdn atm \ + fc4 net/hamradio i2c acpi subdir-y := parport char block net sound misc media cdrom subdir-m := $(subdir-y) @@ -30,7 +31,8 @@ subdir-$(CONFIG_SGI) += sgi subdir-$(CONFIG_IDE) += ide subdir-$(CONFIG_SCSI) += scsi -subdir-$(CONFIG_I2O) += i2o +subdir-$(CONFIG_I2O) += message/i2o +subdir-$(CONFIG_FUSION) += message/fusion subdir-$(CONFIG_MD) += md subdir-$(CONFIG_IEEE1394) += ieee1394 subdir-$(CONFIG_PNP) += pnp @@ -44,4 +46,3 @@ subdir-$(CONFIG_ACPI) += acpi include $(TOPDIR)/Rules.make - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/atm/iphase.c linux.ac/drivers/atm/iphase.c --- linux.vanilla/drivers/atm/iphase.c Sun Feb 4 18:05:29 2001 +++ linux.ac/drivers/atm/iphase.c Fri May 25 00:04:33 2001 @@ -1,6 +1,7 @@ /****************************************************************************** iphase.c: Device driver for Interphase ATM PCI adapter cards Author: Peter Wang + Some fixes: Arnaldo Carvalho de Melo Interphase Corporation Version: 1.0 ******************************************************************************* @@ -80,7 +81,7 @@ static IADEV *ia_dev[8]; static struct atm_dev *_ia_dev[8]; -static int iadev_count = 0; +static int iadev_count; static void ia_led_timer(unsigned long arg); static struct timer_list ia_timer = { function: ia_led_timer }; struct atm_vcc *vcc_close_que[100]; @@ -118,8 +119,7 @@ } static int ia_enque_rtn_q (IARTN_Q *que, struct desc_tbl_t data) { - IARTN_Q *entry; - entry = (IARTN_Q *)kmalloc(sizeof(IARTN_Q), GFP_KERNEL); + IARTN_Q *entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -1; entry->data = data; entry->next = NULL; @@ -426,7 +426,7 @@ if (srv_p->rdf > MAX_RDF) return INVALID_RDF; #endif - memset ((caddr_t)f_abr_vc, 0, sizeof(f_vc_abr_entry)); + memset ((caddr_t)f_abr_vc, 0, sizeof(*f_abr_vc)); f_abr_vc->f_vc_type = ABR; nrm = 2 << srv_p->nrm; /* (2 ** (srv_p->nrm +1)) */ /* i.e 2**n = 2 << (n-1) */ @@ -541,7 +541,7 @@ TstSchedTbl = (u16*)(SchedTbl+testSlot); //set index and read in value IF_CBR(printk("CBR Testslot 0x%x AT Location 0x%x, NumToAssign=%d\n", testSlot, (u32)TstSchedTbl,toBeAssigned);) - memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(u16)); + memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC)); while (cbrVC) // If another VC at this location, we have to keep looking { inc++; @@ -552,7 +552,7 @@ (u32)SchedTbl,testSlot);) } TstSchedTbl = (u16 *)(SchedTbl + testSlot); // set table index - memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(u16)); + memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC)); if (!cbrVC) break; testSlot = idealSlot + inc; @@ -566,10 +566,10 @@ TstSchedTbl = (u16*)(SchedTbl + testSlot); IF_CBR(printk("Reading CBR Tbl from 0x%x, CbrVal=0x%x Iteration %d\n", (u32)TstSchedTbl,cbrVC,inc);) - memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(u16)); + memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC)); } /* while */ // Move this VCI number into this location of the CBR Sched table. - memcpy((caddr_t)TstSchedTbl, (caddr_t)&vcIndex,sizeof(u16)); + memcpy((caddr_t)TstSchedTbl, (caddr_t)&vcIndex,sizeof(TstSchedTbl)); dev->CbrRemEntries--; toBeAssigned--; } /* while */ @@ -655,20 +655,20 @@ skb = rtne->data.txskb; if (!skb) { printk("ia_tx_poll: skb is null\n"); - return; + goto out; } vcc = ATM_SKB(skb)->vcc; if (!vcc) { printk("ia_tx_poll: vcc is null\n"); dev_kfree_skb_any(skb); - return; + goto out; } iavcc = INPH_IA_VCC(vcc); if (!iavcc) { printk("ia_tx_poll: iavcc is null\n"); dev_kfree_skb_any(skb); - return; + goto out; } skb1 = skb_dequeue(&iavcc->txing_skb); @@ -702,6 +702,7 @@ kfree(rtne); } ia_que_tx(iadev); +out: return; } #if 0 @@ -921,9 +922,9 @@ suni_pm7345->suni_rxcp_ctrl = 0x2c; suni_pm7345->suni_rxcp_fctrl = 0x81; - suni_pm7345->suni_rxcp_idle_pat_h1 = 0; - suni_pm7345->suni_rxcp_idle_pat_h2 = 0; - suni_pm7345->suni_rxcp_idle_pat_h3 = 0; + suni_pm7345->suni_rxcp_idle_pat_h1 = + suni_pm7345->suni_rxcp_idle_pat_h2 = + suni_pm7345->suni_rxcp_idle_pat_h3 = 0; suni_pm7345->suni_rxcp_idle_pat_h4 = 1; suni_pm7345->suni_rxcp_idle_mask_h1 = 0xff; @@ -931,15 +932,15 @@ suni_pm7345->suni_rxcp_idle_mask_h3 = 0xff; suni_pm7345->suni_rxcp_idle_mask_h4 = 0xfe; - suni_pm7345->suni_rxcp_cell_pat_h1 = 0; - suni_pm7345->suni_rxcp_cell_pat_h2 = 0; - suni_pm7345->suni_rxcp_cell_pat_h3 = 0; + suni_pm7345->suni_rxcp_cell_pat_h1 = + suni_pm7345->suni_rxcp_cell_pat_h2 = + suni_pm7345->suni_rxcp_cell_pat_h3 = 0; suni_pm7345->suni_rxcp_cell_pat_h4 = 1; - suni_pm7345->suni_rxcp_cell_mask_h1 = 0xff; - suni_pm7345->suni_rxcp_cell_mask_h2 = 0xff; - suni_pm7345->suni_rxcp_cell_mask_h3 = 0xff; - suni_pm7345->suni_rxcp_cell_mask_h4 = 0xff; + suni_pm7345->suni_rxcp_cell_mask_h1 = + suni_pm7345->suni_rxcp_cell_mask_h2 = + suni_pm7345->suni_rxcp_cell_mask_h3 = + suni_pm7345->suni_rxcp_cell_mask_h4 = 0xff; suni_pm7345->suni_txcp_ctrl = 0xa4; suni_pm7345->suni_txcp_intr_en_sts = 0x10; @@ -1141,8 +1142,7 @@ else { IF_ERR(printk(" cause: buffer over flow\n");) } - free_desc(dev, desc); - return 0; + goto out_free_desc; } /* @@ -1155,8 +1155,7 @@ if (len > iadev->rx_buf_sz) { printk("Over %d bytes sdu received, dropped!!!\n", iadev->rx_buf_sz); atomic_inc(&vcc->stats->rx_err); - free_desc(dev, desc); - return 0; + goto out_free_desc; } #if LINUX_VERSION_CODE >= 0x20312 @@ -1170,8 +1169,7 @@ IF_ERR(printk("can't allocate memory for recv, drop pkt!\n");) atomic_inc(&vcc->stats->rx_drop); atm_return(vcc, atm_pdu2truesize(len)); - free_desc(dev, desc); - return 0; + goto out_free_desc; } } else { @@ -1179,8 +1177,7 @@ #endif if (vcc->vci < 32) printk("Drop control packets\n"); - free_desc(dev, desc); - return 0; + goto out_free_desc; } skb_put(skb,len); // pwang_test @@ -1203,7 +1200,10 @@ udelay(1); /* Increment transaction counter */ writel(1, iadev->dma+IPHASE5575_RX_COUNTER); - return 0; +out: return 0; +out_free_desc: + free_desc(dev, desc); + goto out; } static void rx_intr(struct atm_dev *dev) @@ -1277,6 +1277,7 @@ u_short state; struct dle *dle, *cur_dle; u_int dle_lp; + int len; iadev = INPH_IA_DEV(dev); /* free all the dles done, that is just update our own dle read pointer @@ -1296,7 +1297,7 @@ desc = ATM_DESC(skb); free_desc(dev, desc); - if (!skb->len) + if (!(len = skb->len)) { printk("rx_dle_intr: skb len 0\n"); dev_kfree_skb_any(skb); @@ -1320,15 +1321,15 @@ atomic_inc(&vcc->stats->rx_err); dev_kfree_skb_any(skb); #if LINUX_VERSION_CODE >= 0x20312 - atm_return(vcc, atm_guess_pdu2truesize(skb->len)); + atm_return(vcc, atm_guess_pdu2truesize(len)); #else - atm_return(vcc, atm_pdu2truesize(skb->len)); + atm_return(vcc, atm_pdu2truesize(len)); #endif goto INCR_DLE; } // get real pkt length pwang_test trailer = (struct cpcs_trailer*)((u_char *)skb->data + - skb->len - sizeof(struct cpcs_trailer)); + skb->len - sizeof(*trailer)); length = swap(trailer->length); if ((length > iadev->rx_buf_sz) || (length > (skb->len - sizeof(struct cpcs_trailer)))) @@ -1338,9 +1339,9 @@ IF_ERR(printk("rx_dle_intr: Bad AAL5 trailer %d (skb len %d)", length, skb->len);) #if LINUX_VERSION_CODE >= 0x20312 - atm_return(vcc, atm_guess_pdu2truesize(skb->len)); + atm_return(vcc, atm_guess_pdu2truesize(len)); #else - atm_return(vcc, atm_pdu2truesize(skb->len)); + atm_return(vcc, atm_pdu2truesize(len)); #endif goto INCR_DLE; } @@ -1448,10 +1449,11 @@ */ /* allocate 8k bytes */ - dle_addr = (u32*)kmalloc(2*sizeof(struct dle)*DLE_ENTRIES, GFP_KERNEL); + dle_addr = kmalloc(2*sizeof(struct dle)*DLE_ENTRIES, GFP_KERNEL); if (!dle_addr) { printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n"); + return -ENOMEM; } /* find 4k byte boundary within the 8k allocated */ dle_addr = (u32*)( ((u32)dle_addr+(4096-1)) & ~(4096-1) ); @@ -1496,12 +1498,12 @@ /* Initialize each entry in the Buffer Descriptor Table */ iadev->RX_DESC_BASE_ADDR = iadev->reass_ram+RX_DESC_BASE*iadev->memSize; buf_desc_ptr =(struct rx_buf_desc *)iadev->RX_DESC_BASE_ADDR; - memset((caddr_t)buf_desc_ptr, 0, sizeof(struct rx_buf_desc)); + memset((caddr_t)buf_desc_ptr, 0, sizeof(*buf_desc_ptr)); buf_desc_ptr++; rx_pkt_start = iadev->rx_pkt_ram; for(i=1; i<=iadev->num_rx_desc; i++) { - memset((caddr_t)buf_desc_ptr, 0, sizeof(struct rx_buf_desc)); + memset((caddr_t)buf_desc_ptr, 0, sizeof(*buf_desc_ptr)); buf_desc_ptr->buf_start_hi = rx_pkt_start >> 16; buf_desc_ptr->buf_start_lo = rx_pkt_start & 0x0000ffff; buf_desc_ptr++; @@ -1596,7 +1598,7 @@ i = ABR_VC_TABLE * iadev->memSize; abr_vc_table = (struct abr_vc_table *)(iadev->reass_ram+i); j = REASS_TABLE_SZ * iadev->memSize; - memset ((char*)abr_vc_table, 0, j * sizeof(struct abr_vc_table ) ); + memset ((char*)abr_vc_table, 0, j * sizeof(*abr_vc_table)); for(i = 0; i < j; i++) { abr_vc_table->rdf = 0x0003; abr_vc_table->air = 0x5eb1; @@ -1632,7 +1634,7 @@ skb_queue_head_init(&iadev->rx_dma_q); iadev->rx_free_desc_qhead = NULL; - iadev->rx_open =(struct atm_vcc **)kmalloc(4*iadev->num_vc,GFP_KERNEL); + iadev->rx_open = kmalloc(4*iadev->num_vc,GFP_KERNEL); if (!iadev->rx_open) { printk(KERN_ERR DEV_LABEL "itf %d couldn't get free page\n", @@ -1767,7 +1769,7 @@ } } ia_vcc = INPH_IA_VCC(vcc); - memset((caddr_t)ia_vcc, 0, sizeof(struct ia_vcc)); + memset((caddr_t)ia_vcc, 0, sizeof(*ia_vcc)); if (vcc->qos.txtp.max_sdu > (iadev->tx_buf_sz - sizeof(struct cpcs_trailer))){ printk("IA: SDU size over the configured SDU size %d\n", @@ -1812,8 +1814,8 @@ evc = (struct ext_vc *)iadev->EXT_VC_TABLE_ADDR; vc += vcc->vci; evc += vcc->vci; - memset((caddr_t)vc, 0, sizeof(struct main_vc)); - memset((caddr_t)evc, 0, sizeof(struct ext_vc)); + memset((caddr_t)vc, 0, sizeof(*vc)); + memset((caddr_t)evc, 0, sizeof(*evc)); /* store the most significant 4 bits of vci as the last 4 bits of first part of atm header. @@ -1918,10 +1920,11 @@ readw(iadev->seg_reg+SEG_MASK_REG));) /*---------- Initializing Transmit DLEs ----------*/ /* allocating 8k memory for transmit DLEs */ - dle_addr = (u32*)kmalloc(2*sizeof(struct dle)*DLE_ENTRIES, GFP_KERNEL); + dle_addr = kmalloc(2*sizeof(struct dle)*DLE_ENTRIES, GFP_KERNEL); if (!dle_addr) { printk(KERN_ERR DEV_LABEL "can't allocate TX DLEs\n"); + return -ENOMEM; } /* find 4k byte boundary within the 8k allocated */ @@ -1964,20 +1967,19 @@ /* initialize each entry in the buffer descriptor table */ buf_desc_ptr =(struct tx_buf_desc *)(iadev->seg_ram+TX_DESC_BASE); - memset((caddr_t)buf_desc_ptr, 0, sizeof(struct tx_buf_desc)); + memset((caddr_t)buf_desc_ptr, 0, sizeof(*buf_desc_ptr)); buf_desc_ptr++; tx_pkt_start = TX_PACKET_RAM; for(i=1; i<=iadev->num_tx_desc; i++) { - memset((caddr_t)buf_desc_ptr, 0, sizeof(struct tx_buf_desc)); + memset((caddr_t)buf_desc_ptr, 0, sizeof(*buf_desc_ptr)); buf_desc_ptr->desc_mode = AAL5; buf_desc_ptr->buf_start_hi = tx_pkt_start >> 16; buf_desc_ptr->buf_start_lo = tx_pkt_start & 0x0000ffff; buf_desc_ptr++; tx_pkt_start += iadev->tx_buf_sz; } - iadev->tx_buf= (caddr_t*)kmalloc(iadev->num_tx_desc*sizeof(caddr_t), - GFP_KERNEL); + iadev->tx_buf = kmalloc(iadev->num_tx_desc*sizeof(caddr_t), GFP_KERNEL); if (!iadev->tx_buf) { printk(KERN_ERR DEV_LABEL " couldn't get mem\n"); return -EAGAIN; @@ -1985,14 +1987,14 @@ for (i= 0; i< iadev->num_tx_desc; i++) { - iadev->tx_buf[i] =(caddr_t)kmalloc(sizeof(struct cpcs_trailer), + iadev->tx_buf[i] = kmalloc(sizeof(struct cpcs_trailer), GFP_KERNEL|GFP_DMA); if(!iadev->tx_buf[i]) { printk(KERN_ERR DEV_LABEL " couldn't get freepage\n"); return -EAGAIN; } } - iadev->desc_tbl = (struct desc_tbl_t *)kmalloc(iadev->num_tx_desc * + iadev->desc_tbl = kmalloc(iadev->num_tx_desc * sizeof(struct desc_tbl_t), GFP_KERNEL); /* Communication Queues base address */ @@ -2118,18 +2120,17 @@ memset((caddr_t)(iadev->seg_ram+i), 0, iadev->num_vc*4); vc = (struct main_vc *)iadev->MAIN_VC_TABLE_ADDR; evc = (struct ext_vc *)iadev->EXT_VC_TABLE_ADDR; - iadev->testTable = (struct testTable_t **) - kmalloc(sizeof(long)*iadev->num_vc, GFP_KERNEL); + iadev->testTable = kmalloc(sizeof(long)*iadev->num_vc, GFP_KERNEL); if (!iadev->testTable) { printk("Get freepage failed\n"); return -EAGAIN; } for(i=0; inum_vc; i++) { - memset((caddr_t)vc, 0, sizeof(struct main_vc)); - memset((caddr_t)evc, 0, sizeof(struct ext_vc)); - iadev->testTable[i] = (struct testTable_t *) - kmalloc(sizeof(struct testTable_t), GFP_KERNEL); + memset((caddr_t)vc, 0, sizeof(*vc)); + memset((caddr_t)evc, 0, sizeof(*evc)); + iadev->testTable[i] = kmalloc(sizeof(struct testTable_t), + GFP_KERNEL); if (!iadev->testTable[i]) return -ENOMEM; iadev->testTable[i]->lastTime = 0; @@ -2679,7 +2680,7 @@ vcc->dev->number, vcc->vpi, vcc->vci);) /* Device dependent initialization */ - ia_vcc = kmalloc(sizeof(struct ia_vcc), GFP_KERNEL); + ia_vcc = kmalloc(sizeof(*ia_vcc), GFP_KERNEL); if (!ia_vcc) return -ENOMEM; INPH_IA_VCC(vcc) = ia_vcc; @@ -2986,7 +2987,7 @@ /* Build the DLE structure */ wr_ptr = iadev->tx_dle_q.write; - memset((caddr_t)wr_ptr, 0, sizeof(struct dle)); + memset((caddr_t)wr_ptr, 0, sizeof(*wr_ptr)); wr_ptr->sys_pkt_addr = addr; wr_ptr->local_pkt_addr = (buf_desc_ptr->buf_start_hi << 16) | buf_desc_ptr->buf_start_lo; @@ -3172,9 +3173,9 @@ printk(KERN_ERR DEV_LABEL " driver but no PCI BIOS ?\n"); return 0; } - iadev = (IADEV *)kmalloc(sizeof(IADEV), GFP_KERNEL); + iadev = kmalloc(sizeof(*iadev), GFP_KERNEL); if (!iadev) return -ENOMEM; - memset((char*)iadev, 0, sizeof(IADEV)); + memset((char*)iadev, 0, sizeof(*iadev)); prev_dev = NULL; while((iadev->pci = pci_find_device(PCI_VENDOR_ID_IPHASE, PCI_DEVICE_ID_IPHASE_5575, prev_dev))) { @@ -3209,10 +3210,9 @@ prev_dev = iadev->pci; iadev->next_board = ia_boards; ia_boards = dev; - iadev = (IADEV *)kmalloc( - sizeof(IADEV), GFP_KERNEL); + iadev = kmalloc(sizeof(*iadev), GFP_KERNEL); if (!iadev) break; - memset((char*)iadev, 0, sizeof(IADEV)); + memset((char*)iadev, 0, sizeof(*iadev)); index++; dev = NULL; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/Config.in linux.ac/drivers/block/Config.in --- linux.vanilla/drivers/block/Config.in Sat Feb 3 20:13:19 2001 +++ linux.ac/drivers/block/Config.in Sat May 26 00:47:06 2001 @@ -28,6 +28,12 @@ tristate ' Atari SLM laser printer support' CONFIG_ATARI_SLM fi fi +if [ "$CONFIG_PPC_ISERIES" = "y" ]; then + dep_tristate 'iSeries Virtual I/O disk support' CONFIG_VIODASD $CONFIG_PPC_ISERIES + if [ "$CONFIG_VIODASD" = "y" -o "$CONFIG_VIODASD" = "m" ]; then + bool 'iSeries Virtual disk IDE emulation' CONFIG_VIODASD_IDE + fi +fi dep_tristate 'XT hard disk support' CONFIG_BLK_DEV_XD $CONFIG_ISA dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARPORT if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then @@ -42,7 +48,7 @@ tristate 'RAM disk support' CONFIG_BLK_DEV_RAM if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then - int ' Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096 + int ' Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096 fi dep_bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/DAC960.c linux.ac/drivers/block/DAC960.c --- linux.vanilla/drivers/block/DAC960.c Sat May 26 16:53:02 2001 +++ linux.ac/drivers/block/DAC960.c Wed May 2 14:09:07 2001 @@ -506,16 +506,20 @@ void *DataPointer) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox; - DAC960_V1_CommandStatus_T CommandStatus; - DAC960_V1_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->Type3.CommandOpcode = CommandOpcode; - CommandMailbox->Type3.BusAddress = Virtual_to_Bus32(DataPointer); - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V1.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V1_NormalCompletion); + if (Command == NULL) { + return false; + } else { + DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox; + DAC960_V1_CommandStatus_T CommandStatus; + DAC960_V1_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->Type3.CommandOpcode = CommandOpcode; + CommandMailbox->Type3.BusAddress = Virtual_to_Bus32(DataPointer); + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V1.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V1_NormalCompletion); + } } @@ -532,18 +536,22 @@ void *DataPointer) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox; - DAC960_V1_CommandStatus_T CommandStatus; - DAC960_V1_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->Type3D.CommandOpcode = CommandOpcode; - CommandMailbox->Type3D.Channel = Channel; - CommandMailbox->Type3D.TargetID = TargetID; - CommandMailbox->Type3D.BusAddress = Virtual_to_Bus32(DataPointer); - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V1.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V1_NormalCompletion); + if (Command == NULL) { + return false; + } else { + DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox; + DAC960_V1_CommandStatus_T CommandStatus; + DAC960_V1_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->Type3D.CommandOpcode = CommandOpcode; + CommandMailbox->Type3D.Channel = Channel; + CommandMailbox->Type3D.TargetID = TargetID; + CommandMailbox->Type3D.BusAddress = Virtual_to_Bus32(DataPointer); + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V1.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V1_NormalCompletion); + } } @@ -559,29 +567,33 @@ unsigned int DataByteCount) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; - DAC960_V2_CommandStatus_T CommandStatus; - DAC960_V2_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->Common.CommandOpcode = DAC960_V2_IOCTL; - CommandMailbox->Common.CommandControlBits + if (Command == NULL) { + return false; + } else { + DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; + DAC960_V2_CommandStatus_T CommandStatus; + DAC960_V2_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->Common.CommandOpcode = DAC960_V2_IOCTL; + CommandMailbox->Common.CommandControlBits .DataTransferControllerToHost = true; - CommandMailbox->Common.CommandControlBits + CommandMailbox->Common.CommandControlBits .NoAutoRequestSense = true; - CommandMailbox->Common.DataTransferSize = DataByteCount; - CommandMailbox->Common.IOCTL_Opcode = IOCTL_Opcode; - CommandMailbox->Common.DataTransferMemoryAddress + CommandMailbox->Common.DataTransferSize = DataByteCount; + CommandMailbox->Common.IOCTL_Opcode = IOCTL_Opcode; + CommandMailbox->Common.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentDataPointer = - Virtual_to_Bus64(DataPointer); - CommandMailbox->Common.DataTransferMemoryAddress + Virtual_to_Bus64(DataPointer); + CommandMailbox->Common.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentByteCount = - CommandMailbox->Common.DataTransferSize; - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V2.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V2_NormalCompletion); + CommandMailbox->Common.DataTransferSize; + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V2.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V2_NormalCompletion); + } } @@ -597,30 +609,34 @@ unsigned int DataByteCount) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; - DAC960_V2_CommandStatus_T CommandStatus; - DAC960_V2_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->ControllerInfo.CommandOpcode = DAC960_V2_IOCTL; - CommandMailbox->ControllerInfo.CommandControlBits + if (Command == NULL) { + return false; + } else { + DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; + DAC960_V2_CommandStatus_T CommandStatus; + DAC960_V2_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->ControllerInfo.CommandOpcode = DAC960_V2_IOCTL; + CommandMailbox->ControllerInfo.CommandControlBits .DataTransferControllerToHost = true; - CommandMailbox->ControllerInfo.CommandControlBits + CommandMailbox->ControllerInfo.CommandControlBits .NoAutoRequestSense = true; - CommandMailbox->ControllerInfo.DataTransferSize = DataByteCount; - CommandMailbox->ControllerInfo.ControllerNumber = 0; - CommandMailbox->ControllerInfo.IOCTL_Opcode = IOCTL_Opcode; - CommandMailbox->ControllerInfo.DataTransferMemoryAddress + CommandMailbox->ControllerInfo.DataTransferSize = DataByteCount; + CommandMailbox->ControllerInfo.ControllerNumber = 0; + CommandMailbox->ControllerInfo.IOCTL_Opcode = IOCTL_Opcode; + CommandMailbox->ControllerInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentDataPointer = - Virtual_to_Bus64(DataPointer); - CommandMailbox->ControllerInfo.DataTransferMemoryAddress + Virtual_to_Bus64(DataPointer); + CommandMailbox->ControllerInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentByteCount = - CommandMailbox->ControllerInfo.DataTransferSize; - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V2.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V2_NormalCompletion); + CommandMailbox->ControllerInfo.DataTransferSize; + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V2.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V2_NormalCompletion); + } } @@ -639,31 +655,35 @@ unsigned int DataByteCount) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; - DAC960_V2_CommandStatus_T CommandStatus; - DAC960_V2_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->LogicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL; - CommandMailbox->LogicalDeviceInfo.CommandControlBits + if (Command == NULL) { + return false; + } else { + DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; + DAC960_V2_CommandStatus_T CommandStatus; + DAC960_V2_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->LogicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL; + CommandMailbox->LogicalDeviceInfo.CommandControlBits .DataTransferControllerToHost = true; - CommandMailbox->LogicalDeviceInfo.CommandControlBits + CommandMailbox->LogicalDeviceInfo.CommandControlBits .NoAutoRequestSense = true; - CommandMailbox->LogicalDeviceInfo.DataTransferSize = DataByteCount; - CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber = - LogicalDeviceNumber; - CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode = IOCTL_Opcode; - CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress + CommandMailbox->LogicalDeviceInfo.DataTransferSize = DataByteCount; + CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber = + LogicalDeviceNumber; + CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode = IOCTL_Opcode; + CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentDataPointer = - Virtual_to_Bus64(DataPointer); - CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress + Virtual_to_Bus64(DataPointer); + CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentByteCount = - CommandMailbox->LogicalDeviceInfo.DataTransferSize; - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V2.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V2_NormalCompletion); + CommandMailbox->LogicalDeviceInfo.DataTransferSize; + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V2.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V2_NormalCompletion); + } } @@ -683,32 +703,36 @@ unsigned int DataByteCount) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; - DAC960_V2_CommandStatus_T CommandStatus; - DAC960_V2_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->PhysicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL; - CommandMailbox->PhysicalDeviceInfo.CommandControlBits + if (Command == NULL) { + return false; + } else { + DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; + DAC960_V2_CommandStatus_T CommandStatus; + DAC960_V2_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->PhysicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL; + CommandMailbox->PhysicalDeviceInfo.CommandControlBits .DataTransferControllerToHost = true; - CommandMailbox->PhysicalDeviceInfo.CommandControlBits + CommandMailbox->PhysicalDeviceInfo.CommandControlBits .NoAutoRequestSense = true; - CommandMailbox->PhysicalDeviceInfo.DataTransferSize = DataByteCount; - CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.LogicalUnit = LogicalUnit; - CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.TargetID = TargetID; - CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.Channel = Channel; - CommandMailbox->PhysicalDeviceInfo.IOCTL_Opcode = IOCTL_Opcode; - CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress + CommandMailbox->PhysicalDeviceInfo.DataTransferSize = DataByteCount; + CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.LogicalUnit = LogicalUnit; + CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.TargetID = TargetID; + CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.Channel = Channel; + CommandMailbox->PhysicalDeviceInfo.IOCTL_Opcode = IOCTL_Opcode; + CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentDataPointer = - Virtual_to_Bus64(DataPointer); - CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress + Virtual_to_Bus64(DataPointer); + CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress .ScatterGatherSegments[0] .SegmentByteCount = - CommandMailbox->PhysicalDeviceInfo.DataTransferSize; - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V2.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V2_NormalCompletion); + CommandMailbox->PhysicalDeviceInfo.DataTransferSize; + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V2.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V2_NormalCompletion); + } } @@ -724,21 +748,25 @@ OperationDevice) { DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); - DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; - DAC960_V2_CommandStatus_T CommandStatus; - DAC960_V2_ClearCommand(Command); - Command->CommandType = DAC960_ImmediateCommand; - CommandMailbox->DeviceOperation.CommandOpcode = DAC960_V2_IOCTL; - CommandMailbox->DeviceOperation.CommandControlBits + if (Command == NULL) { + return false; + } else { + DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox; + DAC960_V2_CommandStatus_T CommandStatus; + DAC960_V2_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->DeviceOperation.CommandOpcode = DAC960_V2_IOCTL; + CommandMailbox->DeviceOperation.CommandControlBits .DataTransferControllerToHost = true; - CommandMailbox->DeviceOperation.CommandControlBits + CommandMailbox->DeviceOperation.CommandControlBits .NoAutoRequestSense = true; - CommandMailbox->DeviceOperation.IOCTL_Opcode = IOCTL_Opcode; - CommandMailbox->DeviceOperation.OperationDevice = OperationDevice; - DAC960_ExecuteCommand(Command); - CommandStatus = Command->V2.CommandStatus; - DAC960_DeallocateCommand(Command); - return (CommandStatus == DAC960_V2_NormalCompletion); + CommandMailbox->DeviceOperation.IOCTL_Opcode = IOCTL_Opcode; + CommandMailbox->DeviceOperation.OperationDevice = OperationDevice; + DAC960_ExecuteCommand(Command); + CommandStatus = Command->V2.CommandStatus; + DAC960_DeallocateCommand(Command); + return (CommandStatus == DAC960_V2_NormalCompletion); + } } @@ -1423,20 +1451,27 @@ kmalloc(sizeof(DAC960_V2_PhysicalDeviceInfo_T), GFP_ATOMIC); if (PhysicalDeviceInfo == NULL) return DAC960_Failure(Controller, "PHYSICAL DEVICE ALLOCATION"); + InquiryUnitSerialNumber = (DAC960_SCSI_Inquiry_UnitSerialNumber_T *) + kmalloc(sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T), GFP_ATOMIC); + if (InquiryUnitSerialNumber == NULL) { + kfree(PhysicalDeviceInfo); + return DAC960_Failure(Controller, "SERIAL NUMBER ALLOCATION"); + } + Command = DAC960_AllocateCommand(Controller); + if (Command == NULL) { + kfree(PhysicalDeviceInfo); + kfree(InquiryUnitSerialNumber); + return DAC960_Failure(Controller, "COMMAND ALLOCATION"); + } Controller->V2.PhysicalDeviceInformation[PhysicalDeviceIndex] = PhysicalDeviceInfo; memcpy(PhysicalDeviceInfo, NewPhysicalDeviceInfo, sizeof(DAC960_V2_PhysicalDeviceInfo_T)); - InquiryUnitSerialNumber = (DAC960_SCSI_Inquiry_UnitSerialNumber_T *) - kmalloc(sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T), GFP_ATOMIC); - if (InquiryUnitSerialNumber == NULL) - return DAC960_Failure(Controller, "SERIAL NUMBER ALLOCATION"); Controller->V2.InquiryUnitSerialNumber[PhysicalDeviceIndex] = InquiryUnitSerialNumber; memset(InquiryUnitSerialNumber, 0, sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T)); InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F; - Command = DAC960_AllocateCommand(Controller); CommandMailbox = &Command->V2.CommandMailbox; DAC960_V2_ClearCommand(Command); Command->CommandType = DAC960_ImmediateCommand; @@ -4128,7 +4163,8 @@ if (InquiryUnitSerialNumber == NULL && PhysicalDeviceInfo != NULL) { - kfree(PhysicalDeviceInfo); + if (PhysicalDeviceInfo) + kfree(PhysicalDeviceInfo); PhysicalDeviceInfo = NULL; } DAC960_Critical("Physical Device %d:%d Now Exists%s\n", diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/Makefile linux.ac/drivers/block/Makefile --- linux.vanilla/drivers/block/Makefile Fri Dec 29 22:07:21 2000 +++ linux.ac/drivers/block/Makefile Sat May 26 00:24:53 2001 @@ -10,7 +10,9 @@ O_TARGET := block.o -export-objs := ll_rw_blk.o blkpg.o loop.o DAC960.o +mod-subdirs := paride + +export-objs := ll_rw_blk.o blkpg.o elevator.o loop.o DAC960.o obj-y := ll_rw_blk.o blkpg.o genhd.o elevator.o @@ -18,6 +20,7 @@ obj-$(CONFIG_BLK_DEV_FD) += floppy.o obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o +obj-$(CONFIG_VIODASD) += viodasd.o obj-$(CONFIG_BLK_DEV_SWIM_IOP) += swim_iop.o obj-$(CONFIG_ATARI_ACSI) += acsi.o obj-$(CONFIG_ATARI_SLM) += acsi_slm.o @@ -27,11 +30,17 @@ obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o obj-$(CONFIG_BLK_DEV_XD) += xd.o obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o -obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o +obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o subdir-$(CONFIG_PARIDE) += paride + +ifeq ($(CONFIG_ARCH_ACORN),y) +mod-subdirs += ../acorn/block +subdir-y += ../acorn/block +obj-y += ../acorn/block/acorn-block.o +endif include $(TOPDIR)/Rules.make diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/cciss.c linux.ac/drivers/block/cciss.c --- linux.vanilla/drivers/block/cciss.c Sat May 26 16:53:02 2001 +++ linux.ac/drivers/block/cciss.c Thu May 24 23:31:27 2001 @@ -610,7 +610,10 @@ { /* Copy the data into the buffer we created */ if (copy_from_user(buff, iocommand.buf, iocommand.buf_size)) + { + kfree(buff); return -EFAULT; + } } if ((c = cmd_alloc(h , 0)) == NULL) { @@ -680,6 +683,7 @@ { kfree(buff); cmd_free(h, c, 0); + return -EFAULT; } } kfree(buff); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/elevator.c linux.ac/drivers/block/elevator.c --- linux.vanilla/drivers/block/elevator.c Fri Feb 16 00:58:34 2001 +++ linux.ac/drivers/block/elevator.c Mon Apr 9 23:39:31 2001 @@ -220,3 +220,11 @@ *elevator = type; elevator->queue_ID = queue_ID++; } + +EXPORT_SYMBOL(elevator_init); +EXPORT_SYMBOL(elevator_linus_merge); +EXPORT_SYMBOL(elevator_linus_merge_cleanup); +EXPORT_SYMBOL(elevator_linus_merge_req); +EXPORT_SYMBOL(elevator_noop_merge); +EXPORT_SYMBOL(elevator_noop_merge_cleanup); +EXPORT_SYMBOL(elevator_noop_merge_req); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/genhd.c linux.ac/drivers/block/genhd.c --- linux.vanilla/drivers/block/genhd.c Sat May 26 16:53:02 2001 +++ linux.ac/drivers/block/genhd.c Sat May 26 00:24:54 2001 @@ -21,6 +21,9 @@ #ifdef CONFIG_BLK_DEV_DAC960 extern void DAC960_Initialize(void); #endif +#ifdef CONFIG_FUSION_BOOT +extern int fusion_init(void); +#endif extern int net_dev_init(void); extern void console_map_init(void); extern int soc_probe(void); @@ -39,6 +42,9 @@ #ifdef CONFIG_BLK_DEV_DAC960 DAC960_Initialize(); #endif +#ifdef CONFIG_FUSION_BOOT + fusion_init(); +#endif #ifdef CONFIG_FC4_SOC /* This has to be done before scsi_dev_init */ soc_probe(); @@ -57,6 +63,9 @@ #endif #ifdef CONFIG_VT console_map_init(); +#endif +#ifdef CONFIG_VIODASD + viodasd_init(); #endif return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/ll_rw_blk.c linux.ac/drivers/block/ll_rw_blk.c --- linux.vanilla/drivers/block/ll_rw_blk.c Mon Apr 30 15:13:14 2001 +++ linux.ac/drivers/block/ll_rw_blk.c Sat May 26 00:24:53 2001 @@ -365,6 +365,11 @@ */ for (i = 0; i < queue_nr_requests; i++) { rq = kmem_cache_alloc(request_cachep, SLAB_KERNEL); + if (rq == NULL) { + /* We'll get a `leaked requests' message from blk_cleanup_queue */ + printk(KERN_EMERG "blk_init_free_list: error allocating requests\n"); + break; + } memset(rq, 0, sizeof(struct request)); rq->rq_status = RQ_INACTIVE; list_add(&rq->table, &q->request_freelist[i & 1]); @@ -423,6 +428,7 @@ q->plug_tq.routine = &generic_unplug_device; q->plug_tq.data = q; q->plugged = 0; + /* * These booleans describe the queue properties. We set the * default (and most common) values here. Other drivers can @@ -929,6 +935,20 @@ if (!test_bit(BH_Lock, &bh->b_state)) BUG(); + /* + * don't lock any more buffers if we are above the high + * water mark. instead start I/O on the queued stuff. + */ + if (atomic_read(&queued_sectors) >= high_queued_sectors) { + run_task_queue(&tq_disk); + if (rw == READA) { + bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state)); + return; + } + wait_event(blk_buffers_wait, + atomic_read(&queued_sectors) < low_queued_sectors); + } + set_bit(BH_Req, &bh->b_state); /* @@ -1030,16 +1050,6 @@ for (i = 0; i < nr; i++) { struct buffer_head *bh = bhs[i]; - /* - * don't lock any more buffers if we are above the high - * water mark. instead start I/O on the queued stuff. - */ - if (atomic_read(&queued_sectors) >= high_queued_sectors) { - run_task_queue(&tq_disk); - wait_event(blk_buffers_wait, - atomic_read(&queued_sectors) < low_queued_sectors); - } - /* Only one thread can actually submit the I/O. */ if (test_and_set_bit(BH_Lock, &bh->b_state)) continue; @@ -1225,6 +1235,9 @@ #endif #ifdef CONFIG_BLK_DEV_XD xd_init(); +#endif +#ifdef CONFIG_VIOCD + viocd_init(); #endif #ifdef CONFIG_BLK_DEV_MFM mfm_init(); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/rd.c linux.ac/drivers/block/rd.c --- linux.vanilla/drivers/block/rd.c Fri Feb 9 19:30:22 2001 +++ linux.ac/drivers/block/rd.c Sat May 26 00:24:53 2001 @@ -466,7 +466,7 @@ * romfs * gzip */ -int __init +static int __init identify_ramdisk_image(kdev_t device, struct file *fp, int start_block) { const int size = 512; @@ -570,8 +570,9 @@ char *buf; unsigned short rotate = 0; unsigned short devblocks = 0; +#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_PPC_ISERIES) char rotator[4] = { '|' , '/' , '-' , '\\' }; - +#endif ram_device = MKDEV(MAJOR_NR, unit); if ((inode = get_empty_inode()) == NULL) @@ -672,7 +673,7 @@ } infile.f_op->read(&infile, buf, BLOCK_SIZE, &infile.f_pos); outfile.f_op->write(&outfile, buf, BLOCK_SIZE, &outfile.f_pos); -#if !defined(CONFIG_ARCH_S390) +#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_PPC_ISERIES) if (!(i % 16)) { printk("%c\b", rotator[rotate & 0x3]); rotate++; @@ -690,6 +691,7 @@ done: if (infile.f_op->release) infile.f_op->release(inode, &infile); + blkdev_put(out_inode->i_bdev, BDEV_FILE); set_fs(fs); return; free_inodes: /* free inodes on error */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/block/viodasd.c linux.ac/drivers/block/viodasd.c --- linux.vanilla/drivers/block/viodasd.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/block/viodasd.c Sat May 26 00:24:53 2001 @@ -0,0 +1,1350 @@ +/* + * viodasd.c + * Copyright (C) 2001 Dave Boutcher IBM Corporation + *************************************************************************** + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *************************************************************************** + * This routine provides access to disk space (termed "DASD" in historical + * IBM terms) owned and managed by an OS/400 partition running on the + * same box as this Linux partition. + * + * All disk operations are performed by sending messages back and forth to + * the OS/400 partition. The format of the messages is defined in + * include/asm-ppc/iSeries/vio.h + * + * This device driver can either use it's own major number, or it can + * pretend to be an IDE drive (Major #3). Currently it doesn't + * emulate all the other IDE majors. This is controlled with a + * CONFIG option. You can either call this an elegant solution to the + * fact that a lot of software doesn't recognize a new disk major number... + * or you can call this a really ugly hack. Your choice. + */ + +#include +#include + +/*************************************************************************** + * Decide if we are using our own major or pretending to be an IDE drive + * + * If we are using our own majors, we only support 3 partitions per physical + * disk....so with minor numbers 0-255 we get a maximum of 64 disks. If we + * are emulating IDE, we get 16 partitions per disk, with a maximum of 16 + * disks + ***************************************************************************/ +#ifdef CONFIG_VIODASD_IDE +#define MAJOR_NR IDE0_MAJOR +#define PARTITION_SHIFT 6 +#define do_viodasd_request do_hd_request +static int numdsk = 16; +static int viodasd_max_disk = 16; +#define VIOD_DEVICE_NAME "hd" +#else +#define MAJOR_NR VIODASD_MAJOR +#define PARTITION_SHIFT 3 +static int numdsk = 32; +static int viodasd_max_disk = 32; +#define VIOD_DEVICE_NAME "viod" +#endif + + +#define VIODASD_VERS "1.02" +#define LOCAL_END_REQUEST + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPEVENT_H +#include +#endif +#ifndef _HVLPCONFIG_H +#include +#endif +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif +#ifndef _ISERIES_PROC_H +#include +#endif + +MODULE_DESCRIPTION("iSeries Virtual DASD"); +MODULE_AUTHOR("Dave Boutcher"); + +/*************************************************************************** + * In a perfect world we will perform better if we get page-aligned I/O + * requests, in multiples of pages. At least peg our block size fo the + * actual page size. + ***************************************************************************/ +static int blksize = HVPAGESIZE; /* in bytes */ + +static DECLARE_WAIT_QUEUE_HEAD(viodasd_wait); +struct viodasd_waitevent { + struct semaphore *sem; + int rc; + int changed; /* Used only for check_change */ +}; + +/*************************************************************************** + * All our disk-related global structures + ***************************************************************************/ +static struct hd_struct *viodasd_partitions; +static int *viodasd_sizes; +static int *viodasd_sectsizes; +static int *viodasd_maxsectors; +extern struct gendisk viodasd_gendsk; + +/*************************************************************************** + * Figure out the biggest I/O request (in sectors) we can accept + ***************************************************************************/ +#define VIODASD_MAXSECTORS (4096 / 512 * VIOMAXBLOCKDMA) + +/*************************************************************************** + * Keep some statistics on what's happening for the PROC file system + ***************************************************************************/ +static struct { + long tot; + long nobh; + long ntce[VIOMAXBLOCKDMA]; +} viod_stats[64][2]; + +/*************************************************************************** + * Number of disk I/O requests we've sent to OS/400 + ***************************************************************************/ +static int numReqOut; + +/*************************************************************************** + * Our internal printk macro + ***************************************************************************/ +#define err_printk(fmt, args...) \ +printk(KERN_ERR "(%s:%3.3d) ERR: " fmt, __FILE__, __LINE__ , ## args) + +/*************************************************************************** + * This is our internal structure for keeping track of disk devices + ***************************************************************************/ +struct viodasd_device { + int useCount; + u16 cylinders; + u16 tracks; + u16 sectors; + u16 bytesPerSector; + u64 size; + int readOnly; +} *viodasd_devices; + +/*************************************************************************** + * When we get a disk I/O request we take it off the general request queue + * and put it here. + ***************************************************************************/ +LIST_HEAD(reqlist); + +/*************************************************************************** + *************************************************************************** + * CODE STARTS HERE + *************************************************************************** + ***************************************************************************/ + +/*************************************************************************** + * Handle reads from the proc file system + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + int i; + int j; + +#if defined(MODULE) + len += sprintf(buf+len,"viod Module opened %d times. Major number %d\n", + MOD_IN_USE, + MAJOR_NR); +#endif + len += sprintf(buf+len,"viod %d devices\n", + numdsk); + + for (i=0; i<16; i++) + { + if (viod_stats[i][0].tot || viod_stats[i][1].tot) + { + len += sprintf(buf+len,"DISK %2.2d: rd %-10.10ld wr %-10.10ld (no buffer list rd %-10.10ld wr %-10.10ld\n", + i, viod_stats[i][0].tot,viod_stats[i][1].tot,viod_stats[i][0].nobh,viod_stats[i][1].nobh); + + len += sprintf(buf+len,"rd DMA: "); + + for (j=0; jnlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/*************************************************************************** + * clean up our proc file system entries + ***************************************************************************/ +void viodasd_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viodasd", iSeries_proc); +} + +/*************************************************************************** + * End a request + ***************************************************************************/ +static void viodasd_end_request(struct request *req, int uptodate) { + + if (end_that_request_first(req, uptodate, VIOD_DEVICE_NAME)) + return; + + end_that_request_last(req); +} + +/*************************************************************************** + * This rebuilds the partition information for a single disk device + ***************************************************************************/ +static int viodasd_revalidate(kdev_t dev) +{ + int i; + int device_no = DEVICE_NR(dev); + int part0 = (device_no << PARTITION_SHIFT); + int npart = (1<=0;i--) + { + minor = part0 +i; + + if (viodasd_partitions[minor].nr_sects != 0) + { + devp = MKDEV(MAJOR_NR, minor); + fsync_dev(devp); + + sb = get_super(devp); + if (sb) invalidate_inodes(sb); + + invalidate_buffers(devp); + } + + viodasd_partitions[minor].start_sect = 0; + viodasd_partitions[minor].nr_sects = 0; + } + + grok_partitions(&viodasd_gendsk, device_no, npart, viodasd_devices[device_no].size>>9); + + return 0; +} + +/*************************************************************************** + * This is the actual open code. It gets called from the external + * open entry point, as well as from the init code when we're figuring + * out what disks we have + ***************************************************************************/ +static int internal_open(int device_no) +{ + int i; + struct viodasd_waitevent we; + + HvLpEvent_Rc hvrc; + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) + { + err_printk("Invalid hosting partition\n"); + return -EIO; + } + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code */ + if (we.rc != 0) + { + err_printk("bad rc opening disk: %d\n",(int)we.rc); + return we.rc; + } + + /* If this is the first open of this device, update the device information */ + /* If this is NOT the first open, assume that it isn't changing */ + if (viodasd_devices[device_no].useCount == 0) + { + if (viodasd_devices[device_no].size > 0) + { + /* divide by 512 */ + u64 tmpint = viodasd_devices[device_no].size >> 9; + viodasd_partitions[device_no << PARTITION_SHIFT].nr_sects = tmpint; + /* Now the value divided by 1024 */ + tmpint = tmpint >> 1; + viodasd_sizes[device_no << PARTITION_SHIFT] = tmpint; + + for (i = (device_no << PARTITION_SHIFT); + i < ((device_no+1) << PARTITION_SHIFT); + i++) + viodasd_sectsizes[i] = viodasd_devices[device_no].bytesPerSector; + + } + } + else + { + /* If the size of the device changed, wierd things are happening! */ + if (viodasd_sizes[device_no<> 10) + { + err_printk("disk size change (%dK to %dK) for device %d\n", + viodasd_sizes[device_no<> 10, + device_no); + } + } + + /* Bump the use count */ + viodasd_devices[device_no].useCount++; + + return 0; +} + +/*************************************************************************** + * This is the actual release code. It gets called from the external + * release entry point, as well as from the init code when we're figuring + * out what disks we have + ***************************************************************************/ +static int internal_release(int device_no) +{ + + /* Send the event to OS/400. We DON'T expect a response */ + HvLpEvent_Rc hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockclose, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + viodasd_devices[device_no].useCount--; + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + return 0; +} + +/*************************************************************************** + * External open entry point. + ***************************************************************************/ +static int viodasd_open(struct inode *ino, struct file *fil) +{ + int device_no; + + /* Do a bunch of sanity checks */ + if (!ino) + { + err_printk("no inode provided in open\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) + { + err_printk("Wierd error...wrong major number on open\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) + { + err_printk("Invalid minor device number %d in open\n",device_no); + return -ENODEV; + } + + /* Call the actual open code */ + if (internal_open(device_no) == 0) + { + if (fil && fil->f_mode) + { + if (fil->f_mode & 2) + { + if (viodasd_devices[device_no].readOnly) { + internal_release( device_no ); + return -EROFS; + } + } + } + MOD_INC_USE_COUNT; + return 0; + } + else + { + return -EIO; + } +} + +/*************************************************************************** + * External release entry point. + ***************************************************************************/ +static int viodasd_release(struct inode *ino, struct file *fil) +{ + int device_no; + + /* Do a bunch of sanity checks */ + if (!ino) + { + err_printk("no inode provided in release\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) + { + err_printk("Wierd error...wrong major number on release\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) + { + return -ENODEV; + } + + /* Just to be paranoid, sync the device */ + fsync_dev(ino->i_rdev); + + /* Call the actual release code */ + internal_release(device_no); + + MOD_DEC_USE_COUNT; + return 0; +} + +/*************************************************************************** + * External ioctl entry point. + ***************************************************************************/ +static int viodasd_ioctl(struct inode *ino, struct file *fil, unsigned int cmd, unsigned long arg) +{ + int device_no; + int err; + HvLpEvent_Rc hvrc; + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Sanity checks */ + if (!ino) + { + err_printk("no inode provided in ioctl\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) + { + err_printk("Wierd error...wrong major number on ioctl\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) + { + err_printk("Invalid minor device number %d in ioctl\n",device_no); + return -ENODEV; + } + + switch(cmd) { + case BLKGETSIZE: + /* return the device size in sectors */ + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *)arg, sizeof(long)); + if (err) return err; + + put_user(viodasd_partitions[MINOR(ino->i_rdev)].nr_sects, (long *)arg); + return 0; + + case FDFLUSH: + case BLKFLSBUF: + if (!suser()) return -EACCES; + fsync_dev(ino->i_rdev); + invalidate_buffers(ino->i_rdev); + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockflush, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&Semaphore, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + err_printk("bad rc on sync signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + down(&Semaphore); + + return 0; + + case BLKRAGET: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *)arg, sizeof(long)); + if (err) return err; + put_user(read_ahead[MAJOR_NR], (long *)arg); + return 0; + + case BLKRASET: + if (!suser()) return -EACCES; + if (arg > 0x00ff) return -EINVAL; + read_ahead[MAJOR_NR] = arg; + return 0; + + case BLKRRPART: + viodasd_revalidate(ino->i_rdev); + return 0; + + case HDIO_GETGEO: + { + unsigned char sectors; + unsigned char heads; + unsigned short cylinders; + + struct hd_geometry *geo = (struct hd_geometry *)arg; + if (geo == NULL) return -EINVAL; + + err = verify_area(VERIFY_WRITE, geo, sizeof(*geo)); + if (err) return err; + + sectors = viodasd_devices[device_no].sectors; + if (sectors == 0) + sectors = 32; + + heads = viodasd_devices[device_no].tracks; + if (heads == 0) + heads = 64; + + cylinders = viodasd_devices[device_no].cylinders; + if (cylinders == 0) + cylinders = viodasd_partitions[MINOR(ino->i_rdev)].nr_sects / (sectors * heads); + + put_user(sectors , &geo->sectors ); + put_user(heads , &geo->heads ); + put_user(cylinders, &geo->cylinders); + + put_user(viodasd_partitions[MINOR(ino->i_rdev)].start_sect,(long *)&geo->start); + + return 0; + } + +#define PRTIOC(x) case x: err_printk("got unsupported FD ioctl " #x "\n"); \ + return -EINVAL; + + PRTIOC(FDCLRPRM); + PRTIOC(FDSETPRM); + PRTIOC(FDDEFPRM); + PRTIOC(FDGETPRM); + PRTIOC(FDMSGON); + PRTIOC(FDMSGOFF); + PRTIOC(FDFMTBEG); + PRTIOC(FDFMTTRK); + PRTIOC(FDFMTEND); + PRTIOC(FDSETEMSGTRESH); + PRTIOC(FDSETMAXERRS); + PRTIOC(FDGETMAXERRS); + PRTIOC(FDGETDRVTYP); + PRTIOC(FDSETDRVPRM); + PRTIOC(FDGETDRVPRM); + PRTIOC(FDGETDRVSTAT); + PRTIOC(FDPOLLDRVSTAT); + PRTIOC(FDRESET); + PRTIOC(FDGETFDCSTAT); + PRTIOC(FDWERRORCLR); + PRTIOC(FDWERRORGET); + PRTIOC(FDRAWCMD); + PRTIOC(FDEJECT); + PRTIOC(FDTWADDLE); + + } + + return -EINVAL; +} + +/*************************************************************************** + * Send an actual I/O request to OS/400 + ***************************************************************************/ +static int send_request(struct request *req) +{ + u64 sect_size; + u64 start; + u64 len; + int direction; + int nsg; + u16 viocmd; + HvLpEvent_Rc hvrc; + struct vioblocklpevent *bevent = (struct vioblocklpevent *)vio_block_event_buffer; + struct scatterlist sg[VIOMAXBLOCKDMA]; + struct buffer_head *bh; + int sgindex; + int device_no = DEVICE_NR(req->rq_dev); + int statindex; + + /* Note that this SHOULD always be 512...but lets be architecturally correct */ + sect_size = hardsect_size[MAJOR_NR][device_no]; + + /* Figure out teh starting sector and length */ + start = (req->sector + viodasd_partitions[MINOR(req->rq_dev)].start_sect) * sect_size; + len = req->nr_sectors * sect_size; + + /* More paranoia checks */ + if ((req->sector + req->nr_sectors) > + (viodasd_partitions[MINOR(req->rq_dev)].start_sect + + viodasd_partitions[MINOR(req->rq_dev)].nr_sects)) + { + err_printk("Invalid request offset & length\n"); + err_printk("req->sector: %ld, req->nr_sectors: %ld\n", req->sector, req->nr_sectors); + err_printk("RQ_DEV: %d, minor: %d\n", req->rq_dev, MINOR(req->rq_dev)); + return -1; + } + + if (req->cmd == READ) + { + direction = PCI_DMA_FROMDEVICE; + viocmd = viomajorsubtype_blockio | vioblockread; + statindex = 0; + } + else + { + direction = PCI_DMA_TODEVICE; + viocmd = viomajorsubtype_blockio | vioblockwrite; + statindex = 1; + } + + /* Update totals */ + viod_stats[device_no][statindex].tot++; + + /* Now build the scatter-gather list */ + memset(&sg, 0x00, sizeof(sg)); + sgindex = 0; + + /* See if this is a swap I/O (without a bh pointer) or a regular I/O */ + if (req->bh) + { + /* OK...this loop takes buffers from the request and adds them to the SG + until we're done, or until we hit a maximum. If we hit a maximum we'll + just finish this request later */ + bh = req->bh; + while ((bh) && (sgindex < VIOMAXBLOCKDMA)) + { + sg[sgindex].address = bh->b_data; + sg[sgindex].length = bh->b_size; + + sgindex++; + bh = bh->b_reqnext; + } + nsg = pci_map_sg(NULL, sg, sgindex, direction); + if ((nsg == 0) || (sg[0].dma_length == 0) || (sg[0].dma_address == 0xFFFFFFFF)) + { + err_printk("error getting sg tces\n"); + return -1; + } + + } + else + { + /* Update stats */ + viod_stats[device_no][statindex].nobh++; + + sg[0].dma_address = pci_map_single(NULL, req->buffer, + len, + direction); + if (sg[0].dma_address == 0xFFFFFFFF) + { + err_printk("error allocating tce for address %p len %ld\n", + req->buffer, + (long)len); + return -1; + } + sg[0].dma_length = len; + nsg = 1; + } + + /* Update stats */ + viod_stats[device_no][statindex].ntce[sgindex]++; + + /* This optimization handles a single DMA block */ + if (sgindex == 1) + { + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | viocmd, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)req->buffer, + VIOVERSION << 16, + ((u64)device_no << 48), + start, + ((u64)sg[0].dma_address)<<32, + sg[0].dma_length); + } + else + { + /* Now build up the actual request. Note that we store the pointer */ + /* to the request buffer in the correlation token so we can match */ + /* this response up later */ + memset(bevent,0x00,sizeof(struct vioblocklpevent)); + bevent->event.xFlags.xValid = 1; + bevent->event.xFlags.xFunction = HvLpEvent_Function_Int; + bevent->event.xFlags.xAckInd = HvLpEvent_AckInd_DoAck; + bevent->event.xFlags.xAckType = HvLpEvent_AckType_ImmediateAck; + bevent->event.xType = HvLpEvent_Type_VirtualIo; + bevent->event.xSubtype = viocmd; + bevent->event.xSourceLp = HvLpConfig_getLpIndex(); + bevent->event.xTargetLp = viopath_hostLp; + bevent->event.xSizeMinus1 = offsetof(struct vioblocklpevent, + u.rwData.dmaInfo) + + (sizeof(bevent->u.rwData.dmaInfo[0])*(sgindex))-1; + bevent->event.xSizeMinus1 = sizeof(struct vioblocklpevent)-1; + bevent->event.xSourceInstanceId = viopath_sourceinst(viopath_hostLp); + bevent->event.xTargetInstanceId = viopath_targetinst(viopath_hostLp); + bevent->event.xCorrelationToken = (u64)(u32)req->buffer; + bevent->mVersion = VIOVERSION; + bevent->mDisk = device_no; + bevent->u.rwData.mOffset = start; + + /* Copy just the dma information from the sg list into the request */ + for (sgindex = 0; sgindex < nsg; sgindex++) + { + bevent->u.rwData.dmaInfo[sgindex].mToken = sg[sgindex].dma_address; + bevent->u.rwData.dmaInfo[sgindex].mLen = sg[sgindex].dma_length; + } + + /* Send the request */ + hvrc = HvCallEvent_signalLpEvent(&bevent->event); + } + + if (hvrc != HvLpEvent_Rc_Good) + { + err_printk("hv error on op %d\n",(int)hvrc); + return -1; + } + else + { + /* If the request was successful, bump the number of outstanding */ + numReqOut++; + } + return 0; +} + +/*************************************************************************** + * This is the external request processing routine + ***************************************************************************/ +static void do_viodasd_request(request_queue_t *q) +{ + int device_no; + struct request *req; + for (;;) { + + INIT_REQUEST; + + device_no = CURRENT_DEV; + if (device_no > numdsk) + { + err_printk("Invalid device # %d\n",CURRENT_DEV); + viodasd_end_request(CURRENT, 0); + continue; + } + + if (viodasd_gendsk.sizes == NULL) + { + err_printk("Ouch! viodasd_gendsk.sizes is NULL\n"); + viodasd_end_request(CURRENT, 0); + continue; + } + + /* If the queue is plugged, don't dequeue anything right now */ + if ((q) && (q->plugged)) + { + return; + } + + /* If we already have the maximum number of requests outstanding to OS/400 + just bail out. We'll come back later */ + if (numReqOut >= VIOMAXREQ) + return; + + /* get the current request, then dequeue it from the queue */ + req = CURRENT; + blkdev_dequeue_request(req); + + /* Try sending the request */ + if (send_request(req) == 0) + { + list_add_tail(&req->queue, &reqlist); + } + else + { + viodasd_end_request(req, 0); + } + } +} + +/*************************************************************************** + * Check for changed disks + ***************************************************************************/ +static int viodasd_check_change(kdev_t dev) +{ + struct viodasd_waitevent we; + HvLpEvent_Rc hvrc; + int device_no = DEVICE_NR(dev); + + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) + { + err_printk("Invalid hosting partition\n"); + return -EIO; + } + + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockcheck, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code. If bad, assume no change */ + if (we.rc != 0) + { + err_printk("bad rc on check_change. Assuming no change\n"); + return 0; + } + + return we.changed; +} + +/*************************************************************************** + * Our file operations table + ***************************************************************************/ +static struct block_device_operations viodasd_fops = { + open: viodasd_open, + release: viodasd_release, + ioctl: viodasd_ioctl, + check_media_change: viodasd_check_change, + revalidate: viodasd_revalidate +}; + +/*************************************************************************** + * Our gendisk table + ***************************************************************************/ +struct gendisk viodasd_gendsk = { + 0, /* major - fill in later */ + "viodasd", + PARTITION_SHIFT, + 1<xFlags.xFunction == HvLpEvent_Function_Int) + { + err_printk("Yikes! got an int in viodasd event handler!\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + + switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) + { + + /* Handle a response to an open request. We get all the disk information + * in the response, so update it. The correlation token contains a pointer to + * a waitevent structure that has a semaphore in it. update the return code + * in the waitevent structure and post the semaphore to wake up the guy who + * sent the request */ + case vioblockopen: + pwe = (struct viodasd_waitevent *)(u32)event->xCorrelationToken; + pwe->rc = event->xRc; + if (event->xRc == HvLpEvent_Rc_Good) + { + viodasd_devices[bevent->mDisk].size = bevent->u.openData.mDiskLen; + viodasd_devices[bevent->mDisk].cylinders = bevent->u.openData.mCylinders; + viodasd_devices[bevent->mDisk].tracks = bevent->u.openData.mTracks; + viodasd_devices[bevent->mDisk].sectors = bevent->u.openData.mSectors; + viodasd_devices[bevent->mDisk].bytesPerSector = bevent->u.openData.mBytesPerSector; + viodasd_devices[bevent->mDisk].readOnly = bevent->mFlags & vioblockflags_ro; + + if (viodasd_max_disk != bevent->u.openData.mMaxDisks) + { + viodasd_max_disk = bevent->u.openData.mMaxDisks; + } + } + up(pwe->sem); + break; + + case vioblockclose: + break; + + /* For read and write requests, decrement the number of outstanding requests, + * Free the DMA buffers we allocated, and find the matching request by + * using the buffer pointer we stored in the correlation token. + */ + case vioblockread: + case vioblockwrite: + + /* Decrement the number of outstanding requests */ + numReqOut--; + + /* Free the DMA buffers */ + i = 0; + nsect = 0; + memset(sg, 0x00, sizeof(sg)); + + maxsg = (((bevent->event.xSizeMinus1 + 1) - + offsetof(struct vioblocklpevent, + u.rwData.dmaInfo)) / + sizeof(bevent->u.rwData.dmaInfo[0])); + + + while ((iu.rwData.dmaInfo[i].mLen > 0) && + (i < VIOMAXBLOCKDMA)) + { + sg[i].dma_address = bevent->u.rwData.dmaInfo[i].mToken; + sg[i].dma_length = bevent->u.rwData.dmaInfo[i].mLen; + nsect += bevent->u.rwData.dmaInfo[i].mLen; + i++; + } + + pci_unmap_sg(NULL, + sg, + i, + (bevent->event.xSubtype == (viomajorsubtype_blockio | vioblockread)) ? + PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + + + /* Since this is running in interrupt mode, we need to make sure we're not + * stepping on any global I/O operations + */ + spin_lock_irqsave(&io_request_lock, flags); + + /* Now find the matching request in OUR list (remember we moved the request + * from the global list to our list when we got it) + */ + req = blkdev_entry_to_request(reqlist.next); + while ((&req->queue != &reqlist) && + ((u64)(u32)req->buffer != bevent->event.xCorrelationToken)) + req = blkdev_entry_to_request(req->queue.next); + + if (&req->queue == &reqlist) + { + err_printk("Yikes! Could not find matching buffer %p in reqlist\n", + req->buffer); + break; + } + + /* Remove the request from our list */ + list_del(&req->queue); + + /* Calculate the number of sectors from the length in bytes */ + nsect = nsect >> 9; + if (!req->bh) { + if (event->xRc != HvLpEvent_Rc_Good) + { + err_printk("rw error %d:%d\n",event->xRc,bevent->mSubTypeRc); + viodasd_end_request(req,0); + } + else + { + if (nsect != req->current_nr_sectors) + { + err_printk("Yikes...non bh i/o # sect doesn't match!!!\n"); + } + viodasd_end_request(req, 1); + } + } + else { + while ((nsect > 0) && (req->bh)) + { + nsect -= req->current_nr_sectors; + viodasd_end_request(req, 1); + } + if (nsect) + { + err_printk("Yikes...sectors left over on a request!!!\n"); + } + + /* If the original request could not handle all the buffers, re-send + * the request + */ + if (req->bh) + { + if (send_request(req) == 0) + { + list_add_tail(&req->queue, &reqlist); + } + else + { + viodasd_end_request(req, 0); + } + } + + } + + /* Finally, send more requests */ + do_viodasd_request(NULL); + + spin_unlock_irqrestore(&io_request_lock, flags); + break; + + case vioblockflush: + up((void*)(u32)event->xCorrelationToken); + break; + + case vioblockcheck: + pwe = (struct viodasd_waitevent *)(u32)event->xCorrelationToken; + pwe->rc = event->xRc; + pwe->changed = bevent->u.check.changed; + up(pwe->sem); + break; + + default: + err_printk("invalid subtype!"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +/*************************************************************************** + * This routine tries to clean up anything we allocated/registered + ***************************************************************************/ +static void cleanup2(void) +{ + int i; + +#define CLEANIT(x) if (x) {kfree(x); x=NULL;} + + for (i=0; inr & ~(sizeof(unsigned long) * 8); + int cd_index = cdi->nr / (sizeof(unsigned long) * 8); + + clear_bit(bit_nr, &cdrom_numbers[cd_index]); +} + + /* This macro makes sure we don't have to check on cdrom_device_ops * existence in the run-time routines below. Change_capability is a * hack to have the capability flags defined const, while we can still * change it here without gcc complaining at every line. */ #define ENSURE(call, bits) if (cdo->call == NULL) *change_capability &= ~(bits) - +static int cdrom_init(void); int register_cdrom(struct cdrom_device_info *cdi) { static char banner_printed; @@ -354,7 +389,6 @@ struct cdrom_device_ops *cdo = cdi->ops; int *change_capability = (int *)&cdo->capability; /* hack */ char vname[16]; - static unsigned int cdrom_counter; cdinfo(CD_OPEN, "entering register_cdrom\n"); @@ -363,11 +397,9 @@ if (cdo->open == NULL || cdo->release == NULL) return -2; if ( !banner_printed ) { - printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n"); banner_printed = 1; -#ifdef CONFIG_SYSCTL - cdrom_sysctl_register(); -#endif /* CONFIG_SYSCTL */ + printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n"); + cdrom_init(); } ENSURE(drive_status, CDC_DRIVE_STATUS ); ENSURE(media_changed, CDC_MEDIA_CHANGED); @@ -395,7 +427,17 @@ if (!devfs_handle) devfs_handle = devfs_mk_dir (NULL, "cdroms", NULL); - sprintf (vname, "cdrom%u", cdrom_counter++); + + /* + * get new cdrom number + */ + down(&cdrom_sem); + cdi->nr = cdrom_get_entry(); + up(&cdrom_sem); + if (cdi->nr == -1) + return -ENOMEM; + + sprintf(vname, "cdrom%u", cdi->nr); if (cdi->de) { int pos; devfs_handle_t slave; @@ -418,9 +460,13 @@ S_IFBLK | S_IRUGO | S_IWUGO, &cdrom_fops, NULL); } - cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); + + down(&cdrom_sem); cdi->next = topCdromPtr; topCdromPtr = cdi; + up(&cdrom_sem); + + cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); return 0; } #undef ENSURE @@ -435,6 +481,7 @@ if (major < 0 || major >= MAX_BLKDEV) return -1; + down(&cdrom_sem); prev = NULL; cdi = topCdromPtr; while (cdi != NULL && cdi->dev != unreg->dev) { @@ -442,14 +489,20 @@ cdi = cdi->next; } - if (cdi == NULL) + if (cdi == NULL) { + up(&cdrom_sem); return -2; + } + + cdrom_clear_entry(cdi); + if (prev) prev->next = cdi->next; else topCdromPtr = cdi->next; + up(&cdrom_sem); cdi->ops->n_minors--; - devfs_unregister (cdi->de); + devfs_unregister(cdi->de); cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name); return 0; } @@ -458,10 +511,14 @@ { struct cdrom_device_info *cdi; + down(&cdrom_sem); + cdi = topCdromPtr; while (cdi != NULL && cdi->dev != dev) cdi = cdi->next; + up(&cdrom_sem); + return cdi; } @@ -1140,14 +1197,23 @@ memset(&rpc_state, 0, sizeof(rpc_state_t)); cgc.buffer = (char *) &rpc_state; - if ((ret = cdo->generic_packet(cdi, &cgc))) - return ret; - - ai->lrpcs.type = rpc_state.type_code; - ai->lrpcs.vra = rpc_state.vra; - ai->lrpcs.ucca = rpc_state.ucca; - ai->lrpcs.region_mask = rpc_state.region_mask; - ai->lrpcs.rpc_scheme = rpc_state.rpc_scheme; + /* + * we already checked dvd capability, so if it fails + * assume it's a rpc phase-1 drive and report that back. + * this is necessary at least on some toshiba drives, + * that don't support REPORT_KEY with key format 001000b + */ + cgc.quiet = 1; + if ((ret = cdo->generic_packet(cdi, &cgc))) { + ai->lrpcs.type = 0; + ai->lrpcs.rpc_scheme = 0; + } else { + ai->lrpcs.type = rpc_state.type_code; + ai->lrpcs.vra = rpc_state.vra; + ai->lrpcs.ucca = rpc_state.ucca; + ai->lrpcs.region_mask = rpc_state.region_mask; + ai->lrpcs.rpc_scheme = rpc_state.rpc_scheme; + } break; /* Set region settings */ @@ -1897,8 +1963,10 @@ if (cgc->data_direction == CGC_DATA_UNKNOWN) return -EINVAL; + if (cgc->data_direction == CGC_DATA_NONE && cgc->buflen) + return -EINVAL; - if (cgc->buflen < 0 || cgc->buflen >= 131072) + if (cgc->buflen >= 131072) return -EINVAL; if ((ubuf = cgc->buffer)) { @@ -1909,24 +1977,24 @@ usense = cgc->sense; cgc->sense = &sense; + ret = -EFAULT; if (usense && !access_ok(VERIFY_WRITE, usense, sizeof(*usense))) - return -EFAULT; + goto out; - if (cgc->data_direction == CGC_DATA_READ) { + if (cgc->data_direction == CGC_DATA_READ) if (!access_ok(VERIFY_READ, ubuf, cgc->buflen)) - return -EFAULT; - } else if (cgc->data_direction == CGC_DATA_WRITE) { - if (copy_from_user(cgc->buffer, ubuf, cgc->buflen)) { - kfree(cgc->buffer); - return -EFAULT; - } - } + goto out; + else if (cgc->data_direction == CGC_DATA_WRITE) + if (copy_from_user(cgc->buffer, ubuf, cgc->buflen)) + goto out; ret = cdi->ops->generic_packet(cdi, cgc); __copy_to_user(usense, cgc->sense, sizeof(*usense)); if (!ret && cgc->data_direction == CGC_DATA_READ) __copy_to_user(ubuf, cgc->buffer, cgc->buflen); - kfree(cgc->buffer); +out: + if (cgc->buffer) + kfree(cgc->buffer); return ret; } @@ -1936,7 +2004,7 @@ struct cdrom_device_ops *cdo = cdi->ops; struct cdrom_generic_command cgc; kdev_t dev = cdi->dev; - char buffer[32]; + unsigned char buffer[32]; int ret = 0; memset(&cgc, 0, sizeof(cgc)); @@ -2094,7 +2162,7 @@ case CDROMVOLCTRL: case CDROMVOLREAD: { struct cdrom_volctrl volctrl; - char mask[32]; + unsigned char mask[32]; unsigned short offset; cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n"); @@ -2205,19 +2273,19 @@ return cdrom_do_cmd(cdi, &cgc); } case CDROM_NEXT_WRITABLE: { - long next = 0; + unsigned int next = 0; cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n"); if ((ret = cdrom_get_next_writable(dev, &next))) return ret; - IOCTL_OUT(arg, long, next); + IOCTL_OUT(arg, unsigned int, next); return 0; } case CDROM_LAST_WRITTEN: { - long last = 0; + unsigned int last = 0; cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n"); if ((ret = cdrom_get_last_written(dev, &last))) return ret; - IOCTL_OUT(arg, long, last); + IOCTL_OUT(arg, unsigned int, last); return 0; } } /* switch */ @@ -2282,7 +2350,7 @@ /* return the last written block on the CD-R media. this is for the udf file system. */ -int cdrom_get_last_written(kdev_t dev, long *last_written) +int cdrom_get_last_written(kdev_t dev, unsigned int *last_written) { struct cdrom_device_info *cdi = cdrom_find_device(dev); struct cdrom_tocentry toc; @@ -2334,7 +2402,7 @@ } /* return the next writable block. also for udf file system. */ -int cdrom_get_next_writable(kdev_t dev, long *next_writable) +int cdrom_get_next_writable(kdev_t dev, unsigned int *next_writable) { struct cdrom_device_info *cdi = cdrom_find_device(dev); disc_information di; @@ -2418,6 +2486,8 @@ } pos = sprintf(info, "CD-ROM information, " VERSION "\n"); + + down(&cdrom_sem); pos += sprintf(info+pos, "\ndrive name:\t"); for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) @@ -2487,6 +2557,8 @@ for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD_RAM) != 0); + up(&cdrom_sem); + strcpy(info+pos,"\n\n"); return proc_dostring(ctl, write, filp, buffer, lenp); @@ -2632,8 +2704,12 @@ #endif /* CONFIG_SYSCTL */ -static int __init cdrom_init(void) +static int cdrom_init(void) { + int n_entries = CDROM_MAX_CDROMS / (sizeof(unsigned long) * 8); + + cdrom_numbers = kmalloc(n_entries * sizeof(unsigned long), GFP_KERNEL); + #ifdef CONFIG_SYSCTL cdrom_sysctl_register(); #endif @@ -2644,11 +2720,11 @@ static void __exit cdrom_exit(void) { printk(KERN_INFO "Uniform CD-ROM driver unloaded\n"); + kfree(cdrom_numbers); #ifdef CONFIG_SYSCTL cdrom_sysctl_unregister(); #endif devfs_unregister(devfs_handle); } -module_init(cdrom_init); module_exit(cdrom_exit); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/cdrom/viocd.c linux.ac/drivers/cdrom/viocd.c --- linux.vanilla/drivers/cdrom/viocd.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/cdrom/viocd.c Sat May 26 00:24:53 2001 @@ -0,0 +1,650 @@ +/* + * drivers/cdrom/viocd.c + * + *************************************************************************** + * iSeries Virtual CD Rom + * + * Author: Dave Boutcher + * (C) Copyright 2000 IBM Corporation + * + * 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) anyu later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *************************************************************************** + * This routine provides access to CD ROM drives owned and managed by an + * OS/400 partition running on the same box as this Linux partition. + * + * All operations are performed by sending messages back and forth to + * the OS/400 partition. The format of the messages is defined in + * include/asm-ppc/iSeries/vio.h + * + * This device driver can either use it's own major number, or it can + * pretend to be an AZTECH drive. This is controlled with a + * CONFIG option. You can either call this an elegant solution to the + * fact that a lot of software doesn't recognize a new CD major number... + * or you can call this a really ugly hack. Your choice. + * + */ +#include +#include + +/*************************************************************************** + * Decide if we are using our own major or pretending to be an AZTECH drive + ***************************************************************************/ +#ifdef CONFIG_VIOCD_AZTECH +#define MAJOR_NR AZTECH_CDROM_MAJOR +#define do_viocd_request do_aztcd_request +#else +#define MAJOR_NR VIOCD_MAJOR +#endif + +#define VIOCD_VERS "1.02" + +#include +#include +#include +#include +#include +#include +#include + +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPEVENT_H +#include +#endif +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif +#ifndef _ISERIES_PROC_H +#include +#endif + +/*************************************************************************** + * Our internal printk macro + ***************************************************************************/ +#define err_printk(fmt, args...) \ +printk(KERN_ERR "(%s:%3.3d) ERR: " fmt, __FILE__, __LINE__ , ## args) + +/*************************************************************************** + * Should probably make this a module parameter....sigh + ***************************************************************************/ +#define VIOCD_MAX_CD 8 +int viocd_blocksizes[VIOCD_MAX_CD]; + +/*************************************************************************** + * This is the structure we use to exchange info between driver and interrupt + * handler + ***************************************************************************/ +struct viocd_waitevent { + struct semaphore *sem; + int rc; + int changed; +}; + +/*************************************************************************** + * These are our internal structures for keeping track of devices + ***************************************************************************/ +static int viocd_numdev; +static struct { + char rsrcname[10]; + char type[4]; + char model[3]; +} viocd_unitinfo[VIOCD_MAX_CD]; + +static struct { + u32 useCount; + u32 blocksize; + u32 mediasize; +} viocd_diskinfo[VIOCD_MAX_CD]; + +static struct cdrom_device_info viocd_info[VIOCD_MAX_CD]; + +/*************************************************************************** + *************************************************************************** + * CODE STARTS HERE + *************************************************************************** + ***************************************************************************/ + +/*************************************************************************** + * Get info on CD devices from OS/400 + ***************************************************************************/ +static void get_viocd_info(void) +{ + dma_addr_t dmaaddr; + HvLpEvent_Rc hvrc; + int i; + DECLARE_MUTEX_LOCKED(Semaphore); + struct viocd_waitevent we; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return; + + memset(viocd_unitinfo, 0x00, sizeof(viocd_unitinfo)); + + dmaaddr = pci_map_single(NULL, &viocd_unitinfo, + sizeof(viocd_unitinfo), + PCI_DMA_FROMDEVICE); + if (dmaaddr == 0xFFFFFFFF) + { + err_printk("error allocating tce\n"); + return; + } + + we.sem = &Semaphore; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdgetinfo, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&we, + VIOVERSION << 16, + dmaaddr, + 0, + sizeof(viocd_unitinfo), + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + err_printk("hv error on op %d\n",(int)hvrc); + return; + } + + down(&Semaphore); + + if (we.rc) + { + err_printk("bad rc %d on getinfo\n",we.rc); + return; + } + + + for (i=0; + ((i < VIOCD_MAX_CD) && (viocd_unitinfo[i].rsrcname[0])); + i++) + { + viocd_numdev++; + } +} + +/*************************************************************************** + * Open a device + ***************************************************************************/ +static int viocd_open(struct cdrom_device_info * cdi, int purpose) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + int device_no = MINOR(cdi->dev); + HvLpEvent_Rc hvrc; + struct viocd_waitevent we; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return -ENODEV; + + if (device_no >= viocd_numdev) + return -ENODEV; + + we.sem = &Semaphore; + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + down(&Semaphore); + + if (we.rc) + return -EIO; + + if (viocd_diskinfo[device_no].useCount == 0) + { + if (viocd_diskinfo[device_no].blocksize > 0) + { + viocd_blocksizes[device_no] = viocd_diskinfo[device_no].blocksize; + } + } + return 0; +} + +/*************************************************************************** + * Release a device + ***************************************************************************/ +static void viocd_release(struct cdrom_device_info * cdi) +{ + int device_no = MINOR(cdi->dev); + HvLpEvent_Rc hvrc; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return; + + if (device_no >= viocd_numdev) + return; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdclose, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return; + } +} + +/*************************************************************************** + * Do a request + ***************************************************************************/ +static int rwreq; +static void do_viocd_request(request_queue_t *q) +{ + int device_no; + long start; + long len; + dma_addr_t dmaaddr; + HvLpEvent_Rc hvrc; + for (;;) { + if (rwreq > 0) return; + + INIT_REQUEST; + + if (CURRENT->cmd != READ) + { + err_printk("Invalid write request for CD\n"); + end_request(0); + continue; + } + + device_no = CURRENT_DEV; + if (device_no > viocd_numdev) + { + err_printk("Invalid device # %d\n",CURRENT_DEV); + end_request(0); + continue; + } + + if (rwreq > 0) + { + err_printk("multiple rw req!\n"); + end_request(0); + continue; + } + + start = CURRENT->sector * 512; + len = CURRENT->current_nr_sectors * 512; + + dmaaddr = pci_map_single(NULL, CURRENT->buffer, + len, + PCI_DMA_FROMDEVICE); + if (dmaaddr == 0xFFFFFFFF) + { + err_printk("error allocating tce for address %p len %ld\n", + CURRENT->buffer, + len); + end_request(0); + continue; + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdread, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, + ((u64)device_no << 48) | + dmaaddr, + start, + len, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + err_printk("hv error on op %d\n",(int)hvrc); + end_request(0); + } + else + { + rwreq++; + return; + } + } +} + +/*************************************************************************** + * Check if the CD changed + ***************************************************************************/ +static int viocd_media_changed(struct cdrom_device_info * cdi, int disc_nr) +{ + struct viocd_waitevent we; + HvLpEvent_Rc hvrc; + int device_no = MINOR(cdi->dev); + + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) + { + err_printk("Invalid hosting partition\n"); + return -EIO; + } + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdcheck, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code. If bad, assume no change */ + if (we.rc != 0) + { + err_printk("bad rc on check_change. Assuming no change\n"); + return 0; + } + + return we.changed; +} + +/*************************************************************************** + * This routine handles incoming CD LP events + ***************************************************************************/ +static void vioHandleCDEvent(struct HvLpEvent *event) +{ + struct viocdlpevent *bevent = (struct viocdlpevent *)event; + struct viocd_waitevent *pwe; + + if (event == NULL) + { + /* Notification that a partition went away! */ + return; + } + + // First, we should NEVER get an int here...only acks + if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + err_printk("Yikes! got an int in viocd event handler!\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + + switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) + { + case viocdgetinfo: + case viocdopen: + pwe = (struct viocd_waitevent *)(u32)event->xCorrelationToken; + pwe->rc = event->xRc; + viocd_diskinfo[bevent->mDisk].blocksize = bevent->mBlockSize; + viocd_diskinfo[bevent->mDisk].mediasize = bevent->mMediaSize; + + up(pwe->sem); + break; + + case viocdclose: + break; + + case viocdread: + rwreq--; + pci_unmap_single(NULL, + bevent->mToken, + bevent->mLen, + PCI_DMA_FROMDEVICE); + + if (event->xRc == HvLpEvent_Rc_Good) + { + end_request(1); + } + else + { + err_printk("rw error %d:%d\n",event->xRc,bevent->mSubTypeRc); + end_request(0); + } + do_viocd_request(NULL); + break; + + case viocdcheck: + pwe = (struct viocd_waitevent *)(u32)event->xCorrelationToken; + pwe->rc = event->xRc; + pwe->changed = bevent->mFlags; + up(pwe->sem); + break; + + default: + err_printk("invalid subtype!"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +/*************************************************************************** + * Our file operations table + ***************************************************************************/ +static struct cdrom_device_ops viocd_dops = { + open: viocd_open, + release: viocd_release, + media_changed: viocd_media_changed, + capability: CDC_CD_R +}; + +/*************************************************************************** + * Handle reads from the proc file system + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + +#if defined(MODULE) + len += sprintf(buf+len,"viocd Module opened %d times. Major number %d\n", + MOD_IN_USE, + MAJOR_NR); +#endif + *eof = 1; + return len; +} + +/*************************************************************************** + * Handle writes to our proc file system + ***************************************************************************/ +static int proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + printk("viocd: in proc_write, got %ld bytes starting with %c\n", + count, buffer[0]); + return count; +} + + +/*************************************************************************** + * setup our proc file system entries + ***************************************************************************/ +void viocd_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = create_proc_entry("viocd", S_IFREG|S_IRUSR, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/*************************************************************************** + * clean up our proc file system entries + ***************************************************************************/ +void viocd_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viocd", iSeries_proc); +} + +/*************************************************************************** + * Initialize the whole device driver. Handle module and non-module + * versions + ***************************************************************************/ +__init int viocd_init(void) +{ + int i; + int rc; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return -ENODEV; + + rc = viopath_open(viopath_hostLp, viomajorsubtype_cdio); + if (rc) + { + err_printk("error opening path to host partition %d\n",viopath_hostLp); + return rc; + } + + /* + * Initialize our request handler + */ + rwreq = 0; + vio_setCDHandler(vioHandleCDEvent); + + memset(viocd_unitinfo, 0x00, sizeof(viocd_unitinfo)); + memset(viocd_diskinfo, 0x00, sizeof(viocd_diskinfo)); + + get_viocd_info(); + + if (viocd_numdev == 0) + { + viopath_close(viopath_hostLp, viomajorsubtype_cdio); + return 0; + } + + printk("%s: iSeries Virtual CD vers %s, major %d, max disks %d, hosting partition %d\n", + DEVICE_NAME,VIOCD_VERS,MAJOR_NR,VIOCD_MAX_CD,viopath_hostLp); + + if (devfs_register_blkdev(MAJOR_NR, "viocd", &cdrom_fops) != 0) + { + printk("Unable to get major %d for viocd CD-ROM\n", + MAJOR_NR); + return -EIO; + } + + blksize_size[MAJOR_NR] = viocd_blocksizes; + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); + read_ahead[MAJOR_NR] = 4; + + memset(&viocd_info,0x00,sizeof(viocd_info)); + for (i=0; i #include #include +#include #include #include #include @@ -627,6 +628,15 @@ return 0; } +static int agp_generic_suspend(void) +{ + return 0; +} + +static void agp_generic_resume(void) +{ +} + static int agp_generic_free_gatt_table(void) { int page_order; @@ -775,28 +785,31 @@ static unsigned long agp_generic_alloc_page(void) { - void *pt; - - pt = (void *) __get_free_page(GFP_KERNEL); - if (pt == NULL) { + struct page * page; + + page = alloc_page(GFP_KERNEL); + if (page == NULL) { return 0; } - atomic_inc(&virt_to_page(pt)->count); - set_bit(PG_locked, &virt_to_page(pt)->flags); + atomic_inc(&page->count); + set_bit(PG_locked, &page->flags); atomic_inc(&agp_bridge.current_memory_agp); - return (unsigned long) pt; + return (unsigned long)page_address(page); } -static void agp_generic_destroy_page(unsigned long page) +static void agp_generic_destroy_page(unsigned long addr) { - void *pt = (void *) page; + void *pt = (void *) addr; + struct page *page; if (pt == NULL) { return; } - atomic_dec(&virt_to_page(pt)->count); - clear_bit(PG_locked, &virt_to_page(pt)->flags); - wake_up(&virt_to_page(pt)->wait); + + page = virt_to_page(pt); + atomic_dec(&page->count); + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); free_page((unsigned long) pt); atomic_dec(&agp_bridge.current_memory_agp); } @@ -1083,6 +1096,8 @@ agp_bridge.free_by_type = intel_i810_free_by_type; agp_bridge.agp_alloc_page = agp_generic_alloc_page; agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; return 0; } @@ -1233,6 +1248,10 @@ return addr | agp_bridge.masks[0].mask; } +static void intel_resume(void) +{ + intel_configure(); +} /* Setup function */ static gatt_mask intel_generic_masks[] = @@ -1275,6 +1294,8 @@ agp_bridge.free_by_type = agp_generic_free_by_type; agp_bridge.agp_alloc_page = agp_generic_alloc_page; agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = intel_resume; return 0; @@ -1305,6 +1326,8 @@ agp_bridge.free_by_type = agp_generic_free_by_type; agp_bridge.agp_alloc_page = agp_generic_alloc_page; agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; return 0; @@ -1335,6 +1358,8 @@ agp_bridge.free_by_type = agp_generic_free_by_type; agp_bridge.agp_alloc_page = agp_generic_alloc_page; agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; return 0; @@ -1452,6 +1477,8 @@ agp_bridge.free_by_type = agp_generic_free_by_type; agp_bridge.agp_alloc_page = agp_generic_alloc_page; agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; return 0; @@ -1563,6 +1590,8 @@ agp_bridge.free_by_type = agp_generic_free_by_type; agp_bridge.agp_alloc_page = agp_generic_alloc_page; agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; return 0; } @@ -1938,6 +1967,8 @@ agp_bridge.free_by_type = agp_generic_free_by_type; agp_bridge.agp_alloc_page = agp_generic_alloc_page; agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; return 0; @@ -2092,15 +2123,15 @@ static unsigned long ali_alloc_page(void) { - void *pt; + struct page *page; u32 temp; - pt = (void *) __get_free_page(GFP_KERNEL); - if (pt == NULL) + page = alloc_page(GFP_KERNEL); + if (page == NULL) return 0; - atomic_inc(&virt_to_page(pt)->count); - set_bit(PG_locked, &virt_to_page(pt)->flags); + atomic_inc(&page->count); + set_bit(PG_locked, &page->flags); atomic_inc(&agp_bridge.current_memory_agp); global_cache_flush(); @@ -2109,16 +2140,17 @@ pci_read_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, &temp); pci_write_config_dword(agp_bridge.dev, ALI_CACHE_FLUSH_CTRL, (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | - virt_to_phys((void *)pt)) | + virt_to_phys(page_address(page))) | ALI_CACHE_FLUSH_EN )); } - return (unsigned long) pt; + return (unsigned long)page_address(page); } -static void ali_destroy_page(unsigned long page) +static void ali_destroy_page(unsigned long addr) { u32 temp; - void *pt = (void *) page; + void *pt = (void *) addr; + struct page *page; if (pt == NULL) return; @@ -2133,9 +2165,10 @@ ALI_CACHE_FLUSH_EN)); } - atomic_dec(&virt_to_page(pt)->count); - clear_bit(PG_locked, &virt_to_page(pt)->flags); - wake_up(&virt_to_page(pt)->wait); + page = virt_to_page(pt); + atomic_dec(&page->count); + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); free_page((unsigned long) pt); atomic_dec(&agp_bridge.current_memory_agp); } @@ -2181,6 +2214,8 @@ agp_bridge.free_by_type = agp_generic_free_by_type; agp_bridge.agp_alloc_page = ali_alloc_page; agp_bridge.agp_destroy_page = ali_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; return 0; @@ -2312,6 +2347,7 @@ "Intel", "Generic", intel_generic_setup }, + #endif /* CONFIG_AGP_INTEL */ #ifdef CONFIG_AGP_SIS @@ -2759,6 +2795,19 @@ } } +static int agp_power(struct pm_dev *dev, pm_request_t rq, void *data) +{ + switch(rq) + { + case PM_SUSPEND: + return agp_bridge.suspend(); + case PM_RESUME: + agp_bridge.resume(); + return 0; + } + return 0; +} + extern int agp_frontend_initialize(void); extern void agp_frontend_cleanup(void); @@ -2793,11 +2842,14 @@ } inter_module_register("drm_agp", THIS_MODULE, &drm_agp); + + pm_register(PM_PCI_DEV, PM_PCI_ID(agp_bridge.dev), agp_power); return 0; } static void __exit agp_cleanup(void) { + pm_unregister_all(agp_power); agp_frontend_cleanup(); agp_backend_cleanup(); inter_module_unregister("drm_agp"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/agp/agpgart_fe.c linux.ac/drivers/char/agp/agpgart_fe.c --- linux.vanilla/drivers/char/agp/agpgart_fe.c Sat May 26 16:53:02 2001 +++ linux.ac/drivers/char/agp/agpgart_fe.c Thu May 17 14:04:56 2001 @@ -875,6 +875,9 @@ } else { agp_segment *segment; + if (reserve.seg_count >= 16384) + return -EINVAL; + segment = kmalloc((sizeof(agp_segment) * reserve.seg_count), GFP_KERNEL); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/amiserial.c linux.ac/drivers/char/amiserial.c --- linux.vanilla/drivers/char/amiserial.c Sat Feb 17 00:02:35 2001 +++ linux.ac/drivers/char/amiserial.c Tue Apr 3 17:54:38 2001 @@ -2288,7 +2288,7 @@ * Print a string to the serial port trying not to disturb * any possible real use of the port... * - * The console_lock must be held when we get here. + * The console must be locked when we get here. */ static void serial_console_write(struct console *co, const char *s, unsigned count) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/applicom.c linux.ac/drivers/char/applicom.c --- linux.vanilla/drivers/char/applicom.c Fri Feb 9 19:30:22 2001 +++ linux.ac/drivers/char/applicom.c Tue May 22 17:23:58 2001 @@ -1,6 +1,6 @@ /* Derived from Applicom driver ac.c for SCO Unix */ /* Ported by David Woodhouse, Axiom (Cambridge) Ltd. */ -/* Dave@mvhi.com 30/8/98 */ +/* dwmw2@redhat.com 30/8/98 */ /* $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $ */ /* This module is for Linux 2.1 and 2.2 series kernels. */ /*****************************************************************************/ @@ -82,7 +82,7 @@ MODULE_SUPPORTED_DEVICE("ac"); -struct applicom_board { +static struct applicom_board { unsigned long PhysIO; unsigned long RamIO; wait_queue_head_t FlagSleepSend; @@ -107,7 +107,7 @@ unsigned long); static void ac_interrupt(int, void *, struct pt_regs *); -struct file_operations ac_fops = { +static struct file_operations ac_fops = { owner:THIS_MODULE, llseek:ac_llseek, read:ac_read, @@ -115,7 +115,7 @@ ioctl:ac_ioctl, }; -struct miscdevice ac_miscdev = { +static struct miscdevice ac_miscdev = { AC_MINOR, "ac", &ac_fops @@ -123,7 +123,7 @@ static int dummy; /* dev_id for request_irq() */ -int ac_register_board(unsigned long physloc, unsigned long loc, +static int ac_register_board(unsigned long physloc, unsigned long loc, unsigned char boardno) { volatile unsigned char byte_reset_it; @@ -254,6 +254,7 @@ /* Now try the specified ISA cards */ +#warning "LEAK" RamIO = ioremap(mem, LEN_RAM_IO * MAX_ISA_BOARD); if (!RamIO) @@ -421,7 +422,7 @@ } /* Place ourselves on the wait queue */ - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait); /* Check whether the card is ready for us */ @@ -435,8 +436,9 @@ remove_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait); return -EINTR; - } + } spin_lock_irqsave(&apbs[IndexCard].mutex, flags); + set_current_state(TASK_INTERRUPTIBLE); } /* We may not have actually slept */ @@ -521,7 +523,7 @@ printk("\n"); #endif - /* Je suis stupide. DW. */ +#warning "Je suis stupide. DW. - copy*user in cli" if (copy_to_user(buf, &st_loc, sizeof(struct st_ram_io))) return -EFAULT; @@ -550,7 +552,7 @@ while(1) { /* Stick ourself on the wait queue */ - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&FlagSleepRec, &wait); /* Scan each board, looking for one which has a packet for us */ @@ -565,7 +567,7 @@ /* Got a packet for us */ ret = do_ac_read(i, buf); spin_unlock_irqrestore(&apbs[i].mutex, flags); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&FlagSleepRec, &wait); return tmp; } @@ -575,7 +577,7 @@ Dummy = readb(apbs[i].RamIO + VERS); spin_unlock_irqrestore(&apbs[i].mutex, flags); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&FlagSleepRec, &wait); printk(KERN_WARNING "APPLICOM driver read error board %d, DataToPcReady = %d\n", diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/console.c linux.ac/drivers/char/console.c --- linux.vanilla/drivers/char/console.c Fri Feb 9 19:30:22 2001 +++ linux.ac/drivers/char/console.c Thu Apr 19 14:24:50 2001 @@ -69,6 +69,9 @@ * * Removed old-style timers, introduced console_timer, made timer * deletion SMP-safe. 17Jun00, Andrew Morton + * + * Removed console_lock, enabled interrupts across all console operations + * 13 March 2001, Andrew Morton */ #include @@ -103,8 +106,6 @@ #include #include -#include - #include "console_macros.h" @@ -151,6 +152,7 @@ static void set_cursor(int currcons); static void hide_cursor(int currcons); static void unblank_screen_t(unsigned long dummy); +static void console_callback(void *ignored); static int printable; /* Is console ready for printing? */ @@ -161,6 +163,10 @@ static int blankinterval = 10*60*HZ; static int vesa_off_interval; +static struct tq_struct console_callback_tq = { + routine: console_callback, +}; + /* * fg_console is the current virtual console, * last_console is the last used one, @@ -182,15 +188,13 @@ /* * Unfortunately, we need to delay tty echo when we're currently writing to the - * console since the code is (and always was) not re-entrant, so we insert - * all filp requests to con_task_queue instead of tq_timer and run it from - * the console_tasklet. The console_tasklet is protected by the IRQ - * protected console_lock. + * console since the code is (and always was) not re-entrant, so we schedule + * all flip requests to process context with schedule-task() and run it from + * console_callback(). */ -DECLARE_TASK_QUEUE(con_task_queue); /* - * For the same reason, we defer scrollback to the console tasklet. + * For the same reason, we defer scrollback to the console callback. */ static int scrollback_delta; @@ -234,7 +238,12 @@ static inline void scrolldelta(int lines) { scrollback_delta += lines; - tasklet_schedule(&console_tasklet); + schedule_console_callback(); +} + +void schedule_console_callback(void) +{ + schedule_task(&console_callback_tq); } static void scrup(int currcons, unsigned int t, unsigned int b, int nr) @@ -1028,6 +1037,7 @@ color = def_color; } +/* console_sem is held */ static void csi_m(int currcons) { int i; @@ -1167,6 +1177,7 @@ return report_mouse; } +/* console_sem is held */ static void set_mode(int currcons, int on_off) { int i; @@ -1232,6 +1243,7 @@ } } +/* console_sem is held */ static void setterm_command(int currcons) { switch(par[0]) { @@ -1286,19 +1298,7 @@ } } -static void insert_line(int currcons, unsigned int nr) -{ - scrdown(currcons,y,bottom,nr); - need_wrap = 0; -} - - -static void delete_line(int currcons, unsigned int nr) -{ - scrup(currcons,y,bottom,nr); - need_wrap = 0; -} - +/* console_sem is held */ static void csi_at(int currcons, unsigned int nr) { if (nr > video_num_columns - x) @@ -1308,15 +1308,18 @@ insert_char(currcons, nr); } +/* console_sem is held */ static void csi_L(int currcons, unsigned int nr) { if (nr > video_num_lines - y) nr = video_num_lines - y; else if (!nr) nr = 1; - insert_line(currcons, nr); + scrdown(currcons,y,bottom,nr); + need_wrap = 0; } +/* console_sem is held */ static void csi_P(int currcons, unsigned int nr) { if (nr > video_num_columns - x) @@ -1326,15 +1329,18 @@ delete_char(currcons, nr); } +/* console_sem is held */ static void csi_M(int currcons, unsigned int nr) { if (nr > video_num_lines - y) nr = video_num_lines - y; else if (!nr) nr=1; - delete_line(currcons, nr); + scrup(currcons,y,bottom,nr); + need_wrap = 0; } +/* console_sem is held (except via vc_init->reset_terminal */ static void save_cur(int currcons) { saved_x = x; @@ -1349,6 +1355,7 @@ saved_G1 = G1_charset; } +/* console_sem is held */ static void restore_cur(int currcons) { gotoxy(currcons,saved_x,saved_y); @@ -1369,6 +1376,7 @@ EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd, ESpalette }; +/* console_sem is held (except via vc_init()) */ static void reset_terminal(int currcons, int do_clear) { top = 0; @@ -1424,6 +1432,7 @@ csi_J(currcons,2); } +/* console_sem is held */ static void do_con_trol(struct tty_struct *tty, unsigned int currcons, int c) { /* @@ -1804,6 +1813,7 @@ #define CON_BUF_SIZE PAGE_SIZE DECLARE_MUTEX(con_buf_sem); +/* acquires console_sem */ static int do_con_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { @@ -1844,6 +1854,7 @@ again: if (count > CON_BUF_SIZE) count = CON_BUF_SIZE; + console_conditional_schedule(); if (copy_from_user(con_buf, buf, count)) { n = 0; /* ?? are error codes legal here ?? */ goto out; @@ -1859,7 +1870,7 @@ * the console spinlock during the entire write. */ - spin_lock_irq(&console_lock); + acquire_console_sem(); himask = hi_font_mask; charmask = himask ? 0x1ff : 0xff; @@ -1977,7 +1988,8 @@ do_con_trol(tty, currcons, c); } FLUSH - spin_unlock_irq(&console_lock); + console_conditional_schedule(); + release_console_sem(); out: if (from_user) { @@ -2001,23 +2013,17 @@ } /* - * This is the console switching tasklet. + * This is the console switching callback. * - * Doing console switching in a tasklet allows + * Doing console switching in a process context allows * us to do the switches asynchronously (needed when we want * to switch due to a keyboard interrupt). Synchronization * with other console code and prevention of re-entrancy is - * ensured with console_lock. + * ensured with console_sem. */ -static void console_softint(unsigned long ignored) +static void console_callback(void *ignored) { - /* Runs the task queue outside of the console lock. These - * callbacks can come back into the console code and thus - * will perform their own locking. - */ - run_task_queue(&con_task_queue); - - spin_lock_irq(&console_lock); + acquire_console_sem(); if (want_console >= 0) { if (want_console != fg_console && vc_cons_allocated(want_console)) { @@ -2041,7 +2047,13 @@ scrollback_delta = 0; } - spin_unlock_irq(&console_lock); + release_console_sem(); +} + +void set_console(int nr) +{ + want_console = nr; + schedule_console_callback(); } #ifdef CONFIG_VT_CONSOLE @@ -2049,7 +2061,7 @@ /* * Console on virtual terminal * - * The console_lock must be held when we get here. + * The console must be locked when we get here. */ void vt_console_print(struct console *co, const char * b, unsigned count) @@ -2136,6 +2148,9 @@ } set_cursor(currcons); + if (!oops_in_progress) + poke_blanked_console(); + quit: clear_bit(0, &printing); } @@ -2160,27 +2175,45 @@ * Handling of Linux-specific VC ioctls */ +/* + * Generally a bit racy with respect to console_sem(). + * + * There are some functions which don't need it. + * + * There are some functions which can sleep for arbitrary periods (paste_selection) + * but we don't need the lock there anyway. + * + * set_selection has locking, and definitely needs it + */ + int tioclinux(struct tty_struct *tty, unsigned long arg) { char type, data; + int ret; if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE) return -EINVAL; - if (current->tty != tty && !suser()) + if (current->tty != tty && !capable(CAP_SYS_ADMIN)) return -EPERM; if (get_user(type, (char *)arg)) return -EFAULT; + ret = 0; switch (type) { case 2: - return set_selection(arg, tty, 1); + acquire_console_sem(); + ret = set_selection(arg, tty, 1); + release_console_sem(); + break; case 3: - return paste_selection(tty); + ret = paste_selection(tty); + break; case 4: unblank_screen(); - return 0; + break; case 5: - return sel_loadlut(arg); + ret = sel_loadlut(arg); + break; case 6: /* @@ -2190,24 +2223,33 @@ * related to the kernel should not use this. */ data = shift_state; - return __put_user(data, (char *) arg); + ret = __put_user(data, (char *) arg); + break; case 7: data = mouse_reporting(); - return __put_user(data, (char *) arg); + ret = __put_user(data, (char *) arg); + break; case 10: set_vesa_blanking(arg); - return 0; + break;; case 11: /* set kmsg redirect */ - if (!suser()) - return -EPERM; - if (get_user(data, (char *)arg+1)) - return -EFAULT; - kmsg_redirect = data; - return 0; + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + } else { + if (get_user(data, (char *)arg+1)) + ret = -EFAULT; + else + kmsg_redirect = data; + } + break; case 12: /* get fg_console */ - return fg_console; + ret = fg_console; + break; + default: + ret = -EINVAL; + break; } - return -EINVAL; + return ret; } /* @@ -2228,6 +2270,8 @@ static void con_put_char(struct tty_struct *tty, unsigned char ch) { + if (in_interrupt()) + return; /* n_r3964 calls put_char() from interrupt context */ pm_access(pm_con); do_con_write(tty, 0, &ch, 1); } @@ -2292,13 +2336,15 @@ static void con_flush_chars(struct tty_struct *tty) { - unsigned long flags; struct vt_struct *vt = (struct vt_struct *)tty->driver_data; + if (in_interrupt()) /* from flush_to_ldisc */ + return; + pm_access(pm_con); - spin_lock_irqsave(&console_lock, flags); + acquire_console_sem(); set_cursor(vt->vc_num); - spin_unlock_irqrestore(&console_lock, flags); + release_console_sem(); } /* @@ -2369,8 +2415,6 @@ struct tty_driver console_driver; static int console_refcount; -DECLARE_TASKLET_DISABLED(console_tasklet, console_softint, 0); - void __init con_init(void) { const char *display_desc = NULL; @@ -2455,9 +2499,6 @@ #ifdef CONFIG_VT_CONSOLE register_console(&vt_console_driver); #endif - - tasklet_enable(&console_tasklet); - tasklet_schedule(&console_tasklet); } #ifndef VT_SINGLE_DRIVER @@ -2563,6 +2604,9 @@ console_driver.minor_start + i); } +/* + * This is called by a timer handler + */ static void vesa_powerdown(void) { struct vc_data *c = vc_cons[fg_console].d; @@ -2583,9 +2627,12 @@ } } +/* + * This is a timer handler + */ static void vesa_powerdown_screen(unsigned long dummy) { - console_timer.function = unblank_screen_t; /* I don't have a clue why this is necessary */ + console_timer.function = unblank_screen_t; vesa_powerdown(); } @@ -2644,11 +2691,17 @@ timer_do_blank_screen(entering_gfx, 0); } +/* + * This is a timer handler + */ static void unblank_screen_t(unsigned long dummy) { unblank_screen(); } +/* + * Called by timer as well as from vt_console_driver + */ void unblank_screen(void) { int currcons; @@ -2660,12 +2713,14 @@ printk("unblank_screen: tty %d not allocated ??\n", fg_console+1); return; } + currcons = fg_console; + if (vcmode != KD_TEXT) + return; /* but leave console_blanked != 0 */ console_timer.function = blank_screen; if (blankinterval) { mod_timer(&console_timer, jiffies + blankinterval); } - currcons = fg_console; console_blanked = 0; if (console_blank_hook) console_blank_hook(0); @@ -2676,6 +2731,9 @@ set_cursor(fg_console); } +/* + * This is both a user-level callable and a timer handler + */ static void blank_screen(unsigned long dummy) { timer_do_blank_screen(0, 1); @@ -2683,7 +2741,7 @@ void poke_blanked_console(void) { - del_timer(&console_timer); /* Can't use _sync here: called from tasklet */ + del_timer(&console_timer); if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) return; if (console_blanked) { @@ -2831,9 +2889,9 @@ op->data = temp; } - spin_lock_irq(&console_lock); + acquire_console_sem(); rc = sw->con_font_op(vc_cons[currcons].d, op); - spin_unlock_irq(&console_lock); + release_console_sem(); op->data = old_op.data; if (!rc && !set) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/cyclades.c linux.ac/drivers/char/cyclades.c --- linux.vanilla/drivers/char/cyclades.c Mon Apr 30 15:13:14 2001 +++ linux.ac/drivers/char/cyclades.c Wed May 9 11:51:36 2001 @@ -2983,10 +2983,11 @@ return 0; } - CY_LOCK(info, flags); if (from_user) { down(&tmp_buf_sem); while (1) { + int c1; + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) @@ -2999,23 +3000,30 @@ } break; } - c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + CY_LOCK(info, flags); + c1 = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); + + if (c1 < c) + c = c1; memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); info->xmit_head = ((info->xmit_head + c) & (SERIAL_XMIT_SIZE-1)); info->xmit_cnt += c; + CY_UNLOCK(info, flags); buf += c; count -= c; ret += c; } up(&tmp_buf_sem); } else { + CY_LOCK(info, flags); while (1) { c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0) { + + if (c <= 0) break; - } + memcpy(info->xmit_buf + info->xmit_head, buf, c); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); info->xmit_cnt += c; @@ -3023,8 +3031,8 @@ count -= c; ret += c; } + CY_UNLOCK(info, flags); } - CY_UNLOCK(info, flags); info->idle_stats.xmit_bytes += ret; info->idle_stats.xmit_idle = jiffies; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/agpsupport-mod.c linux.ac/drivers/char/drm/agpsupport-mod.c --- linux.vanilla/drivers/char/drm/agpsupport-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/agpsupport-mod.c Tue Apr 3 17:52:36 2001 @@ -0,0 +1,328 @@ +/* agpsupport.c -- DRM support for AGP/GART backend -*- linux-c -*- + * Created: Mon Dec 13 09:56:45 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include +#if LINUX_VERSION_CODE < 0x020400 +#include "agpsupport-pre24.h" +#else +#define DRM_AGP_GET (drm_agp_t *)inter_module_get("drm_agp") +#define DRM_AGP_PUT inter_module_put("drm_agp") +#endif + +static const drm_agp_t *drm_agp = NULL; + +int drm_agp_info(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + agp_kern_info *kern; + drm_agp_info_t info; + + if (!dev->agp->acquired || !drm_agp->copy_info) return -EINVAL; + + kern = &dev->agp->agp_info; + info.agp_version_major = kern->version.major; + info.agp_version_minor = kern->version.minor; + info.mode = kern->mode; + info.aperture_base = kern->aper_base; + info.aperture_size = kern->aper_size * 1024 * 1024; + info.memory_allowed = kern->max_memory << PAGE_SHIFT; + info.memory_used = kern->current_memory << PAGE_SHIFT; + info.id_vendor = kern->device->vendor; + info.id_device = kern->device->device; + + if (copy_to_user((drm_agp_info_t *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +int drm_agp_acquire(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode; + + if (dev->agp->acquired || !drm_agp->acquire) return -EINVAL; + if ((retcode = drm_agp->acquire())) return retcode; + dev->agp->acquired = 1; + return 0; +} + +int drm_agp_release(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + if (!dev->agp->acquired || !drm_agp->release) return -EINVAL; + drm_agp->release(); + dev->agp->acquired = 0; + return 0; + +} + +void _drm_agp_release(void) +{ + if (drm_agp->release) drm_agp->release(); +} + +int drm_agp_enable(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_mode_t mode; + + if (!dev->agp->acquired || !drm_agp->enable) return -EINVAL; + + if (copy_from_user(&mode, (drm_agp_mode_t *)arg, sizeof(mode))) + return -EFAULT; + + dev->agp->mode = mode.mode; + drm_agp->enable(mode.mode); + dev->agp->base = dev->agp->agp_info.aper_base; + dev->agp->enabled = 1; + return 0; +} + +int drm_agp_alloc(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_buffer_t request; + drm_agp_mem_t *entry; + agp_memory *memory; + unsigned long pages; + u32 type; + if (!dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = drm_alloc(sizeof(*entry), DRM_MEM_AGPLISTS))) + return -ENOMEM; + + memset(entry, 0, sizeof(*entry)); + + pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE; + type = (u32) request.type; + + if (!(memory = drm_alloc_agp(pages, type))) { + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return -ENOMEM; + } + + entry->handle = (unsigned long)memory->memory; + entry->memory = memory; + entry->bound = 0; + entry->pages = pages; + entry->prev = NULL; + entry->next = dev->agp->memory; + if (dev->agp->memory) dev->agp->memory->prev = entry; + dev->agp->memory = entry; + + request.handle = entry->handle; + request.physical = memory->physical; + + if (copy_to_user((drm_agp_buffer_t *)arg, &request, sizeof(request))) { + dev->agp->memory = entry->next; + dev->agp->memory->prev = NULL; + drm_free_agp(memory, pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return -EFAULT; + } + return 0; +} + +static drm_agp_mem_t *drm_agp_lookup_entry(drm_device_t *dev, + unsigned long handle) +{ + drm_agp_mem_t *entry; + + for (entry = dev->agp->memory; entry; entry = entry->next) { + if (entry->handle == handle) return entry; + } + return NULL; +} + +int drm_agp_unbind(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_binding_t request; + drm_agp_mem_t *entry; + + if (!dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = drm_agp_lookup_entry(dev, request.handle))) + return -EINVAL; + if (!entry->bound) return -EINVAL; + return drm_unbind_agp(entry->memory); +} + +int drm_agp_bind(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_binding_t request; + drm_agp_mem_t *entry; + int retcode; + int page; + + if (!dev->agp->acquired || !drm_agp->bind_memory) return -EINVAL; + if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = drm_agp_lookup_entry(dev, request.handle))) + return -EINVAL; + if (entry->bound) return -EINVAL; + page = (request.offset + PAGE_SIZE - 1) / PAGE_SIZE; + if ((retcode = drm_bind_agp(entry->memory, page))) return retcode; + entry->bound = dev->agp->base + (page << PAGE_SHIFT); + DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n", + dev->agp->base, entry->bound); + return 0; +} + +int drm_agp_free(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_buffer_t request; + drm_agp_mem_t *entry; + + if (!dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = drm_agp_lookup_entry(dev, request.handle))) + return -EINVAL; + if (entry->bound) drm_unbind_agp(entry->memory); + + if (entry->prev) entry->prev->next = entry->next; + else dev->agp->memory = entry->next; + if (entry->next) entry->next->prev = entry->prev; + drm_free_agp(entry->memory, entry->pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return 0; +} + +drm_agp_head_t *drm_agp_init(void) +{ + drm_agp_head_t *head = NULL; + + drm_agp = DRM_AGP_GET; + if (drm_agp) { + if (!(head = drm_alloc(sizeof(*head), DRM_MEM_AGPLISTS))) + return NULL; + memset((void *)head, 0, sizeof(*head)); + drm_agp->copy_info(&head->agp_info); + if (head->agp_info.chipset == NOT_SUPPORTED) { + drm_free(head, sizeof(*head), DRM_MEM_AGPLISTS); + return NULL; + } + head->memory = NULL; + switch (head->agp_info.chipset) { + case INTEL_GENERIC: head->chipset = "Intel"; break; + case INTEL_LX: head->chipset = "Intel 440LX"; break; + case INTEL_BX: head->chipset = "Intel 440BX"; break; + case INTEL_GX: head->chipset = "Intel 440GX"; break; + case INTEL_I810: head->chipset = "Intel i810"; break; + +#if LINUX_VERSION_CODE >= 0x020400 + case INTEL_I840: head->chipset = "Intel i840"; break; +#endif + + case VIA_GENERIC: head->chipset = "VIA"; break; + case VIA_VP3: head->chipset = "VIA VP3"; break; + case VIA_MVP3: head->chipset = "VIA MVP3"; break; + +#if LINUX_VERSION_CODE >= 0x020400 + case VIA_MVP4: head->chipset = "VIA MVP4"; break; + case VIA_APOLLO_KX133: head->chipset = "VIA Apollo KX133"; + break; + case VIA_APOLLO_KT133: head->chipset = "VIA Apollo KT133"; + break; +#endif + + case VIA_APOLLO_PRO: head->chipset = "VIA Apollo Pro"; + break; + case SIS_GENERIC: head->chipset = "SiS"; break; + case AMD_GENERIC: head->chipset = "AMD"; break; + case AMD_IRONGATE: head->chipset = "AMD Irongate"; break; + case ALI_GENERIC: head->chipset = "ALi"; break; + case ALI_M1541: head->chipset = "ALi M1541"; break; + default: head->chipset = "Unknown"; break; + } + DRM_INFO("AGP %d.%d on %s @ 0x%08lx %ZuMB\n", + head->agp_info.version.major, + head->agp_info.version.minor, + head->chipset, + head->agp_info.aper_base, + head->agp_info.aper_size); + } + return head; +} + +void drm_agp_uninit(void) +{ + DRM_AGP_PUT; + drm_agp = NULL; +} + +agp_memory *drm_agp_allocate_memory(size_t pages, u32 type) +{ + if (!drm_agp->allocate_memory) return NULL; + return drm_agp->allocate_memory(pages, type); +} + +int drm_agp_free_memory(agp_memory *handle) +{ + if (!handle || !drm_agp->free_memory) return 0; + drm_agp->free_memory(handle); + return 1; +} + +int drm_agp_bind_memory(agp_memory *handle, off_t start) +{ + if (!handle || !drm_agp->bind_memory) return -EINVAL; + return drm_agp->bind_memory(handle, start); +} + +int drm_agp_unbind_memory(agp_memory *handle) +{ + if (!handle || !drm_agp->unbind_memory) return -EINVAL; + return drm_agp->unbind_memory(handle); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/auth-mod.c linux.ac/drivers/char/drm/auth-mod.c --- linux.vanilla/drivers/char/drm/auth-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/auth-mod.c Tue Apr 3 17:52:36 2001 @@ -0,0 +1,162 @@ +/* auth.c -- IOCTLs for authentication -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static int drm_hash_magic(drm_magic_t magic) +{ + return magic & (DRM_HASH_SIZE-1); +} + +static drm_file_t *drm_find_file(drm_device_t *dev, drm_magic_t magic) +{ + drm_file_t *retval = NULL; + drm_magic_entry_t *pt; + int hash = drm_hash_magic(magic); + + down(&dev->struct_sem); + for (pt = dev->magiclist[hash].head; pt; pt = pt->next) { + if (pt->magic == magic) { + retval = pt->priv; + break; + } + } + up(&dev->struct_sem); + return retval; +} + +int drm_add_magic(drm_device_t *dev, drm_file_t *priv, drm_magic_t magic) +{ + int hash; + drm_magic_entry_t *entry; + + DRM_DEBUG("%d\n", magic); + + hash = drm_hash_magic(magic); + entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC); + if (!entry) return -ENOMEM; + entry->magic = magic; + entry->priv = priv; + entry->next = NULL; + + down(&dev->struct_sem); + if (dev->magiclist[hash].tail) { + dev->magiclist[hash].tail->next = entry; + dev->magiclist[hash].tail = entry; + } else { + dev->magiclist[hash].head = entry; + dev->magiclist[hash].tail = entry; + } + up(&dev->struct_sem); + + return 0; +} + +int drm_remove_magic(drm_device_t *dev, drm_magic_t magic) +{ + drm_magic_entry_t *prev = NULL; + drm_magic_entry_t *pt; + int hash; + + DRM_DEBUG("%d\n", magic); + hash = drm_hash_magic(magic); + + down(&dev->struct_sem); + for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) { + if (pt->magic == magic) { + if (dev->magiclist[hash].head == pt) { + dev->magiclist[hash].head = pt->next; + } + if (dev->magiclist[hash].tail == pt) { + dev->magiclist[hash].tail = prev; + } + if (prev) { + prev->next = pt->next; + } + up(&dev->struct_sem); + return 0; + } + } + up(&dev->struct_sem); + + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + + return -EINVAL; +} + +int drm_getmagic(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + static drm_magic_t sequence = 0; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_auth_t auth; + + /* Find unique magic */ + if (priv->magic) { + auth.magic = priv->magic; + } else { + do { + spin_lock(&lock); + if (!sequence) ++sequence; /* reserve 0 */ + auth.magic = sequence++; + spin_unlock(&lock); + } while (drm_find_file(dev, auth.magic)); + priv->magic = auth.magic; + drm_add_magic(dev, priv, auth.magic); + } + + DRM_DEBUG("%u\n", auth.magic); + if (copy_to_user((drm_auth_t *)arg, &auth, sizeof(auth))) + return -EFAULT; + return 0; +} + +int drm_authmagic(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_auth_t auth; + drm_file_t *file; + + if (copy_from_user(&auth, (drm_auth_t *)arg, sizeof(auth))) + return -EFAULT; + DRM_DEBUG("%u\n", auth.magic); + if ((file = drm_find_file(dev, auth.magic))) { + file->authenticated = 1; + drm_remove_magic(dev, auth.magic); + return 0; + } + return -EINVAL; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/bufs-mod.c linux.ac/drivers/char/drm/bufs-mod.c --- linux.vanilla/drivers/char/drm/bufs-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/bufs-mod.c Tue Apr 3 17:54:38 2001 @@ -0,0 +1,537 @@ +/* bufs.c -- IOCTLs to manage buffers -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include +#include "drmP.h" +#include "linux/un.h" + + /* Compute order. Can be made faster. */ +int drm_order(unsigned long size) +{ + int order; + unsigned long tmp; + + for (order = 0, tmp = size; tmp >>= 1; ++order); + if (size & ~(1 << order)) ++order; + return order; +} + +int drm_addmap(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map; + + if (!(filp->f_mode & 3)) return -EACCES; /* Require read/write */ + + map = drm_alloc(sizeof(*map), DRM_MEM_MAPS); + if (!map) return -ENOMEM; + if (copy_from_user(map, (drm_map_t *)arg, sizeof(*map))) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EFAULT; + } + + DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n", + map->offset, map->size, map->type); + if ((map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK))) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + map->mtrr = -1; + map->handle = 0; + + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifndef __sparc__ + if (map->offset + map->size < map->offset + || map->offset < virt_to_phys(high_memory)) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } +#endif +#ifdef CONFIG_MTRR + if (map->type == _DRM_FRAME_BUFFER + || (map->flags & _DRM_WRITE_COMBINING)) { + map->mtrr = mtrr_add(map->offset, map->size, + MTRR_TYPE_WRCOMB, 1); + } +#endif + map->handle = drm_ioremap(map->offset, map->size); + break; + + + case _DRM_SHM: + map->handle = (void *)drm_alloc_pages(drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + DRM_DEBUG("%ld %d %p\n", map->size, drm_order(map->size), + map->handle); + if (!map->handle) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -ENOMEM; + } + map->offset = (unsigned long)map->handle; + if (map->flags & _DRM_CONTAINS_LOCK) { + dev->lock.hw_lock = map->handle; /* Pointer to lock */ + } + break; +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + case _DRM_AGP: + map->offset = map->offset + dev->agp->base; + break; +#endif + default: + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + + down(&dev->struct_sem); + if (dev->maplist) { + ++dev->map_count; + dev->maplist = drm_realloc(dev->maplist, + (dev->map_count-1) + * sizeof(*dev->maplist), + dev->map_count + * sizeof(*dev->maplist), + DRM_MEM_MAPS); + } else { + dev->map_count = 1; + dev->maplist = drm_alloc(dev->map_count*sizeof(*dev->maplist), + DRM_MEM_MAPS); + } + dev->maplist[dev->map_count-1] = map; + up(&dev->struct_sem); + + if (copy_to_user((drm_map_t *)arg, map, sizeof(*map))) + return -EFAULT; + if (map->type != _DRM_SHM) { + if (copy_to_user(&((drm_map_t *)arg)->handle, + &map->offset, + sizeof(map->offset))) + return -EFAULT; + } + return 0; +} + +int drm_addbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int count; + int order; + int size; + int total; + int page_order; + drm_buf_entry_t *entry; + unsigned long page; + drm_buf_t *buf; + int alignment; + unsigned long offset; + int i; + int byte_count; + int page_count; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + + DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n", + request.count, request.size, size, order, dev->queue_count); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->seglist = drm_alloc(count * sizeof(*entry->seglist), + DRM_MEM_SEGS); + if (!entry->seglist) { + drm_free(entry->buflist, + count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->seglist, 0, count * sizeof(*entry->seglist)); + + dma->pagelist = drm_realloc(dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + (dma->page_count + (count << page_order)) + * sizeof(*dma->pagelist), + DRM_MEM_PAGES); + DRM_DEBUG("pagelist: %d entries\n", + dma->page_count + (count << page_order)); + + + entry->buf_size = size; + entry->page_order = page_order; + byte_count = 0; + page_count = 0; + while (entry->buf_count < count) { + if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break; + entry->seglist[entry->seg_count++] = page; + for (i = 0; i < (1 << page_order); i++) { + DRM_DEBUG("page %d @ 0x%08lx\n", + dma->page_count + page_count, + page + PAGE_SIZE * i); + dma->pagelist[dma->page_count + page_count++] + = page + PAGE_SIZE * i; + } + for (offset = 0; + offset + size <= total && entry->buf_count < count; + offset += alignment, ++entry->buf_count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + byte_count + offset); + buf->address = (void *)(page + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + byte_count += PAGE_SIZE << page_order; + } + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + dma->seg_count += entry->seg_count; + dma->page_count += entry->seg_count << page_order; + dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); + + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + if (copy_to_user((drm_buf_desc_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + atomic_dec(&dev->buf_alloc); + return 0; +} + +int drm_infobufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_info_t request; + int i; + int count; + + if (!dma) return -EINVAL; + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + if (copy_from_user(&request, + (drm_buf_info_t *)arg, + sizeof(request))) + return -EFAULT; + + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) ++count; + } + + DRM_DEBUG("count = %d\n", count); + + if (request.count >= count) { + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) { + if (copy_to_user(&request.list[count].count, + &dma->bufs[i].buf_count, + sizeof(dma->bufs[0] + .buf_count)) || + copy_to_user(&request.list[count].size, + &dma->bufs[i].buf_size, + sizeof(dma->bufs[0].buf_size)) || + copy_to_user(&request.list[count].low_mark, + &dma->bufs[i] + .freelist.low_mark, + sizeof(dma->bufs[0] + .freelist.low_mark)) || + copy_to_user(&request.list[count] + .high_mark, + &dma->bufs[i] + .freelist.high_mark, + sizeof(dma->bufs[0] + .freelist.high_mark))) + return -EFAULT; + + DRM_DEBUG("%d %d %d %d %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].buf_size, + dma->bufs[i].freelist.low_mark, + dma->bufs[i].freelist.high_mark); + ++count; + } + } + } + request.count = count; + + if (copy_to_user((drm_buf_info_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + return 0; +} + +int drm_markbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int order; + drm_buf_entry_t *entry; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_desc_t *)arg, + sizeof(request))) + return -EFAULT; + + DRM_DEBUG("%d, %d, %d\n", + request.size, request.low_mark, request.high_mark); + order = drm_order(request.size); + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + entry = &dma->bufs[order]; + + if (request.low_mark < 0 || request.low_mark > entry->buf_count) + return -EINVAL; + if (request.high_mark < 0 || request.high_mark > entry->buf_count) + return -EINVAL; + + entry->freelist.low_mark = request.low_mark; + entry->freelist.high_mark = request.high_mark; + + return 0; +} + +int drm_freebufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_free_t request; + int i; + int idx; + drm_buf_t *buf; + + if (!dma) return -EINVAL; + + if (copy_from_user(&request, + (drm_buf_free_t *)arg, + sizeof(request))) + return -EFAULT; + + DRM_DEBUG("%d\n", request.count); + for (i = 0; i < request.count; i++) { + if (copy_from_user(&idx, + &request.list[i], + sizeof(idx))) + return -EFAULT; + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + idx, dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[idx]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d freeing buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + drm_free_buffer(dev, buf); + } + + return 0; +} + +int drm_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + unsigned long virtual; + unsigned long address; + drm_buf_map_t request; + int i; + + if (!dma) return -EINVAL; + + DRM_DEBUG("\n"); + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + if (copy_from_user(&request, + (drm_buf_map_t *)arg, + sizeof(request))) + return -EFAULT; + + if (request.count >= dma->buf_count) { + down_write(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, dma->byte_count, + PROT_READ|PROT_WRITE, MAP_SHARED, 0); + up_write(¤t->mm->mmap_sem); + if (virtual > -1024UL) { + /* Real error */ + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for (i = 0; i < dma->buf_count; i++) { + if (copy_to_user(&request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].used, + &zero, + sizeof(zero))) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; + if (copy_to_user(&request.list[i].address, + &address, + sizeof(address))) { + retcode = -EFAULT; + goto done; + } + } + } +done: + request.count = dma->buf_count; + DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode); + + if (copy_to_user((drm_buf_map_t *)arg, + &request, + sizeof(request))) + return -EFAULT; + + return retcode; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/context-mod.c linux.ac/drivers/char/drm/context-mod.c --- linux.vanilla/drivers/char/drm/context-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/context-mod.c Fri May 4 16:25:46 2001 @@ -0,0 +1,321 @@ +/* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static int drm_init_queue(drm_device_t *dev, drm_queue_t *q, drm_ctx_t *ctx) +{ + DRM_DEBUG("\n"); + + if (atomic_read(&q->use_count) != 1 + || atomic_read(&q->finalization) + || atomic_read(&q->block_count)) { + DRM_ERROR("New queue is already in use: u%d f%d b%d\n", + atomic_read(&q->use_count), + atomic_read(&q->finalization), + atomic_read(&q->block_count)); + } + + atomic_set(&q->finalization, 0); + atomic_set(&q->block_count, 0); + atomic_set(&q->block_read, 0); + atomic_set(&q->block_write, 0); + atomic_set(&q->total_queued, 0); + atomic_set(&q->total_flushed, 0); + atomic_set(&q->total_locks, 0); + + init_waitqueue_head(&q->write_queue); + init_waitqueue_head(&q->read_queue); + init_waitqueue_head(&q->flush_queue); + + q->flags = ctx->flags; + + drm_waitlist_create(&q->waitlist, dev->dma->buf_count); + + return 0; +} + + +/* drm_alloc_queue: +PRE: 1) dev->queuelist[0..dev->queue_count] is allocated and will not + disappear (so all deallocation must be done after IOCTLs are off) + 2) dev->queue_count < dev->queue_slots + 3) dev->queuelist[i].use_count == 0 and + dev->queuelist[i].finalization == 0 if i not in use +POST: 1) dev->queuelist[i].use_count == 1 + 2) dev->queue_count < dev->queue_slots */ + +static int drm_alloc_queue(drm_device_t *dev) +{ + int i; + drm_queue_t *queue; + int oldslots; + int newslots; + /* Check for a free queue */ + for (i = 0; i < dev->queue_count; i++) { + atomic_inc(&dev->queuelist[i]->use_count); + if (atomic_read(&dev->queuelist[i]->use_count) == 1 + && !atomic_read(&dev->queuelist[i]->finalization)) { + DRM_DEBUG("%d (free)\n", i); + return i; + } + atomic_dec(&dev->queuelist[i]->use_count); + } + /* Allocate a new queue */ + + queue = drm_alloc(sizeof(*queue), DRM_MEM_QUEUES); + if(queue == NULL) + return -ENOMEM; + + memset(queue, 0, sizeof(*queue)); + down(&dev->struct_sem); + atomic_set(&queue->use_count, 1); + + ++dev->queue_count; + if (dev->queue_count >= dev->queue_slots) { + oldslots = dev->queue_slots * sizeof(*dev->queuelist); + if (!dev->queue_slots) dev->queue_slots = 1; + dev->queue_slots *= 2; + newslots = dev->queue_slots * sizeof(*dev->queuelist); + + dev->queuelist = drm_realloc(dev->queuelist, + oldslots, + newslots, + DRM_MEM_QUEUES); + if (!dev->queuelist) { + up(&dev->struct_sem); + DRM_DEBUG("out of memory\n"); + return -ENOMEM; + } + } + dev->queuelist[dev->queue_count-1] = queue; + + up(&dev->struct_sem); + DRM_DEBUG("%d (new)\n", dev->queue_count - 1); + return dev->queue_count - 1; +} + +int drm_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res))) + return -EFAULT; + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + if (copy_to_user(&res.contexts[i], + &i, + sizeof(i))) + return -EFAULT; + } + } + res.count = DRM_RESERVED_CONTEXTS; + if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res))) + return -EFAULT; + return 0; +} + + +int drm_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + if ((ctx.handle = drm_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Init kernel's context and get a new one. */ + drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx); + ctx.handle = drm_alloc_queue(dev); + } + drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx); + DRM_DEBUG("%d\n", ctx.handle); + if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx))) + return -EFAULT; + return 0; +} + +int drm_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle < 0 || ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + if (DRM_BUFCOUNT(&q->waitlist)) { + atomic_dec(&q->use_count); + return -EBUSY; + } + + q->flags = ctx.flags; + + atomic_dec(&q->use_count); + return 0; +} + +int drm_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + ctx.flags = q->flags; + atomic_dec(&q->use_count); + + if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx))) + return -EFAULT; + + return 0; +} + +int drm_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + return drm_context_switch(dev, dev->last_context, ctx.handle); +} + +int drm_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + drm_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int drm_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + drm_buf_t *buf; + + if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx))) + return -EFAULT; + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + atomic_inc(&q->finalization); /* Mark queue in finalization state */ + atomic_sub(2, &q->use_count); /* Mark queue as unused (pending + finalization) */ + + while (test_and_set_bit(0, &dev->interrupt_flag)) { + schedule(); + if (signal_pending(current)) { + clear_bit(0, &dev->interrupt_flag); + return -EINTR; + } + } + /* Remove queued buffers */ + while ((buf = drm_waitlist_get(&q->waitlist))) { + drm_free_buffer(dev, buf); + } + clear_bit(0, &dev->interrupt_flag); + + /* Wakeup blocked processes */ + wake_up_interruptible(&q->read_queue); + wake_up_interruptible(&q->write_queue); + wake_up_interruptible(&q->flush_queue); + + /* Finalization over. Queue is made + available when both use_count and + finalization become 0, which won't + happen until all the waiting processes + stop waiting. */ + atomic_dec(&q->finalization); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/context.c linux.ac/drivers/char/drm/context.c --- linux.vanilla/drivers/char/drm/context.c Tue Aug 29 22:09:15 2000 +++ linux.ac/drivers/char/drm/context.c Fri May 4 16:25:46 2001 @@ -91,10 +91,13 @@ atomic_dec(&dev->queuelist[i]->use_count); } /* Allocate a new queue */ - down(&dev->struct_sem); queue = drm_alloc(sizeof(*queue), DRM_MEM_QUEUES); + if(queue == NULL) + return -ENOMEM; + memset(queue, 0, sizeof(*queue)); + down(&dev->struct_sem); atomic_set(&queue->use_count, 1); ++dev->queue_count; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/ctxbitmap-mod.c linux.ac/drivers/char/drm/ctxbitmap-mod.c --- linux.vanilla/drivers/char/drm/ctxbitmap-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/ctxbitmap-mod.c Tue Apr 3 17:52:36 2001 @@ -0,0 +1,85 @@ +/* ctxbitmap.c -- Context bitmap management -*- linux-c -*- + * Created: Thu Jan 6 03:56:42 2000 by jhartmann@precisioninsight.com + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +void drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle) +{ + if (ctx_handle < 0) goto failed; + + if (ctx_handle < DRM_MAX_CTXBITMAP) { + clear_bit(ctx_handle, dev->ctx_bitmap); + return; + } +failed: + DRM_ERROR("Attempt to free invalid context handle: %d\n", + ctx_handle); + return; +} + +int drm_ctxbitmap_next(drm_device_t *dev) +{ + int bit; + + bit = find_first_zero_bit(dev->ctx_bitmap, DRM_MAX_CTXBITMAP); + if (bit < DRM_MAX_CTXBITMAP) { + set_bit(bit, dev->ctx_bitmap); + DRM_DEBUG("drm_ctxbitmap_next bit : %d\n", bit); + return bit; + } + return -1; +} + +int drm_ctxbitmap_init(drm_device_t *dev) +{ + int i; + int temp; + + dev->ctx_bitmap = (unsigned long *) drm_alloc(PAGE_SIZE, + DRM_MEM_CTXBITMAP); + if(dev->ctx_bitmap == NULL) { + return -ENOMEM; + } + memset((void *) dev->ctx_bitmap, 0, PAGE_SIZE); + for(i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + temp = drm_ctxbitmap_next(dev); + DRM_DEBUG("drm_ctxbitmap_init : %d\n", temp); + } + + return 0; +} + +void drm_ctxbitmap_cleanup(drm_device_t *dev) +{ + drm_free((void *)dev->ctx_bitmap, PAGE_SIZE, + DRM_MEM_CTXBITMAP); +} + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/dma-mod.c linux.ac/drivers/char/drm/dma-mod.c --- linux.vanilla/drivers/char/drm/dma-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/dma-mod.c Tue Apr 3 17:52:36 2001 @@ -0,0 +1,543 @@ +/* dma.c -- DMA IOCTL and function support -*- linux-c -*- + * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +#include /* For task queue support */ + +void drm_dma_setup(drm_device_t *dev) +{ + int i; + + dev->dma = drm_alloc(sizeof(*dev->dma), DRM_MEM_DRIVER); + memset(dev->dma, 0, sizeof(*dev->dma)); + for (i = 0; i <= DRM_MAX_ORDER; i++) + memset(&dev->dma->bufs[i], 0, sizeof(dev->dma->bufs[0])); +} + +void drm_dma_takedown(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + int i, j; + + if (!dma) return; + + /* Clear dma buffers */ + for (i = 0; i <= DRM_MAX_ORDER; i++) { + if (dma->bufs[i].seg_count) { + DRM_DEBUG("order %d: buf_count = %d," + " seg_count = %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].seg_count); + for (j = 0; j < dma->bufs[i].seg_count; j++) { + drm_free_pages(dma->bufs[i].seglist[j], + dma->bufs[i].page_order, + DRM_MEM_DMA); + } + drm_free(dma->bufs[i].seglist, + dma->bufs[i].seg_count + * sizeof(*dma->bufs[0].seglist), + DRM_MEM_SEGS); + } + if(dma->bufs[i].buf_count) { + for(j = 0; j < dma->bufs[i].buf_count; j++) { + if(dma->bufs[i].buflist[j].dev_private) { + drm_free(dma->bufs[i].buflist[j].dev_private, + dma->bufs[i].buflist[j].dev_priv_size, + DRM_MEM_BUFS); + } + } + drm_free(dma->bufs[i].buflist, + dma->bufs[i].buf_count * + sizeof(*dma->bufs[0].buflist), + DRM_MEM_BUFS); + drm_freelist_destroy(&dma->bufs[i].freelist); + } + } + + if (dma->buflist) { + drm_free(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + DRM_MEM_BUFS); + } + + if (dma->pagelist) { + drm_free(dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + DRM_MEM_PAGES); + } + drm_free(dev->dma, sizeof(*dev->dma), DRM_MEM_DRIVER); + dev->dma = NULL; +} + +#if DRM_DMA_HISTOGRAM +/* This is slow, but is useful for debugging. */ +int drm_histogram_slot(unsigned long count) +{ + int value = DRM_DMA_HISTOGRAM_INITIAL; + int slot; + + for (slot = 0; + slot < DRM_DMA_HISTOGRAM_SLOTS; + ++slot, value = DRM_DMA_HISTOGRAM_NEXT(value)) { + if (count < value) return slot; + } + return DRM_DMA_HISTOGRAM_SLOTS - 1; +} + +void drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf) +{ + cycles_t queued_to_dispatched; + cycles_t dispatched_to_completed; + cycles_t completed_to_freed; + int q2d, d2c, c2f, q2c, q2f; + + if (buf->time_queued) { + queued_to_dispatched = (buf->time_dispatched + - buf->time_queued); + dispatched_to_completed = (buf->time_completed + - buf->time_dispatched); + completed_to_freed = (buf->time_freed + - buf->time_completed); + + q2d = drm_histogram_slot(queued_to_dispatched); + d2c = drm_histogram_slot(dispatched_to_completed); + c2f = drm_histogram_slot(completed_to_freed); + + q2c = drm_histogram_slot(queued_to_dispatched + + dispatched_to_completed); + q2f = drm_histogram_slot(queued_to_dispatched + + dispatched_to_completed + + completed_to_freed); + + atomic_inc(&dev->histo.total); + atomic_inc(&dev->histo.queued_to_dispatched[q2d]); + atomic_inc(&dev->histo.dispatched_to_completed[d2c]); + atomic_inc(&dev->histo.completed_to_freed[c2f]); + + atomic_inc(&dev->histo.queued_to_completed[q2c]); + atomic_inc(&dev->histo.queued_to_freed[q2f]); + + } + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +} +#endif + +void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf) +{ + drm_device_dma_t *dma = dev->dma; + + if (!buf) return; + + buf->waiting = 0; + buf->pending = 0; + buf->pid = 0; + buf->used = 0; +#if DRM_DMA_HISTOGRAM + buf->time_completed = get_cycles(); +#endif + if (waitqueue_active(&buf->dma_wait)) { + wake_up_interruptible(&buf->dma_wait); + } else { + /* If processes are waiting, the last one + to wake will put the buffer on the free + list. If no processes are waiting, we + put the buffer on the freelist here. */ + drm_freelist_put(dev, &dma->bufs[buf->order].freelist, buf); + } +} + +void drm_reclaim_buffers(drm_device_t *dev, pid_t pid) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return; + for (i = 0; i < dma->buf_count; i++) { + if (dma->buflist[i]->pid == pid) { + switch (dma->buflist[i]->list) { + case DRM_LIST_NONE: + drm_free_buffer(dev, dma->buflist[i]); + break; + case DRM_LIST_WAIT: + dma->buflist[i]->list = DRM_LIST_RECLAIM; + break; + default: + /* Buffer already on hardware. */ + break; + } + } + } +} + +int drm_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + drm_queue_t *q; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new >= dev->queue_count) { + clear_bit(0, &dev->context_flag); + return -EINVAL; + } + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + q = dev->queuelist[new]; + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + atomic_dec(&q->use_count); + clear_bit(0, &dev->context_flag); + return -EINVAL; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + drm_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + atomic_dec(&q->use_count); + + return 0; +} + +int drm_context_switch_complete(drm_device_t *dev, int new) +{ + drm_device_dma_t *dma = dev->dma; + + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + if (!dma || !(dma->next_buffer && dma->next_buffer->while_locked)) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("Cannot free lock\n"); + } + } + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up_interruptible(&dev->context_wait); + + return 0; +} + +void drm_clear_next_buffer(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + + dma->next_buffer = NULL; + if (dma->next_queue && !DRM_BUFCOUNT(&dma->next_queue->waitlist)) { + wake_up_interruptible(&dma->next_queue->flush_queue); + } + dma->next_queue = NULL; +} + + +int drm_select_queue(drm_device_t *dev, void (*wrapper)(unsigned long)) +{ + int i; + int candidate = -1; + int j = jiffies; + + if (!dev) { + DRM_ERROR("No device\n"); + return -1; + } + if (!dev->queuelist || !dev->queuelist[DRM_KERNEL_CONTEXT]) { + /* This only happens between the time the + interrupt is initialized and the time + the queues are initialized. */ + return -1; + } + + /* Doing "while locked" DMA? */ + if (DRM_WAITCOUNT(dev, DRM_KERNEL_CONTEXT)) { + return DRM_KERNEL_CONTEXT; + } + + /* If there are buffers on the last_context + queue, and we have not been executing + this context very long, continue to + execute this context. */ + if (dev->last_switch <= j + && dev->last_switch + DRM_TIME_SLICE > j + && DRM_WAITCOUNT(dev, dev->last_context)) { + return dev->last_context; + } + + /* Otherwise, find a candidate */ + for (i = dev->last_checked + 1; i < dev->queue_count; i++) { + if (DRM_WAITCOUNT(dev, i)) { + candidate = dev->last_checked = i; + break; + } + } + + if (candidate < 0) { + for (i = 0; i < dev->queue_count; i++) { + if (DRM_WAITCOUNT(dev, i)) { + candidate = dev->last_checked = i; + break; + } + } + } + + if (wrapper + && candidate >= 0 + && candidate != dev->last_context + && dev->last_switch <= j + && dev->last_switch + DRM_TIME_SLICE > j) { + if (dev->timer.expires != dev->last_switch + DRM_TIME_SLICE) { + del_timer(&dev->timer); + dev->timer.function = wrapper; + dev->timer.data = (unsigned long)dev; + dev->timer.expires = dev->last_switch+DRM_TIME_SLICE; + add_timer(&dev->timer); + } + return -1; + } + + return candidate; +} + + +int drm_dma_enqueue(drm_device_t *dev, drm_dma_t *d) +{ + int i; + drm_queue_t *q; + drm_buf_t *buf; + int idx; + int while_locked = 0; + drm_device_dma_t *dma = dev->dma; + DECLARE_WAITQUEUE(entry, current); + + DRM_DEBUG("%d\n", d->send_count); + + if (d->flags & _DRM_DMA_WHILE_LOCKED) { + int context = dev->lock.hw_lock->lock; + + if (!_DRM_LOCK_IS_HELD(context)) { + DRM_ERROR("No lock held during \"while locked\"" + " request\n"); + return -EINVAL; + } + if (d->context != _DRM_LOCKING_CONTEXT(context) + && _DRM_LOCKING_CONTEXT(context) != DRM_KERNEL_CONTEXT) { + DRM_ERROR("Lock held by %d while %d makes" + " \"while locked\" request\n", + _DRM_LOCKING_CONTEXT(context), + d->context); + return -EINVAL; + } + q = dev->queuelist[DRM_KERNEL_CONTEXT]; + while_locked = 1; + } else { + q = dev->queuelist[d->context]; + } + + + atomic_inc(&q->use_count); + if (atomic_read(&q->block_write)) { + add_wait_queue(&q->write_queue, &entry); + atomic_inc(&q->block_count); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!atomic_read(&q->block_write)) break; + schedule(); + if (signal_pending(current)) { + atomic_dec(&q->use_count); + remove_wait_queue(&q->write_queue, &entry); + return -EINTR; + } + } + atomic_dec(&q->block_count); + current->state = TASK_RUNNING; + remove_wait_queue(&q->write_queue, &entry); + } + + for (i = 0; i < d->send_count; i++) { + idx = d->send_indices[i]; + if (idx < 0 || idx >= dma->buf_count) { + atomic_dec(&q->use_count); + DRM_ERROR("Index %d (of %d max)\n", + d->send_indices[i], dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[ idx ]; + if (buf->pid != current->pid) { + atomic_dec(&q->use_count); + DRM_ERROR("Process %d using buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + if (buf->list != DRM_LIST_NONE) { + atomic_dec(&q->use_count); + DRM_ERROR("Process %d using buffer %d on list %d\n", + current->pid, buf->idx, buf->list); + } + buf->used = d->send_sizes[i]; + buf->while_locked = while_locked; + buf->context = d->context; + if (!buf->used) { + DRM_ERROR("Queueing 0 length buffer\n"); + } + if (buf->pending) { + atomic_dec(&q->use_count); + DRM_ERROR("Queueing pending buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + return -EINVAL; + } + if (buf->waiting) { + atomic_dec(&q->use_count); + DRM_ERROR("Queueing waiting buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + return -EINVAL; + } + buf->waiting = 1; + if (atomic_read(&q->use_count) == 1 + || atomic_read(&q->finalization)) { + drm_free_buffer(dev, buf); + } else { + drm_waitlist_put(&q->waitlist, buf); + atomic_inc(&q->total_queued); + } + } + atomic_dec(&q->use_count); + + return 0; +} + +static int drm_dma_get_buffers_of_order(drm_device_t *dev, drm_dma_t *d, + int order) +{ + int i; + drm_buf_t *buf; + drm_device_dma_t *dma = dev->dma; + + for (i = d->granted_count; i < d->request_count; i++) { + buf = drm_freelist_get(&dma->bufs[order].freelist, + d->flags & _DRM_DMA_WAIT); + if (!buf) break; + if (buf->pending || buf->waiting) { + DRM_ERROR("Free buffer %d in use by %d (w%d, p%d)\n", + buf->idx, + buf->pid, + buf->waiting, + buf->pending); + } + buf->pid = current->pid; + if (copy_to_user(&d->request_indices[i], + &buf->idx, + sizeof(buf->idx))) + return -EFAULT; + + if (copy_to_user(&d->request_sizes[i], + &buf->total, + sizeof(buf->total))) + return -EFAULT; + + ++d->granted_count; + } + return 0; +} + + +int drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma) +{ + int order; + int retcode = 0; + int tmp_order; + + order = drm_order(dma->request_size); + + dma->granted_count = 0; + retcode = drm_dma_get_buffers_of_order(dev, dma, order); + + if (dma->granted_count < dma->request_count + && (dma->flags & _DRM_DMA_SMALLER_OK)) { + for (tmp_order = order - 1; + !retcode + && dma->granted_count < dma->request_count + && tmp_order >= DRM_MIN_ORDER; + --tmp_order) { + + retcode = drm_dma_get_buffers_of_order(dev, dma, + tmp_order); + } + } + + if (dma->granted_count < dma->request_count + && (dma->flags & _DRM_DMA_LARGER_OK)) { + for (tmp_order = order + 1; + !retcode + && dma->granted_count < dma->request_count + && tmp_order <= DRM_MAX_ORDER; + ++tmp_order) { + + retcode = drm_dma_get_buffers_of_order(dev, dma, + tmp_order); + } + } + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/drawable-mod.c linux.ac/drivers/char/drm/drawable-mod.c --- linux.vanilla/drivers/char/drm/drawable-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/drawable-mod.c Tue Apr 3 17:52:36 2001 @@ -0,0 +1,51 @@ +/* drawable.c -- IOCTLs for drawables -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_adddraw(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_draw_t draw; + + draw.handle = 0; /* NOOP */ + DRM_DEBUG("%d\n", draw.handle); + if (copy_to_user((drm_draw_t *)arg, &draw, sizeof(draw))) + return -EFAULT; + return 0; +} + +int drm_rmdraw(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + return 0; /* NOOP */ +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/fops-mod.c linux.ac/drivers/char/drm/fops-mod.c --- linux.vanilla/drivers/char/drm/fops-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/fops-mod.c Wed May 23 00:08:37 2001 @@ -0,0 +1,253 @@ +/* fops.c -- File operations for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Daryll Strauss + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include + +/* drm_open is called whenever a process opens /dev/drm. */ + +int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t *dev) +{ + kdev_t minor = MINOR(inode->i_rdev); + drm_file_t *priv; + + if (filp->f_flags & O_EXCL) return -EBUSY; /* No exclusive opens */ + if (!drm_cpu_valid()) return -EINVAL; + + DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor); + + priv = drm_alloc(sizeof(*priv), DRM_MEM_FILES); + if(priv == NULL) + return -ENOMEM; + memset(priv, 0, sizeof(*priv)); + + filp->private_data = priv; + priv->uid = current->euid; + priv->pid = current->pid; + priv->minor = minor; + priv->dev = dev; + priv->ioctl_count = 0; + priv->authenticated = capable(CAP_SYS_ADMIN); + + down(&dev->struct_sem); + if (!dev->file_last) { + priv->next = NULL; + priv->prev = NULL; + dev->file_first = priv; + dev->file_last = priv; + } else { + priv->next = NULL; + priv->prev = dev->file_last; + dev->file_last->next = priv; + dev->file_last = priv; + } + up(&dev->struct_sem); + + return 0; +} + +int drm_flush(struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + return 0; +} + +/* drm_release is called whenever a process closes /dev/drm*. Linux calls + this only if any mappings have been closed. */ + +int drm_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + + if (dev->lock.hw_lock + && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.pid == current->pid) { + DRM_ERROR("Process %d dead, freeing lock for context %d\n", + current->pid, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + drm_lock_free(dev, + &dev->lock.hw_lock->lock, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + + /* FIXME: may require heavy-handed reset of + hardware at this point, possibly + processed via a callback to the X + server. */ + } + drm_reclaim_buffers(dev, priv->pid); + + drm_fasync(-1, filp, 0); + + down(&dev->struct_sem); + if (priv->prev) priv->prev->next = priv->next; + else dev->file_first = priv->next; + if (priv->next) priv->next->prev = priv->prev; + else dev->file_last = priv->prev; + up(&dev->struct_sem); + + drm_free(priv, sizeof(*priv), DRM_MEM_FILES); + + return 0; +} + +int drm_fasync(int fd, struct file *filp, int on) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode; + + DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device); + retcode = fasync_helper(fd, filp, on, &dev->buf_async); + if (retcode < 0) return retcode; + return 0; +} + + +/* The drm_read and drm_write_string code (especially that which manages + the circular buffer), is based on Alessandro Rubini's LINUX DEVICE + DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */ + +ssize_t drm_read(struct file *filp, char *buf, size_t count, loff_t *off) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int left; + int avail; + int send; + int cur; + + DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp); + + while (dev->buf_rp == dev->buf_wp) { + DRM_DEBUG(" sleeping\n"); + if (filp->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + interruptible_sleep_on(&dev->buf_readers); + if (signal_pending(current)) { + DRM_DEBUG(" interrupted\n"); + return -ERESTARTSYS; + } + DRM_DEBUG(" awake\n"); + } + + left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + avail = DRM_BSZ - left; + send = DRM_MIN(avail, count); + + while (send) { + if (dev->buf_wp > dev->buf_rp) { + cur = DRM_MIN(send, dev->buf_wp - dev->buf_rp); + } else { + cur = DRM_MIN(send, dev->buf_end - dev->buf_rp); + } + if (copy_to_user(buf, dev->buf_rp, cur)) + return -EFAULT; + dev->buf_rp += cur; + if (dev->buf_rp == dev->buf_end) dev->buf_rp = dev->buf; + send -= cur; + } + + wake_up_interruptible(&dev->buf_writers); + return DRM_MIN(avail, count);; +} + +int drm_write_string(drm_device_t *dev, const char *s) +{ + int left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + int send = strlen(s); + int count; + + DRM_DEBUG("%d left, %d to send (%p, %p)\n", + left, send, dev->buf_rp, dev->buf_wp); + + if (left == 1 || dev->buf_wp != dev->buf_rp) { + DRM_ERROR("Buffer not empty (%d left, wp = %p, rp = %p)\n", + left, + dev->buf_wp, + dev->buf_rp); + } + + while (send) { + if (dev->buf_wp >= dev->buf_rp) { + count = DRM_MIN(send, dev->buf_end - dev->buf_wp); + if (count == left) --count; /* Leave a hole */ + } else { + count = DRM_MIN(send, dev->buf_rp - dev->buf_wp - 1); + } + strncpy(dev->buf_wp, s, count); + dev->buf_wp += count; + if (dev->buf_wp == dev->buf_end) dev->buf_wp = dev->buf; + send -= count; + } + +#if LINUX_VERSION_CODE < 0x020315 && !defined(KILLFASYNCHASTHREEPARAMETERS) + /* The extra parameter to kill_fasync was added in 2.3.21, and is + _not_ present in _stock_ 2.2.14 and 2.2.15. However, some + distributions patch 2.2.x kernels to add this parameter. The + Makefile.linux attempts to detect this addition and defines + KILLFASYNCHASTHREEPARAMETERS if three parameters are found. */ + if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO); +#else + + /* Parameter added in 2.3.21. */ +#if LINUX_VERSION_CODE < 0x020400 + if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_IN); +#else + /* Type of first parameter changed in + Linux 2.4.0-test2... */ + if (dev->buf_async) kill_fasync(&dev->buf_async, SIGIO, POLL_IN); +#endif +#endif + DRM_DEBUG("waking\n"); + wake_up_interruptible(&dev->buf_readers); + return 0; +} + +unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + poll_wait(filp, &dev->buf_readers, wait); + if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM; + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/init-mod.c linux.ac/drivers/char/drm/init-mod.c --- linux.vanilla/drivers/char/drm/init-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/init-mod.c Tue Apr 3 17:52:36 2001 @@ -0,0 +1,113 @@ +/* init.c -- Setup/Cleanup for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_flags = 0; + +/* drm_parse_option parses a single option. See description for + drm_parse_options for details. */ + +static void drm_parse_option(char *s) +{ + char *c, *r; + + DRM_DEBUG("\"%s\"\n", s); + if (!s || !*s) return; + for (c = s; *c && *c != ':'; c++); /* find : or \0 */ + if (*c) r = c + 1; else r = NULL; /* remember remainder */ + *c = '\0'; /* terminate */ + if (!strcmp(s, "noctx")) { + drm_flags |= DRM_FLAG_NOCTX; + DRM_INFO("Server-mediated context switching OFF\n"); + return; + } + if (!strcmp(s, "debug")) { + drm_flags |= DRM_FLAG_DEBUG; + DRM_INFO("Debug messages ON\n"); + return; + } + DRM_ERROR("\"%s\" is not a valid option\n", s); + return; +} + +/* drm_parse_options parse the insmod "drm=" options, or the command-line + * options passed to the kernel via LILO. The grammar of the format is as + * follows: + * + * drm ::= 'drm=' option_list + * option_list ::= option [ ';' option_list ] + * option ::= 'device:' major + * | 'debug' + * | 'noctx' + * major ::= INTEGER + * + * Note that 's' contains option_list without the 'drm=' part. + * + * device=major,minor specifies the device number used for /dev/drm + * if major == 0 then the misc device is used + * if major == 0 and minor == 0 then dynamic misc allocation is used + * debug=on specifies that debugging messages will be printk'd + * debug=trace specifies that each function call will be logged via printk + * debug=off turns off all debugging options + * + */ + +void drm_parse_options(char *s) +{ + char *h, *t, *n; + + DRM_DEBUG("\"%s\"\n", s ?: ""); + if (!s || !*s) return; + + for (h = t = n = s; h && *h; h = n) { + for (; *t && *t != ';'; t++); /* find ; or \0 */ + if (*t) n = t + 1; else n = NULL; /* remember next */ + *t = '\0'; /* terminate */ + drm_parse_option(h); /* parse */ + } +} + +/* drm_cpu_valid returns non-zero if the DRI will run on this CPU, and 0 + * otherwise. */ + +int drm_cpu_valid(void) +{ +#if defined(__i386__) + if (boot_cpu_data.x86 == 3) return 0; /* No cmpxchg on a 386 */ +#endif +#if defined(__sparc__) && !defined(__sparc_v9__) + if (1) + return 0; /* No cmpxchg before v9 sparc. */ +#endif + return 1; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/ioctl-mod.c linux.ac/drivers/char/drm/ioctl-mod.c --- linux.vanilla/drivers/char/drm/ioctl-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/ioctl-mod.c Tue Apr 3 17:52:36 2001 @@ -0,0 +1,99 @@ +/* ioctl.c -- IOCTL processing for DRM -*- linux-c -*- + * Created: Fri Jan 8 09:01:26 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_irq_busid(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_irq_busid_t p; + struct pci_dev *dev; + + if (copy_from_user(&p, (drm_irq_busid_t *)arg, sizeof(p))) + return -EFAULT; + dev = pci_find_slot(p.busnum, PCI_DEVFN(p.devnum, p.funcnum)); + if (dev) p.irq = dev->irq; + else p.irq = 0; + DRM_DEBUG("%d:%d:%d => IRQ %d\n", + p.busnum, p.devnum, p.funcnum, p.irq); + if (copy_to_user((drm_irq_busid_t *)arg, &p, sizeof(p))) + return -EFAULT; + return 0; +} + +int drm_getunique(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u))) + return -EFAULT; + if (u.unique_len >= dev->unique_len) { + if (copy_to_user(u.unique, dev->unique, dev->unique_len)) + return -EFAULT; + } + u.unique_len = dev->unique_len; + if (copy_to_user((drm_unique_t *)arg, &u, sizeof(u))) + return -EFAULT; + return 0; +} + +int drm_setunique(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + if (dev->unique_len || dev->unique) + return -EBUSY; + + if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u))) + return -EFAULT; + + if (!u.unique_len) + return -EINVAL; + + dev->unique_len = u.unique_len; + dev->unique = drm_alloc(u.unique_len + 1, DRM_MEM_DRIVER); + if (copy_from_user(dev->unique, u.unique, dev->unique_len)) + return -EFAULT; + dev->unique[dev->unique_len] = '\0'; + + dev->devname = drm_alloc(strlen(dev->name) + strlen(dev->unique) + 2, + DRM_MEM_DRIVER); + sprintf(dev->devname, "%s@%s", dev->name, dev->unique); + + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/lists-mod.c linux.ac/drivers/char/drm/lists-mod.c --- linux.vanilla/drivers/char/drm/lists-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/lists-mod.c Tue Apr 3 17:52:36 2001 @@ -0,0 +1,218 @@ +/* lists.c -- Buffer list handling routines -*- linux-c -*- + * Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_waitlist_create(drm_waitlist_t *bl, int count) +{ + if (bl->count) return -EINVAL; + + bl->count = count; + bl->bufs = drm_alloc((bl->count + 2) * sizeof(*bl->bufs), + DRM_MEM_BUFLISTS); + bl->rp = bl->bufs; + bl->wp = bl->bufs; + bl->end = &bl->bufs[bl->count+1]; + bl->write_lock = SPIN_LOCK_UNLOCKED; + bl->read_lock = SPIN_LOCK_UNLOCKED; + return 0; +} + +int drm_waitlist_destroy(drm_waitlist_t *bl) +{ + if (bl->rp != bl->wp) return -EINVAL; + if (bl->bufs) drm_free(bl->bufs, + (bl->count + 2) * sizeof(*bl->bufs), + DRM_MEM_BUFLISTS); + bl->count = 0; + bl->bufs = NULL; + bl->rp = NULL; + bl->wp = NULL; + bl->end = NULL; + return 0; +} + +int drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf) +{ + int left; + unsigned long flags; + + left = DRM_LEFTCOUNT(bl); + if (!left) { + DRM_ERROR("Overflow while adding buffer %d from pid %d\n", + buf->idx, buf->pid); + return -EINVAL; + } +#if DRM_DMA_HISTOGRAM + buf->time_queued = get_cycles(); +#endif + buf->list = DRM_LIST_WAIT; + + spin_lock_irqsave(&bl->write_lock, flags); + *bl->wp = buf; + if (++bl->wp >= bl->end) bl->wp = bl->bufs; + spin_unlock_irqrestore(&bl->write_lock, flags); + + return 0; +} + +drm_buf_t *drm_waitlist_get(drm_waitlist_t *bl) +{ + drm_buf_t *buf; + unsigned long flags; + + spin_lock_irqsave(&bl->read_lock, flags); + buf = *bl->rp; + if (bl->rp == bl->wp) { + spin_unlock_irqrestore(&bl->read_lock, flags); + return NULL; + } + if (++bl->rp >= bl->end) bl->rp = bl->bufs; + spin_unlock_irqrestore(&bl->read_lock, flags); + + return buf; +} + +int drm_freelist_create(drm_freelist_t *bl, int count) +{ + atomic_set(&bl->count, 0); + bl->next = NULL; + init_waitqueue_head(&bl->waiting); + bl->low_mark = 0; + bl->high_mark = 0; + atomic_set(&bl->wfh, 0); + bl->lock = SPIN_LOCK_UNLOCKED; + ++bl->initialized; + return 0; +} + +int drm_freelist_destroy(drm_freelist_t *bl) +{ + atomic_set(&bl->count, 0); + bl->next = NULL; + return 0; +} + +int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf) +{ + drm_device_dma_t *dma = dev->dma; + + if (!dma) { + DRM_ERROR("No DMA support\n"); + return 1; + } + + if (buf->waiting || buf->pending || buf->list == DRM_LIST_FREE) { + DRM_ERROR("Freed buffer %d: w%d, p%d, l%d\n", + buf->idx, buf->waiting, buf->pending, buf->list); + } + if (!bl) return 1; +#if DRM_DMA_HISTOGRAM + buf->time_freed = get_cycles(); + drm_histogram_compute(dev, buf); +#endif + buf->list = DRM_LIST_FREE; + + spin_lock(&bl->lock); + buf->next = bl->next; + bl->next = buf; + spin_unlock(&bl->lock); + + atomic_inc(&bl->count); + if (atomic_read(&bl->count) > dma->buf_count) { + DRM_ERROR("%d of %d buffers free after addition of %d\n", + atomic_read(&bl->count), dma->buf_count, buf->idx); + return 1; + } + /* Check for high water mark */ + if (atomic_read(&bl->wfh) && atomic_read(&bl->count)>=bl->high_mark) { + atomic_set(&bl->wfh, 0); + wake_up_interruptible(&bl->waiting); + } + return 0; +} + +static drm_buf_t *drm_freelist_try(drm_freelist_t *bl) +{ + drm_buf_t *buf; + + if (!bl) return NULL; + + /* Get buffer */ + spin_lock(&bl->lock); + if (!bl->next) { + spin_unlock(&bl->lock); + return NULL; + } + buf = bl->next; + bl->next = bl->next->next; + spin_unlock(&bl->lock); + + atomic_dec(&bl->count); + buf->next = NULL; + buf->list = DRM_LIST_NONE; + if (buf->waiting || buf->pending) { + DRM_ERROR("Free buffer %d: w%d, p%d, l%d\n", + buf->idx, buf->waiting, buf->pending, buf->list); + } + + return buf; +} + +drm_buf_t *drm_freelist_get(drm_freelist_t *bl, int block) +{ + drm_buf_t *buf = NULL; + DECLARE_WAITQUEUE(entry, current); + + if (!bl || !bl->initialized) return NULL; + + /* Check for low water mark */ + if (atomic_read(&bl->count) <= bl->low_mark) /* Became low */ + atomic_set(&bl->wfh, 1); + if (atomic_read(&bl->wfh)) { + if (block) { + add_wait_queue(&bl->waiting, &entry); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!atomic_read(&bl->wfh) + && (buf = drm_freelist_try(bl))) break; + schedule(); + if (signal_pending(current)) break; + } + current->state = TASK_RUNNING; + remove_wait_queue(&bl->waiting, &entry); + } + return buf; + } + + return drm_freelist_try(bl); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/lock-mod.c linux.ac/drivers/char/drm/lock-mod.c --- linux.vanilla/drivers/char/drm/lock-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/lock-mod.c Tue Apr 3 17:52:36 2001 @@ -0,0 +1,252 @@ +/* lock.c -- IOCTLs for locking -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_block(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + DRM_DEBUG("\n"); + return 0; +} + +int drm_unblock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + DRM_DEBUG("\n"); + return 0; +} + +int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new, prev; + + do { + old = *lock; + if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT; + else new = context | _DRM_LOCK_HELD; + prev = cmpxchg(lock, old, new); + } while (prev != old); + if (_DRM_LOCKING_CONTEXT(old) == context) { + if (old & _DRM_LOCK_HELD) { + if (context != DRM_KERNEL_CONTEXT) { + DRM_ERROR("%d holds heavyweight lock\n", + context); + } + return 0; + } + } + if (new == (context | _DRM_LOCK_HELD)) { + /* Have lock */ + return 1; + } + return 0; +} + +/* This takes a lock forcibly and hands it to context. Should ONLY be used + inside *_unlock to give lock to kernel before calling *_dma_schedule. */ +int drm_lock_transfer(drm_device_t *dev, + __volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new, prev; + + dev->lock.pid = 0; + do { + old = *lock; + new = context | _DRM_LOCK_HELD; + prev = cmpxchg(lock, old, new); + } while (prev != old); + return 1; +} + +int drm_lock_free(drm_device_t *dev, + __volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new, prev; + pid_t pid = dev->lock.pid; + + dev->lock.pid = 0; + do { + old = *lock; + new = 0; + prev = cmpxchg(lock, old, new); + } while (prev != old); + if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { + DRM_ERROR("%d freed heavyweight lock held by %d (pid %d)\n", + context, + _DRM_LOCKING_CONTEXT(old), + pid); + return 1; + } + wake_up_interruptible(&dev->lock.lock_queue); + return 0; +} + +static int drm_flush_queue(drm_device_t *dev, int context) +{ + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_queue_t *q = dev->queuelist[context]; + + DRM_DEBUG("\n"); + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) > 1) { + atomic_inc(&q->block_write); + add_wait_queue(&q->flush_queue, &entry); + atomic_inc(&q->block_count); + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (!DRM_BUFCOUNT(&q->waitlist)) break; + schedule(); + if (signal_pending(current)) { + ret = -EINTR; /* Can't restart */ + break; + } + } + atomic_dec(&q->block_count); + current->state = TASK_RUNNING; + remove_wait_queue(&q->flush_queue, &entry); + } + atomic_dec(&q->use_count); + atomic_inc(&q->total_flushed); + + /* NOTE: block_write is still incremented! + Use drm_flush_unlock_queue to decrement. */ + return ret; +} + +static int drm_flush_unblock_queue(drm_device_t *dev, int context) +{ + drm_queue_t *q = dev->queuelist[context]; + + DRM_DEBUG("\n"); + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) > 1) { + if (atomic_read(&q->block_write)) { + atomic_dec(&q->block_write); + wake_up_interruptible(&q->write_queue); + } + } + atomic_dec(&q->use_count); + return 0; +} + +int drm_flush_block_and_flush(drm_device_t *dev, int context, + drm_lock_flags_t flags) +{ + int ret = 0; + int i; + + DRM_DEBUG("\n"); + + if (flags & _DRM_LOCK_FLUSH) { + ret = drm_flush_queue(dev, DRM_KERNEL_CONTEXT); + if (!ret) ret = drm_flush_queue(dev, context); + } + if (flags & _DRM_LOCK_FLUSH_ALL) { + for (i = 0; !ret && i < dev->queue_count; i++) { + ret = drm_flush_queue(dev, i); + } + } + return ret; +} + +int drm_flush_unblock(drm_device_t *dev, int context, drm_lock_flags_t flags) +{ + int ret = 0; + int i; + + DRM_DEBUG("\n"); + + if (flags & _DRM_LOCK_FLUSH) { + ret = drm_flush_unblock_queue(dev, DRM_KERNEL_CONTEXT); + if (!ret) ret = drm_flush_unblock_queue(dev, context); + } + if (flags & _DRM_LOCK_FLUSH_ALL) { + for (i = 0; !ret && i < dev->queue_count; i++) { + ret = drm_flush_unblock_queue(dev, i); + } + } + + return ret; +} + +int drm_finish(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int ret = 0; + drm_lock_t lock; + + DRM_DEBUG("\n"); + + if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock))) + return -EFAULT; + ret = drm_flush_block_and_flush(dev, lock.context, lock.flags); + drm_flush_unblock(dev, lock.context, lock.flags); + return ret; +} + +/* If we get here, it means that the process has called DRM_IOCTL_LOCK + without calling DRM_IOCTL_UNLOCK. + + If the lock is not held, then let the signal proceed as usual. + + If the lock is held, then set the contended flag and keep the signal + blocked. + + + Return 1 if the signal should be delivered normally. + Return 0 if the signal should be blocked. */ + +int drm_notifier(void *priv) +{ + drm_sigdata_t *s = (drm_sigdata_t *)priv; + unsigned int old, new, prev; + + + /* Allow signal delivery if lock isn't held */ + if (!_DRM_LOCK_IS_HELD(s->lock->lock) + || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context) return 1; + + /* Otherwise, set flag to force call to + drmUnlock */ + do { + old = s->lock->lock; + new = old | _DRM_LOCK_CONT; + prev = cmpxchg(&s->lock->lock, old, new); + } while (prev != old); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/memory-mod.c linux.ac/drivers/char/drm/memory-mod.c --- linux.vanilla/drivers/char/drm/memory-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/memory-mod.c Tue Apr 3 17:52:36 2001 @@ -0,0 +1,448 @@ +/* memory.c -- Memory management wrappers for DRM -*- linux-c -*- + * Created: Thu Feb 4 14:00:34 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include +#include "drmP.h" +#include + +typedef struct drm_mem_stats { + const char *name; + int succeed_count; + int free_count; + int fail_count; + unsigned long bytes_allocated; + unsigned long bytes_freed; +} drm_mem_stats_t; + +static spinlock_t drm_mem_lock = SPIN_LOCK_UNLOCKED; +static unsigned long drm_ram_available = 0; /* In pages */ +static unsigned long drm_ram_used = 0; +static drm_mem_stats_t drm_mem_stats[] = { + [DRM_MEM_DMA] = { "dmabufs" }, + [DRM_MEM_SAREA] = { "sareas" }, + [DRM_MEM_DRIVER] = { "driver" }, + [DRM_MEM_MAGIC] = { "magic" }, + [DRM_MEM_IOCTLS] = { "ioctltab" }, + [DRM_MEM_MAPS] = { "maplist" }, + [DRM_MEM_VMAS] = { "vmalist" }, + [DRM_MEM_BUFS] = { "buflist" }, + [DRM_MEM_SEGS] = { "seglist" }, + [DRM_MEM_PAGES] = { "pagelist" }, + [DRM_MEM_FILES] = { "files" }, + [DRM_MEM_QUEUES] = { "queues" }, + [DRM_MEM_CMDS] = { "commands" }, + [DRM_MEM_MAPPINGS] = { "mappings" }, + [DRM_MEM_BUFLISTS] = { "buflists" }, + [DRM_MEM_AGPLISTS] = { "agplist" }, + [DRM_MEM_TOTALAGP] = { "totalagp" }, + [DRM_MEM_BOUNDAGP] = { "boundagp" }, + [DRM_MEM_CTXBITMAP] = { "ctxbitmap"}, + { NULL, 0, } /* Last entry must be null */ +}; + +void drm_mem_init(void) +{ + drm_mem_stats_t *mem; + struct sysinfo si; + + for (mem = drm_mem_stats; mem->name; ++mem) { + mem->succeed_count = 0; + mem->free_count = 0; + mem->fail_count = 0; + mem->bytes_allocated = 0; + mem->bytes_freed = 0; + } + + si_meminfo(&si); +#if LINUX_VERSION_CODE < 0x020317 + /* Changed to page count in 2.3.23 */ + drm_ram_available = si.totalram >> PAGE_SHIFT; +#else + drm_ram_available = si.totalram; +#endif + drm_ram_used = 0; +} + +/* drm_mem_info is called whenever a process reads /dev/drm/mem. */ + +static int _drm_mem_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_mem_stats_t *pt; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" total counts " + " | outstanding \n"); + DRM_PROC_PRINT("type alloc freed fail bytes freed" + " | allocs bytes\n\n"); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB |\n", + "system", 0, 0, 0, + drm_ram_available << (PAGE_SHIFT - 10)); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB |\n", + "locked", 0, 0, 0, drm_ram_used >> 10); + DRM_PROC_PRINT("\n"); + for (pt = drm_mem_stats; pt->name; pt++) { + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu %10lu | %6d %10ld\n", + pt->name, + pt->succeed_count, + pt->free_count, + pt->fail_count, + pt->bytes_allocated, + pt->bytes_freed, + pt->succeed_count - pt->free_count, + (long)pt->bytes_allocated + - (long)pt->bytes_freed); + } + + return len; +} + +int drm_mem_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + int ret; + + spin_lock(&drm_mem_lock); + ret = _drm_mem_info(buf, start, offset, len, eof, data); + spin_unlock(&drm_mem_lock); + return ret; +} + +void *drm_alloc(size_t size, int area) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(area, "Allocating 0 bytes\n"); + return NULL; + } + + if (!(pt = kmalloc(size, GFP_KERNEL))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].fail_count; + spin_unlock(&drm_mem_lock); + return NULL; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_allocated += size; + spin_unlock(&drm_mem_lock); + return pt; +} + +void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area) +{ + void *pt; + + if (!(pt = drm_alloc(size, area))) return NULL; + if (oldpt && oldsize) { + memcpy(pt, oldpt, oldsize); + drm_free(oldpt, oldsize, area); + } + return pt; +} + +char *drm_strdup(const char *s, int area) +{ + char *pt; + int length = s ? strlen(s) : 0; + + if (!(pt = drm_alloc(length+1, area))) return NULL; + strcpy(pt, s); + return pt; +} + +void drm_strfree(const char *s, int area) +{ + unsigned int size; + + if (!s) return; + + size = 1 + (s ? strlen(s) : 0); + drm_free((void *)s, size, area); +} + +void drm_free(void *pt, size_t size, int area) +{ + int alloc_count; + int free_count; + + if (!pt) DRM_MEM_ERROR(area, "Attempt to free NULL pointer\n"); + else kfree(pt); + spin_lock(&drm_mem_lock); + drm_mem_stats[area].bytes_freed += size; + free_count = ++drm_mem_stats[area].free_count; + alloc_count = drm_mem_stats[area].succeed_count; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +unsigned long drm_alloc_pages(int order, int area) +{ + unsigned long address; + unsigned long bytes = PAGE_SIZE << order; + unsigned long addr; + unsigned int sz; + + spin_lock(&drm_mem_lock); + if ((drm_ram_used >> PAGE_SHIFT) + > (DRM_RAM_PERCENT * drm_ram_available) / 100) { + spin_unlock(&drm_mem_lock); + return 0; + } + spin_unlock(&drm_mem_lock); + + address = __get_free_pages(GFP_KERNEL, order); + if (!address) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].fail_count; + spin_unlock(&drm_mem_lock); + return 0; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_allocated += bytes; + drm_ram_used += bytes; + spin_unlock(&drm_mem_lock); + + + /* Zero outside the lock */ + memset((void *)address, 0, bytes); + + /* Reserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { +#if LINUX_VERSION_CODE >= 0x020400 + /* Argument type changed in 2.4.0-test6/pre8 */ + mem_map_reserve(virt_to_page(addr)); +#else + mem_map_reserve(MAP_NR(addr)); +#endif + } + + return address; +} + +void drm_free_pages(unsigned long address, int order, int area) +{ + unsigned long bytes = PAGE_SIZE << order; + int alloc_count; + int free_count; + unsigned long addr; + unsigned int sz; + + if (!address) { + DRM_MEM_ERROR(area, "Attempt to free address 0\n"); + } else { + /* Unreserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { +#if LINUX_VERSION_CODE >= 0x020400 + /* Argument type changed in 2.4.0-test6/pre8 */ + mem_map_unreserve(virt_to_page(addr)); +#else + mem_map_unreserve(MAP_NR(addr)); +#endif + } + free_pages(address, order); + } + + spin_lock(&drm_mem_lock); + free_count = ++drm_mem_stats[area].free_count; + alloc_count = drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_freed += bytes; + drm_ram_used -= bytes; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +void *drm_ioremap(unsigned long offset, unsigned long size) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Mapping 0 bytes at 0x%08lx\n", offset); + return NULL; + } + + if (!(pt = ioremap(offset, size))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_MAPPINGS].fail_count; + spin_unlock(&drm_mem_lock); + return NULL; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count; + drm_mem_stats[DRM_MEM_MAPPINGS].bytes_allocated += size; + spin_unlock(&drm_mem_lock); + return pt; +} + +void drm_ioremapfree(void *pt, unsigned long size) +{ + int alloc_count; + int free_count; + + if (!pt) + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Attempt to free NULL pointer\n"); + else + iounmap(pt); + + spin_lock(&drm_mem_lock); + drm_mem_stats[DRM_MEM_MAPPINGS].bytes_freed += size; + free_count = ++drm_mem_stats[DRM_MEM_MAPPINGS].free_count; + alloc_count = drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +agp_memory *drm_alloc_agp(int pages, u32 type) +{ + agp_memory *handle; + + if (!pages) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, "Allocating 0 pages\n"); + return NULL; + } + + if ((handle = drm_agp_allocate_memory(pages, type))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count; + drm_mem_stats[DRM_MEM_TOTALAGP].bytes_allocated + += pages << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + return handle; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_TOTALAGP].fail_count; + spin_unlock(&drm_mem_lock); + return NULL; +} + +int drm_free_agp(agp_memory *handle, int pages) +{ + int alloc_count; + int free_count; + int retval = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Attempt to free NULL AGP handle\n"); + return retval;; + } + + if (drm_agp_free_memory(handle)) { + spin_lock(&drm_mem_lock); + free_count = ++drm_mem_stats[DRM_MEM_TOTALAGP].free_count; + alloc_count = drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count; + drm_mem_stats[DRM_MEM_TOTALAGP].bytes_freed + += pages << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + return 0; + } + return retval; +} + +int drm_bind_agp(agp_memory *handle, unsigned int start) +{ + int retcode = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to bind NULL AGP handle\n"); + return retcode; + } + + if (!(retcode = drm_agp_bind_memory(handle, start))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count; + drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_allocated + += handle->page_count << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + return retcode; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_BOUNDAGP].fail_count; + spin_unlock(&drm_mem_lock); + return retcode; +} + +int drm_unbind_agp(agp_memory *handle) +{ + int alloc_count; + int free_count; + int retcode = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to unbind NULL AGP handle\n"); + return retcode; + } + + if ((retcode = drm_agp_unbind_memory(handle))) return retcode; + spin_lock(&drm_mem_lock); + free_count = ++drm_mem_stats[DRM_MEM_BOUNDAGP].free_count; + alloc_count = drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count; + drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_freed + += handle->page_count << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + return retcode; +} +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/proc-mod.c linux.ac/drivers/char/drm/proc-mod.c --- linux.vanilla/drivers/char/drm/proc-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/proc-mod.c Fri May 4 16:25:46 2001 @@ -0,0 +1,578 @@ +/* proc.c -- /proc support for DRM -*- linux-c -*- + * Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static struct proc_dir_entry *drm_root = NULL; +static struct proc_dir_entry *drm_dev_root = NULL; +static char drm_slot_name[64]; + +static int drm_name_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_vm_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_clients_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_queues_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_bufs_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#if DRM_DEBUG_CODE +static int drm_vma_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#endif +#if DRM_DMA_HISTOGRAM +static int drm_histo_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#endif + +struct drm_proc_list { + const char *name; + int (*f)(char *, char **, off_t, int, int *, void *); +} drm_proc_list[] = { + { "name", drm_name_info }, + { "mem", drm_mem_info }, + { "vm", drm_vm_info }, + { "clients", drm_clients_info }, + { "queues", drm_queues_info }, + { "bufs", drm_bufs_info }, +#if DRM_DEBUG_CODE + { "vma", drm_vma_info }, +#endif +#if DRM_DMA_HISTOGRAM + { "histo", drm_histo_info }, +#endif +}; +#define DRM_PROC_ENTRIES (sizeof(drm_proc_list)/sizeof(drm_proc_list[0])) + +int drm_proc_init(drm_device_t *dev) +{ + struct proc_dir_entry *ent; + int i, j; + + drm_root = create_proc_entry("dri", S_IFDIR, NULL); + if (!drm_root) { + DRM_ERROR("Cannot create /proc/dri\n"); + return -1; + } + + /* Instead of doing this search, we should + add some global support for /proc/dri. */ + for (i = 0; i < 8; i++) { + sprintf(drm_slot_name, "dri/%d", i); + drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL); + if (!drm_dev_root) { + DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name); + remove_proc_entry("dri", NULL); + break; + } + if (drm_dev_root->nlink == 2) break; + drm_dev_root = NULL; + } + if (!drm_dev_root) { + DRM_ERROR("Cannot find slot in /proc/dri\n"); + return -1; + } + + for (i = 0; i < DRM_PROC_ENTRIES; i++) { + ent = create_proc_entry(drm_proc_list[i].name, + S_IFREG|S_IRUGO, drm_dev_root); + if (!ent) { + DRM_ERROR("Cannot create /proc/%s/%s\n", + drm_slot_name, drm_proc_list[i].name); + for (j = 0; j < i; j++) + remove_proc_entry(drm_proc_list[i].name, + drm_dev_root); + remove_proc_entry(drm_slot_name, NULL); + remove_proc_entry("dri", NULL); + return -1; + } + ent->read_proc = drm_proc_list[i].f; + ent->data = dev; + } + + return 0; +} + + +int drm_proc_cleanup(void) +{ + int i; + + if (drm_root) { + if (drm_dev_root) { + for (i = 0; i < DRM_PROC_ENTRIES; i++) { + remove_proc_entry(drm_proc_list[i].name, + drm_dev_root); + } + remove_proc_entry(drm_slot_name, NULL); + } + remove_proc_entry("dri", NULL); + remove_proc_entry(DRM_NAME, NULL); + } + drm_root = drm_dev_root = NULL; + return 0; +} + +static int drm_name_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + + if (dev->unique) { + DRM_PROC_PRINT("%s 0x%x %s\n", + dev->name, dev->device, dev->unique); + } else { + DRM_PROC_PRINT("%s 0x%x\n", dev->name, dev->device); + } + return len; +} + +static int _drm_vm_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_map_t *map; + /* Hardcoded from _DRM_FRAME_BUFFER, + _DRM_REGISTERS, _DRM_SHM, and + _DRM_AGP. */ + const char *types[] = { "FB", "REG", "SHM", "AGP" }; + const char *type; + int i; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("slot offset size type flags " + "address mtrr\n\n"); + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + if (map->type < 0 || map->type > 3) type = "??"; + else type = types[map->type]; + DRM_PROC_PRINT("%4d 0x%08lx 0x%08lx %4.4s 0x%02x 0x%08lx ", + i, + map->offset, + map->size, + type, + map->flags, + (unsigned long)map->handle); + if (map->mtrr < 0) { + DRM_PROC_PRINT("none\n"); + } else { + DRM_PROC_PRINT("%4d\n", map->mtrr); + } + } + + return len; +} + +static int drm_vm_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_vm_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + + +static int _drm_queues_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int i; + drm_queue_t *q; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" ctx/flags use fin" + " blk/rw/rwf wait flushed queued" + " locks\n\n"); + for (i = 0; i < dev->queue_count; i++) { + q = dev->queuelist[i]; + atomic_inc(&q->use_count); + DRM_PROC_PRINT_RET(atomic_dec(&q->use_count), + "%5d/0x%03x %5d %5d" + " %5d/%c%c/%c%c%c %5Zd %10d %10d %10d\n", + i, + q->flags, + atomic_read(&q->use_count), + atomic_read(&q->finalization), + atomic_read(&q->block_count), + atomic_read(&q->block_read) ? 'r' : '-', + atomic_read(&q->block_write) ? 'w' : '-', + waitqueue_active(&q->read_queue) ? 'r':'-', + waitqueue_active(&q->write_queue) ? 'w':'-', + waitqueue_active(&q->flush_queue) ? 'f':'-', + DRM_BUFCOUNT(&q->waitlist), + atomic_read(&q->total_flushed), + atomic_read(&q->total_queued), + atomic_read(&q->total_locks)); + atomic_dec(&q->use_count); + } + + return len; +} + +static int drm_queues_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_queues_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + +/* drm_bufs_info is called whenever a process reads + /dev/drm//bufs. */ + +static int _drm_bufs_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return 0; + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" o size count free segs pages kB\n\n"); + for (i = 0; i <= DRM_MAX_ORDER; i++) { + if (dma->bufs[i].buf_count) + DRM_PROC_PRINT("%2d %8d %5d %5d %5d %5d %5ld\n", + i, + dma->bufs[i].buf_size, + dma->bufs[i].buf_count, + atomic_read(&dma->bufs[i] + .freelist.count), + dma->bufs[i].seg_count, + dma->bufs[i].seg_count + *(1 << dma->bufs[i].page_order), + (dma->bufs[i].seg_count + * (1 << dma->bufs[i].page_order)) + * PAGE_SIZE / 1024); + } + DRM_PROC_PRINT("\n"); + for (i = 0; i < dma->buf_count; i++) { + if (i && !(i%32)) DRM_PROC_PRINT("\n"); + DRM_PROC_PRINT(" %d", dma->buflist[i]->list); + } + DRM_PROC_PRINT("\n"); + + return len; +} + +static int drm_bufs_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_bufs_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + + +static int _drm_clients_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_file_t *priv; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("a dev pid uid magic ioctls\n\n"); + for (priv = dev->file_first; priv; priv = priv->next) { + DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n", + priv->authenticated ? 'y' : 'n', + priv->minor, + priv->pid, + priv->uid, + priv->magic, + priv->ioctl_count); + } + + return len; +} + +static int drm_clients_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_clients_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + +#if DRM_DEBUG_CODE + +#define DRM_VMA_VERBOSE 0 + +static int _drm_vma_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_vma_entry_t *pt; + struct vm_area_struct *vma; +#if DRM_VMA_VERBOSE + unsigned long i; + unsigned long address; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; +#endif +#if defined(__i386__) + unsigned int pgprot; +#endif + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("vma use count: %d, high_memory = %p, 0x%08lx\n", + atomic_read(&dev->vma_count), + high_memory, virt_to_phys(high_memory)); + for (pt = dev->vmalist; pt; pt = pt->next) { + if (!(vma = pt->vma)) continue; + DRM_PROC_PRINT("\n%5d 0x%08lx-0x%08lx %c%c%c%c%c%c 0x%08lx", + pt->pid, + vma->vm_start, + vma->vm_end, + vma->vm_flags & VM_READ ? 'r' : '-', + vma->vm_flags & VM_WRITE ? 'w' : '-', + vma->vm_flags & VM_EXEC ? 'x' : '-', + vma->vm_flags & VM_MAYSHARE ? 's' : 'p', + vma->vm_flags & VM_LOCKED ? 'l' : '-', + vma->vm_flags & VM_IO ? 'i' : '-', + VM_OFFSET(vma)); + +#if defined(__i386__) + pgprot = pgprot_val(vma->vm_page_prot); + DRM_PROC_PRINT(" %c%c%c%c%c%c%c%c%c", + pgprot & _PAGE_PRESENT ? 'p' : '-', + pgprot & _PAGE_RW ? 'w' : 'r', + pgprot & _PAGE_USER ? 'u' : 's', + pgprot & _PAGE_PWT ? 't' : 'b', + pgprot & _PAGE_PCD ? 'u' : 'c', + pgprot & _PAGE_ACCESSED ? 'a' : '-', + pgprot & _PAGE_DIRTY ? 'd' : '-', + pgprot & _PAGE_PSE ? 'm' : 'k', + pgprot & _PAGE_GLOBAL ? 'g' : 'l' ); +#endif + DRM_PROC_PRINT("\n"); +#if 0 + for (i = vma->vm_start; i < vma->vm_end; i += PAGE_SIZE) { + pgd = pgd_offset(vma->vm_mm, i); + pmd = pmd_offset(pgd, i); + pte = pte_offset(pmd, i); + if (pte_present(*pte)) { + address = __pa(pte_page(*pte)) + + (i & (PAGE_SIZE-1)); + DRM_PROC_PRINT(" 0x%08lx -> 0x%08lx" + " %c%c%c%c%c\n", + i, + address, + pte_read(*pte) ? 'r' : '-', + pte_write(*pte) ? 'w' : '-', + pte_exec(*pte) ? 'x' : '-', + pte_dirty(*pte) ? 'd' : '-', + pte_young(*pte) ? 'a' : '-' ); + } else { + DRM_PROC_PRINT(" 0x%08lx\n", i); + } + } +#endif + } + + return len; +} + +static int drm_vma_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_vma_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} +#endif + + +#if DRM_DMA_HISTOGRAM +static int _drm_histo_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_device_dma_t *dma = dev->dma; + int i; + unsigned long slot_value = DRM_DMA_HISTOGRAM_INITIAL; + unsigned long prev_value = 0; + drm_buf_t *buffer; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + + DRM_PROC_PRINT("general statistics:\n"); + DRM_PROC_PRINT("total %10u\n", atomic_read(&dev->histo.total)); + DRM_PROC_PRINT("open %10u\n", atomic_read(&dev->total_open)); + DRM_PROC_PRINT("close %10u\n", atomic_read(&dev->total_close)); + DRM_PROC_PRINT("ioctl %10u\n", atomic_read(&dev->total_ioctl)); + DRM_PROC_PRINT("irq %10u\n", atomic_read(&dev->total_irq)); + DRM_PROC_PRINT("ctx %10u\n", atomic_read(&dev->total_ctx)); + + DRM_PROC_PRINT("\nlock statistics:\n"); + DRM_PROC_PRINT("locks %10u\n", atomic_read(&dev->total_locks)); + DRM_PROC_PRINT("unlocks %10u\n", atomic_read(&dev->total_unlocks)); + DRM_PROC_PRINT("contends %10u\n", atomic_read(&dev->total_contends)); + DRM_PROC_PRINT("sleeps %10u\n", atomic_read(&dev->total_sleeps)); + + + if (dma) { + DRM_PROC_PRINT("\ndma statistics:\n"); + DRM_PROC_PRINT("prio %10u\n", + atomic_read(&dma->total_prio)); + DRM_PROC_PRINT("bytes %10u\n", + atomic_read(&dma->total_bytes)); + DRM_PROC_PRINT("dmas %10u\n", + atomic_read(&dma->total_dmas)); + DRM_PROC_PRINT("missed:\n"); + DRM_PROC_PRINT(" dma %10u\n", + atomic_read(&dma->total_missed_dma)); + DRM_PROC_PRINT(" lock %10u\n", + atomic_read(&dma->total_missed_lock)); + DRM_PROC_PRINT(" free %10u\n", + atomic_read(&dma->total_missed_free)); + DRM_PROC_PRINT(" sched %10u\n", + atomic_read(&dma->total_missed_sched)); + DRM_PROC_PRINT("tried %10u\n", + atomic_read(&dma->total_tried)); + DRM_PROC_PRINT("hit %10u\n", + atomic_read(&dma->total_hit)); + DRM_PROC_PRINT("lost %10u\n", + atomic_read(&dma->total_lost)); + + buffer = dma->next_buffer; + if (buffer) { + DRM_PROC_PRINT("next_buffer %7d\n", buffer->idx); + } else { + DRM_PROC_PRINT("next_buffer none\n"); + } + buffer = dma->this_buffer; + if (buffer) { + DRM_PROC_PRINT("this_buffer %7d\n", buffer->idx); + } else { + DRM_PROC_PRINT("this_buffer none\n"); + } + } + + + DRM_PROC_PRINT("\nvalues:\n"); + if (dev->lock.hw_lock) { + DRM_PROC_PRINT("lock 0x%08x\n", + dev->lock.hw_lock->lock); + } else { + DRM_PROC_PRINT("lock none\n"); + } + DRM_PROC_PRINT("context_flag 0x%08lx\n", dev->context_flag); + DRM_PROC_PRINT("interrupt_flag 0x%08lx\n", dev->interrupt_flag); + DRM_PROC_PRINT("dma_flag 0x%08lx\n", dev->dma_flag); + + DRM_PROC_PRINT("queue_count %10d\n", dev->queue_count); + DRM_PROC_PRINT("last_context %10d\n", dev->last_context); + DRM_PROC_PRINT("last_switch %10lu\n", dev->last_switch); + DRM_PROC_PRINT("last_checked %10d\n", dev->last_checked); + + + DRM_PROC_PRINT("\n q2d d2c c2f" + " q2c q2f dma sch" + " ctx lacq lhld\n\n"); + for (i = 0; i < DRM_DMA_HISTOGRAM_SLOTS; i++) { + DRM_PROC_PRINT("%s %10lu %10u %10u %10u %10u %10u" + " %10u %10u %10u %10u %10u\n", + i == DRM_DMA_HISTOGRAM_SLOTS - 1 ? ">=" : "< ", + i == DRM_DMA_HISTOGRAM_SLOTS - 1 + ? prev_value : slot_value , + + atomic_read(&dev->histo + .queued_to_dispatched[i]), + atomic_read(&dev->histo + .dispatched_to_completed[i]), + atomic_read(&dev->histo + .completed_to_freed[i]), + + atomic_read(&dev->histo + .queued_to_completed[i]), + atomic_read(&dev->histo + .queued_to_freed[i]), + atomic_read(&dev->histo.dma[i]), + atomic_read(&dev->histo.schedule[i]), + atomic_read(&dev->histo.ctx[i]), + atomic_read(&dev->histo.lacq[i]), + atomic_read(&dev->histo.lhld[i])); + prev_value = slot_value; + slot_value = DRM_DMA_HISTOGRAM_NEXT(slot_value); + } + return len; +} + +static int drm_histo_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_histo_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/proc.c linux.ac/drivers/char/drm/proc.c --- linux.vanilla/drivers/char/drm/proc.c Thu Jul 27 23:10:22 2000 +++ linux.ac/drivers/char/drm/proc.c Fri May 4 16:25:46 2001 @@ -92,6 +92,7 @@ if (!drm_dev_root) { DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name); remove_proc_entry("dri", NULL); + break; } if (drm_dev_root->nlink == 2) break; drm_dev_root = NULL; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/drm/vm-mod.c linux.ac/drivers/char/drm/vm-mod.c --- linux.vanilla/drivers/char/drm/vm-mod.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/drm/vm-mod.c Tue Apr 3 17:52:36 2001 @@ -0,0 +1,370 @@ +/* vm.c -- Memory mapping for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +struct vm_operations_struct drm_vm_ops = { + nopage: drm_vm_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +struct vm_operations_struct drm_vm_shm_ops = { + nopage: drm_vm_shm_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +struct vm_operations_struct drm_vm_shm_lock_ops = { + nopage: drm_vm_shm_nopage_lock, + open: drm_vm_open, + close: drm_vm_close, +}; + +struct vm_operations_struct drm_vm_dma_ops = { + nopage: drm_vm_dma_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + return NOPAGE_SIGBUS; /* Disallow mremap */ +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ +#if LINUX_VERSION_CODE >= 0x020300 + drm_map_t *map = (drm_map_t *)vma->vm_private_data; +#else + drm_map_t *map = (drm_map_t *)vma->vm_pte; +#endif + unsigned long physical; + unsigned long offset; + + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!map) return NOPAGE_OOM; /* Nothing allocated */ + + offset = address - vma->vm_start; + physical = (unsigned long)map->handle + offset; + atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx => 0x%08lx\n", address, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return virt_to_page(physical); +#endif +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long drm_vm_shm_nopage_lock(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *drm_vm_shm_nopage_lock(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + unsigned long physical; + unsigned long offset; + unsigned long page; + + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!dev->lock.hw_lock) return NOPAGE_OOM; /* Nothing allocated */ + + offset = address - vma->vm_start; + page = offset >> PAGE_SHIFT; + physical = (unsigned long)dev->lock.hw_lock + offset; + atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return virt_to_page(physical); +#endif +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + unsigned long physical; + unsigned long offset; + unsigned long page; + + if (!dma) return NOPAGE_SIGBUS; /* Error */ + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!dma->pagelist) return NOPAGE_OOM ; /* Nothing allocated */ + + offset = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */ + page = offset >> PAGE_SHIFT; + physical = dma->pagelist[page] + (offset & (~PAGE_MASK)); + atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return virt_to_page(physical); +#endif +} + +void drm_vm_open(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; +#if DRM_DEBUG_CODE + drm_vma_entry_t *vma_entry; +#endif + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); + atomic_inc(&dev->vma_count); +#if LINUX_VERSION_CODE < 0x020333 + /* The map can exist after the fd is closed. */ + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + + +#if DRM_DEBUG_CODE + vma_entry = drm_alloc(sizeof(*vma_entry), DRM_MEM_VMAS); + if (vma_entry) { + down(&dev->struct_sem); + vma_entry->vma = vma; + vma_entry->next = dev->vmalist; + vma_entry->pid = current->pid; + dev->vmalist = vma_entry; + up(&dev->struct_sem); + } +#endif +} + +void drm_vm_close(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; +#if DRM_DEBUG_CODE + drm_vma_entry_t *pt, *prev; +#endif + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_dec(&dev->vma_count); + +#if DRM_DEBUG_CODE + down(&dev->struct_sem); + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { + if (pt->vma == vma) { + if (prev) { + prev->next = pt->next; + } else { + dev->vmalist = pt->next; + } + drm_free(pt, sizeof(*pt), DRM_MEM_VMAS); + break; + } + } + up(&dev->struct_sem); +#endif +} + +int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + drm_device_dma_t *dma; + unsigned long length = vma->vm_end - vma->vm_start; + + lock_kernel(); + dev = priv->dev; + dma = dev->dma; + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + /* Length must match exact page count */ + if (!dma || (length >> PAGE_SHIFT) != dma->page_count) { + unlock_kernel(); + return -EINVAL; + } + unlock_kernel(); + + vma->vm_ops = &drm_vm_dma_ops; + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open(vma); + return 0; +} + +int drm_mmap(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map = NULL; + int i; + + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + if (!VM_OFFSET(vma)) return drm_mmap_dma(filp, vma); + + /* A sequential search of a linked list is + fine here because: 1) there will only be + about 5-10 entries in the list and, 2) a + DRI client only has to do this mapping + once, so it doesn't have to be optimized + for performance, even if the list was a + bit longer. */ + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + if (map->offset == VM_OFFSET(vma)) break; + } + + if (i >= dev->map_count) return -EINVAL; + if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) + return -EPERM; + + /* Check for valid size. */ + if (map->size != vma->vm_end - vma->vm_start) return -EINVAL; + + if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) { + vma->vm_flags &= VM_MAYWRITE; +#if defined(__i386__) + pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW; +#else + /* Ye gads this is ugly. With more thought + we could move this up higher and use + `protection_map' instead. */ + vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect( + __pte(pgprot_val(vma->vm_page_prot))))); +#endif + } + + switch (map->type) { + case _DRM_FRAME_BUFFER: + case _DRM_REGISTERS: + case _DRM_AGP: + if (VM_OFFSET(vma) >= __pa(high_memory)) { +#if defined(__i386__) + if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) { + pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; + pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; + } +#elif defined(__ia64__) + if (map->type != _DRM_AGP) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); +#endif + vma->vm_flags |= VM_IO; /* not in core dump */ + } + if (remap_page_range(vma->vm_start, + VM_OFFSET(vma), + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + DRM_DEBUG(" Type = %d; start = 0x%lx, end = 0x%lx," + " offset = 0x%lx\n", + map->type, + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + vma->vm_ops = &drm_vm_ops; + break; + case _DRM_SHM: + if (map->flags & _DRM_CONTAINS_LOCK) + vma->vm_ops = &drm_vm_shm_lock_ops; + else { + vma->vm_ops = &drm_vm_shm_ops; +#if LINUX_VERSION_CODE >= 0x020300 + vma->vm_private_data = (void *)map; +#else + vma->vm_pte = (unsigned long)map; +#endif + } + + /* Don't let this area swap. Change when + DRM_KERNEL advisory is supported. */ + vma->vm_flags |= VM_LOCKED; + break; + default: + return -EINVAL; /* This should never happen. */ + } + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open(vma); + return 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/dsp56k.c linux.ac/drivers/char/dsp56k.c --- linux.vanilla/drivers/char/dsp56k.c Tue Apr 3 17:32:00 2001 +++ linux.ac/drivers/char/dsp56k.c Tue May 22 17:37:31 2001 @@ -64,7 +64,7 @@ #define wait_some(n) \ { \ - current->state = TASK_INTERRUPTIBLE; \ + set_current_state(TASK_INTERRUPTIBLE); \ schedule_timeout(n); \ } @@ -149,7 +149,7 @@ static struct dsp56k_device { - int in_use; + long in_use; long maxio, timeout; int tx_wsize, rx_wsize; } dsp56k; @@ -451,10 +451,9 @@ { case DSP56K_DEV_56001: - if (dsp56k.in_use) + if (test_and_set_bit(0, &dsp56k.in_use)) return -EBUSY; - dsp56k.in_use = 1; dsp56k.timeout = TIMEOUT; dsp56k.maxio = MAXIO; dsp56k.rx_wsize = dsp56k.tx_wsize = 4; @@ -469,8 +468,7 @@ break; default: - printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev); - return -ENXIO; + return -ENODEV; } return 0; @@ -483,11 +481,7 @@ switch(dev) { case DSP56K_DEV_56001: - - lock_kernel(); - dsp56k.in_use = 0; - unlock_kernel(); - + clear_bit(0, &dsp56k.in_use); break; default: printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/dz.c linux.ac/drivers/char/dz.c --- linux.vanilla/drivers/char/dz.c Tue Apr 3 17:32:00 2001 +++ linux.ac/drivers/char/dz.c Tue Apr 3 17:54:39 2001 @@ -1436,7 +1436,7 @@ * dz_console_print () * * dz_console_print is registered for printk. - * The console_lock must be held when we get here. + * The console must be locked when we get here. * ------------------------------------------------------------------- */ static void dz_console_print (struct console *cons, diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/epca.c linux.ac/drivers/char/epca.c --- linux.vanilla/drivers/char/epca.c Sat May 26 16:53:02 2001 +++ linux.ac/drivers/char/epca.c Wed May 23 00:09:01 2001 @@ -1575,7 +1575,8 @@ cli(); if ((tty_unregister_driver(&pc_driver)) || - (tty_unregister_driver(&pc_callout))) + (tty_unregister_driver(&pc_callout)) || + (tty_unregister_driver(&pc_info))) { printk(KERN_WARNING " - DIGI : cleanup_module failed to un-register tty driver\n"); restore_flags(flags); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/generic_serial.c linux.ac/drivers/char/generic_serial.c --- linux.vanilla/drivers/char/generic_serial.c Fri Dec 29 22:35:47 2000 +++ linux.ac/drivers/char/generic_serial.c Thu May 17 20:54:13 2001 @@ -344,7 +344,7 @@ struct gs_port *port = ptr; long end_jiffies; int jiffies_to_transmit, charsleft = 0, rv = 0; - int to, rcib; + int rcib; func_enter(); @@ -368,6 +368,7 @@ return rv; } /* stop trying: now + twice the time it would normally take + seconds */ + if (timeout == 0) timeout = MAX_SCHEDULE_TIMEOUT; end_jiffies = jiffies; if (timeout != MAX_SCHEDULE_TIMEOUT) end_jiffies += port->baud?(2 * rcib * 10 * HZ / port->baud):0; @@ -376,11 +377,9 @@ gs_dprintk (GS_DEBUG_FLUSH, "now=%lx, end=%lx (%ld).\n", jiffies, end_jiffies, end_jiffies-jiffies); - to = 100; /* the expression is actually jiffies < end_jiffies, but that won't work around the wraparound. Tricky eh? */ - while (to-- && - (charsleft = gs_real_chars_in_buffer (port->tty)) && + while ((charsleft = gs_real_chars_in_buffer (port->tty)) && time_after (end_jiffies, jiffies)) { /* Units check: chars * (bits/char) * (jiffies /sec) / (bits/sec) = jiffies! @@ -1059,6 +1058,19 @@ copy_to_user(sp, &sio, sizeof(struct serial_struct)); } + +void gs_got_break(struct gs_port *port) +{ + if (port->flags & ASYNC_SAK) { + do_SAK (port->tty); + } + *(port->tty->flip.flag_buf_ptr) = TTY_BREAK; + port->tty->flip.flag_buf_ptr++; + port->tty->flip.char_buf_ptr++; + port->tty->flip.count++; +} + + EXPORT_SYMBOL(gs_put_char); EXPORT_SYMBOL(gs_write); EXPORT_SYMBOL(gs_write_room); @@ -1075,4 +1087,4 @@ EXPORT_SYMBOL(gs_init_port); EXPORT_SYMBOL(gs_setserial); EXPORT_SYMBOL(gs_getserial); - +EXPORT_SYMBOL(gs_got_break); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/h8.c linux.ac/drivers/char/h8.c --- linux.vanilla/drivers/char/h8.c Thu Jul 13 05:58:42 2000 +++ linux.ac/drivers/char/h8.c Tue May 22 17:57:33 2001 @@ -54,9 +54,9 @@ /* * Forward declarations. */ -static int h8_init(void); -int h8_display_blank(void); -int h8_display_unblank(void); +static int h8_init(void); +static int h8_display_blank(void); +static int h8_display_unblank(void); static void h8_intr(int irq, void *dev_id, struct pt_regs *regs); @@ -106,10 +106,10 @@ static char driver_version[] = "X0.0";/* no spaces */ -union intr_buf intrbuf; -int intr_buf_ptr; -union intr_buf xx; -u_char last_temp; +static union intr_buf intrbuf; +static int intr_buf_ptr; +static union intr_buf xx; +static u_char last_temp; /* * I/O Macros for register reads and writes. @@ -122,40 +122,40 @@ #define WRITE_DATA(d) H8_WRITE((d), h8_base + H8_DATA_REG_OFF) #define WRITE_CMD(d) H8_WRITE((d), h8_base + H8_CMD_REG_OFF) -unsigned int h8_base = H8_BASE_ADDR; -unsigned int h8_irq = H8_IRQ; -unsigned int h8_state = H8_IDLE; -unsigned int h8_index = -1; -unsigned int h8_enabled = 0; - -LIST_HEAD(h8_actq); -LIST_HEAD(h8_cmdq); -LIST_HEAD(h8_freeq); +static unsigned int h8_base = H8_BASE_ADDR; +static unsigned int h8_irq = H8_IRQ; +static unsigned int h8_state = H8_IDLE; +static unsigned int h8_index = -1; +static unsigned int h8_enabled = 0; + +static LIST_HEAD(h8_actq); +static LIST_HEAD(h8_cmdq); +static LIST_HEAD(h8_freeq); /* * Globals used in thermal control of Alphabook1. */ -int cpu_speed_divisor = -1; -int h8_event_mask = 0; -DECLARE_WAIT_QUEUE_HEAD(h8_monitor_wait); -unsigned int h8_command_mask = 0; -int h8_uthermal_threshold = DEFAULT_UTHERMAL_THRESHOLD; -int h8_uthermal_window = UTH_HYSTERESIS; -int h8_debug = 0xfffffdfc; -int h8_ldamp = MHZ_115; -int h8_udamp = MHZ_57; -u_char h8_current_temp = 0; -u_char h8_system_temp = 0; -int h8_sync_channel = 0; -DECLARE_WAIT_QUEUE_HEAD(h8_sync_wait); -int h8_init_performed; +static int cpu_speed_divisor = -1; +static int h8_event_mask = 0; +static DECLARE_WAIT_QUEUE_HEAD(h8_monitor_wait); +static unsigned int h8_command_mask = 0; +static int h8_uthermal_threshold = DEFAULT_UTHERMAL_THRESHOLD; +static int h8_uthermal_window = UTH_HYSTERESIS; +static int h8_debug = 0xfffffdfc; +static int h8_ldamp = MHZ_115; +static int h8_udamp = MHZ_57; +static u_char h8_current_temp = 0; +static u_char h8_system_temp = 0; +static int h8_sync_channel = 0; +static DECLARE_WAIT_QUEUE_HEAD(h8_sync_wait); +static int h8_init_performed; /* CPU speeds and clock divisor values */ -int speed_tab[6] = {230, 153, 115, 57, 28, 14}; +static int speed_tab[6] = {230, 153, 115, 57, 28, 14}; /* * H8 interrupt handler - */ + */ static void h8_intr(int irq, void *dev_id, struct pt_regs *regs) { u_char stat_reg, data_reg; @@ -377,7 +377,7 @@ } /* Called from console driver -- must make sure h8_enabled. */ -int h8_display_blank(void) +static int h8_display_blank(void) { #ifdef CONFIG_H8_DISPLAY_BLANK int error; @@ -393,7 +393,7 @@ } /* Called from console driver -- must make sure h8_enabled. */ -int h8_display_unblank(void) +static int h8_display_unblank(void) { #ifdef CONFIG_H8_DISPLAY_BLANK int error; @@ -408,8 +408,7 @@ return 0; } -int -h8_alloc_queues(void) +static int h8_alloc_queues(void) { h8_cmd_q_t *qp; unsigned long flags; @@ -419,7 +418,7 @@ GFP_KERNEL); if (!qp) { - printk("H8: could not allocate memory for command queue\n"); + printk(KERN_ERR "H8: could not allocate memory for command queue\n"); return(0); } /* add to the free queue */ @@ -560,14 +559,14 @@ { if(h8_debug & 0x200) - printk("h8_read_event_status: value 0x%x\n", intrbuf.word); + printk(KERN_DEBUG "h8_read_event_status: value 0x%x\n", intrbuf.word); /* * Power related items */ if (intrbuf.word & H8_DC_CHANGE) { if(h8_debug & 0x4) - printk("h8_read_event_status: DC_CHANGE\n"); + printk(KERN_DEBUG "h8_read_event_status: DC_CHANGE\n"); /* see if dc added or removed, set batt/dc flag, send event */ h8_set_event_mask(H8_MANAGE_BATTERY); @@ -575,7 +574,7 @@ } if (intrbuf.word & H8_POWER_BUTTON) { - printk("Power switch pressed - please wait - preparing to power + printk(KERN_CRIT "Power switch pressed - please wait - preparing to power off\n"); h8_set_event_mask(H8_POWER_BUTTON); wake_up(&h8_monitor_wait); @@ -586,7 +585,7 @@ */ if (intrbuf.word & H8_THERMAL_THRESHOLD) { if(h8_debug & 0x4) - printk("h8_read_event_status: THERMAL_THRESHOLD\n"); + printk(KERN_DEBUG "h8_read_event_status: THERMAL_THRESHOLD\n"); h8_set_event_mask(H8_MANAGE_UTHERM); wake_up(&h8_monitor_wait); } @@ -596,67 +595,67 @@ */ if (intrbuf.word & H8_DOCKING_STATION_STATUS) { if(h8_debug & 0x4) - printk("h8_read_event_status: DOCKING_STATION_STATUS\n"); + printk(KERN_DEBUG "h8_read_event_status: DOCKING_STATION_STATUS\n"); /* read_ext_status */ } if (intrbuf.word & H8_EXT_BATT_STATUS) { if(h8_debug & 0x4) - printk("h8_read_event_status: EXT_BATT_STATUS\n"); + printk(KERN_DEBUG "h8_read_event_status: EXT_BATT_STATUS\n"); } if (intrbuf.word & H8_EXT_BATT_CHARGE_STATE) { if(h8_debug & 0x4) - printk("h8_read_event_status: EXT_BATT_CHARGE_STATE\n"); + printk(KERN_DEBUG "h8_read_event_status: EXT_BATT_CHARGE_STATE\n"); } if (intrbuf.word & H8_BATT_CHANGE_OVER) { if(h8_debug & 0x4) - printk("h8_read_event_status: BATT_CHANGE_OVER\n"); + printk(KERN_DEBUG "h8_read_event_status: BATT_CHANGE_OVER\n"); } if (intrbuf.word & H8_WATCHDOG) { if(h8_debug & 0x4) - printk("h8_read_event_status: WATCHDOG\n"); + printk(KERN_DEBUG "h8_read_event_status: WATCHDOG\n"); /* nop */ } if (intrbuf.word & H8_SHUTDOWN) { if(h8_debug & 0x4) - printk("h8_read_event_status: SHUTDOWN\n"); + printk(KERN_DEBUG "h8_read_event_status: SHUTDOWN\n"); /* nop */ } if (intrbuf.word & H8_KEYBOARD) { if(h8_debug & 0x4) - printk("h8_read_event_status: KEYBOARD\n"); + printk(KERN_DEBUG "h8_read_event_status: KEYBOARD\n"); /* nop */ } if (intrbuf.word & H8_EXT_MOUSE_OR_CASE_SWITCH) { if(h8_debug & 0x4) - printk("h8_read_event_status: EXT_MOUSE_OR_CASE_SWITCH\n"); + printk(KERN_DEBUG "h8_read_event_status: EXT_MOUSE_OR_CASE_SWITCH\n"); /* read_ext_status*/ } if (intrbuf.word & H8_INT_BATT_LOW) { if(h8_debug & 0x4) - printk("h8_read_event_status: INT_BATT_LOW\n"); - /* post event, warn user */ + printk(KERN_DEBUG "h8_read_event_status: INT_BATT_LOW\n"); post + /* event, warn user */ } if (intrbuf.word & H8_INT_BATT_CHARGE_STATE) { if(h8_debug & 0x4) - printk("h8_read_event_status: INT_BATT_CHARGE_STATE\n"); + printk(KERN_DEBUG "h8_read_event_status: INT_BATT_CHARGE_STATE\n"); /* nop - happens often */ } if (intrbuf.word & H8_INT_BATT_STATUS) { if(h8_debug & 0x4) - printk("h8_read_event_status: INT_BATT_STATUS\n"); + printk(KERN_DEBUG "h8_read_event_status: INT_BATT_STATUS\n"); } if (intrbuf.word & H8_INT_BATT_CHARGE_THRESHOLD) { if(h8_debug & 0x4) - printk("h8_read_event_status: INT_BATT_CHARGE_THRESHOLD\n"); + printk(KERN_DEBUG "h8_read_event_status: INT_BATT_CHARGE_THRESHOLD\n"); /* nop - happens often */ } if (intrbuf.word & H8_EXT_BATT_LOW) { if(h8_debug & 0x4) - printk("h8_read_event_status: EXT_BATT_LOW\n"); + printk(KERN_DEBUG "h8_read_event_status: EXT_BATT_LOW\n"); /*if no internal, post event, warn user */ /* else nop */ } @@ -667,7 +666,7 @@ /* * Function called when H8 has performed requested command. */ -void +static void h8_cmd_done(h8_cmd_q_t *qp) { @@ -675,14 +674,14 @@ switch (qp->cmdbuf[0]) { case H8_SYNC: if (h8_debug & 0x40000) - printk("H8: Sync command done - byte returned was 0x%x\n", + printk(KERN_DEBUG "H8: Sync command done - byte returned was 0x%x\n", qp->rcvbuf[0]); list_add(&qp->link, &h8_freeq); break; case H8_RD_SN: case H8_RD_ENET_ADDR: - printk("H8: read Ethernet address: command done - address: %x - %x - %x - %x - %x - %x \n", + printk(KERN_DEBUG "H8: read Ethernet address: command done - address: %x - %x - %x - %x - %x - %x \n", qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2], qp->rcvbuf[3], qp->rcvbuf[4], qp->rcvbuf[5]); list_add(&qp->link, &h8_freeq); @@ -691,13 +690,13 @@ case H8_RD_HW_VER: case H8_RD_MIC_VER: case H8_RD_MAX_TEMP: - printk("H8: Max recorded CPU temp %d, Sys temp %d\n", + printk(KERN_DEBUG "H8: Max recorded CPU temp %d, Sys temp %d\n", qp->rcvbuf[0], qp->rcvbuf[1]); list_add(&qp->link, &h8_freeq); break; case H8_RD_MIN_TEMP: - printk("H8: Min recorded CPU temp %d, Sys temp %d\n", + printk(KERN_DEBUG "H8: Min recorded CPU temp %d, Sys temp %d\n", qp->rcvbuf[0], qp->rcvbuf[1]); list_add(&qp->link, &h8_freeq); break; @@ -712,11 +711,11 @@ case H8_RD_SYS_VARIENT: case H8_RD_PWR_ON_CYCLES: - printk(" H8: RD_PWR_ON_CYCLES command done\n"); + printk(KERN_DEBUG " H8: RD_PWR_ON_CYCLES command done\n"); break; case H8_RD_PWR_ON_SECS: - printk("H8: RD_PWR_ON_SECS command done\n"); + printk(KERN_DEBUG "H8: RD_PWR_ON_SECS command done\n"); break; case H8_RD_RESET_STATUS: @@ -741,7 +740,7 @@ case H8_RD_NEW_BUSY_SPEED: case H8_RD_CONFIG_INTERFACE: case H8_RD_INT_BATT_STATUS: - printk("H8: Read int batt status cmd done - returned was %x %x %x\n", + printk(KERN_DEBUG "H8: Read int batt status cmd done - returned was %x %x %x\n", qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2]); list_add(&qp->link, &h8_freeq); break; @@ -752,7 +751,7 @@ case H8_CTL_EMU_BITPORT: case H8_DEVICE_CONTROL: if(h8_debug & 0x20000) { - printk("H8: Device control cmd done - byte returned was 0x%x\n", + printk(KERN_DEBUG "H8: Device control cmd done - byte returned was 0x%x\n", qp->rcvbuf[0]); } list_add(&qp->link, &h8_freeq); @@ -767,7 +766,7 @@ case H8_CTL_MOUSE_SENSITIVITY: case H8_CTL_DIAG_MODE: case H8_CTL_IDLE_AND_BUSY_SPDS: - printk("H8: Idle and busy speed command done\n"); + printk(KERN_DEBUG "H8: Idle and busy speed command done\n"); break; case H8_CTL_TFT_BRT_BATT: @@ -808,7 +807,7 @@ case H8_BQ_RD_REG: case H8_BQ_WRT_REG: case H8_PWR_OFF: - printk ("H8: misc command completed\n"); + printk (KERN_DEBUG "H8: misc command completed\n"); break; } return; @@ -948,7 +947,7 @@ */ h8_get_curr_temp(curr_temp); - printk("H8: Initial CPU temp: %d\n", curr_temp[0]); + printk(KERN_INFO "H8: Initial CPU temp: %d\n", curr_temp[0]); if(curr_temp[0] >= h8_uthermal_threshold) { h8_set_event_mask(H8_MANAGE_UTHERM); @@ -965,7 +964,7 @@ sleep_on(&h8_monitor_wait); if(h8_debug & 0x2) - printk("h8_monitor_thread awakened, mask:%x\n", + printk(KERN_DEBUG "h8_monitor_thread awakened, mask:%x\n", h8_event_mask); if (h8_event_mask & (H8_MANAGE_UTHERM|H8_MANAGE_LTHERM)) { @@ -1008,7 +1007,7 @@ if(h8_event_mask & H8_MANAGE_UTHERM) { /* Upper thermal interrupt received, need to cool down. */ if(h8_debug & 0x10) - printk("H8: Thermal threshold %d F reached\n", + printk(KERN_WARNING "H8: Thermal threshold %d F reached\n", h8_uthermal_threshold); h8_set_cpu_speed(h8_udamp); h8_clear_event_mask(H8_MANAGE_UTHERM); @@ -1027,7 +1026,7 @@ h8_set_upper_therm_thold(h8_uthermal_threshold); h8_set_cpu_speed(h8_ldamp); /* adjustable */ if(h8_debug & 0x10) - printk("H8: CPU cool, applying cpu_divisor: %d \n", + printk(KERN_WARNING "H8: CPU cool, applying cpu_divisor: %d \n", h8_ldamp); h8_clear_event_mask(H8_MANAGE_LTHERM); } @@ -1082,7 +1081,7 @@ #endif /* NOT_YET */ if(h8_debug & 0x8) - printk("H8: Setting CPU speed to %d MHz\n", + printk(KERN_DEBUG "H8: Setting CPU speed to %d MHz\n", speed_tab[speed_divisor]); /* Make the actual speed change */ @@ -1125,7 +1124,7 @@ break; } if(h8_debug & 0x8) - printk("H8: CPU speed current setting: %d MHz\n", speed); + printk(KERN_DEBUG "H8: CPU speed current setting: %d MHz\n", speed); #endif /* NOT_YET */ return speed; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ip2/i2cmd.c linux.ac/drivers/char/ip2/i2cmd.c --- linux.vanilla/drivers/char/ip2/i2cmd.c Mon Apr 24 21:39:35 2000 +++ linux.ac/drivers/char/ip2/i2cmd.c Tue May 22 12:20:56 2001 @@ -139,7 +139,7 @@ //static UCHAR ct86[]={ 2, BTH, 0x56,0 }; // RCV_ENABLE static UCHAR ct87[] = { 1, BYP, 0x57 }; // HW_TEST //static UCHAR ct88[]={ 3, BTH, 0x58,0,0 }; // RCV_THRESHOLD -static UCHAR ct89[]={ 1, BYP, 0x59 }; // DSS_NOW +//static UCHAR ct89[]={ 1, BYP, 0x59 }; // DSS_NOW //static UCHAR ct90[]={ 3, BYP, 0x5A,0,0 }; // Set SILO //static UCHAR ct91[]={ 2, BYP, 0x5B,0 }; // timed break diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/keyboard.c linux.ac/drivers/char/keyboard.c --- linux.vanilla/drivers/char/keyboard.c Mon Oct 16 20:58:51 2000 +++ linux.ac/drivers/char/keyboard.c Tue Apr 3 17:54:40 2001 @@ -205,15 +205,12 @@ char raw_mode; pm_access(pm_kbd); - - do_poke_blanked_console = 1; - tasklet_schedule(&console_tasklet); add_keyboard_randomness(scancode | up_flag); tty = ttytab? ttytab[fg_console]: NULL; if (tty && (!tty->driver_data)) { /* - * We touch the tty structure via the the ttytab array + * We touch the tty structure via the ttytab array * without knowing whether or not tty is open, which * is inherently dangerous. We currently rely on that * fact that console_open sets tty->driver_data when @@ -233,7 +230,7 @@ * Convert scancode to keycode */ if (!kbd_translate(scancode, &keycode, raw_mode)) - return; + goto out; /* * At this point the variable `keycode' contains the keycode. @@ -252,11 +249,11 @@ #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ if (keycode == SYSRQ_KEY) { sysrq_pressed = !up_flag; - return; + goto out; } else if (sysrq_pressed) { if (!up_flag) { handle_sysrq(kbd_sysrq_xlate[keycode], kbd_pt_regs, kbd, tty); - return; + goto out; } } #endif @@ -298,7 +295,7 @@ if (type >= 0xf0) { type -= 0xf0; if (raw_mode && ! (TYPES_ALLOWED_IN_RAW_MODE & (1 << type))) - return; + goto out; if (type == KT_LETTER) { type = KT_LATIN; if (vc_kbd_led(kbd, VC_CAPSLOCK)) { @@ -322,13 +319,16 @@ compute_shiftstate(); kbd->slockstate = 0; /* play it safe */ #else - keysym = U(plain_map[keycode]); + keysym = U(key_maps[0][keycode]); type = KTYP(keysym); if (type == KT_SHIFT) (*key_handler[type])(keysym & 0xff, up_flag); #endif } } +out: + do_poke_blanked_console = 1; + schedule_console_callback(); } @@ -750,7 +750,7 @@ k = i*BITS_PER_LONG; for(j=0; j= KERNEL_VERSION(2,3,34) owner: THIS_MODULE, -#endif read: zf_read, write: zf_write, ioctl: zf_ioctl, @@ -458,19 +468,19 @@ { char *str[] = { "RESET", "SMI", "NMI", "SCI" }; - printk(PFX ": Watchdog using action = %s\n", str[act]); + printk(KERN_INFO PFX ": Watchdog using action = %s\n", str[act]); } -int __init zf_init(void) +static int __init zf_init(void) { int ret; - printk(PFX ": MachZ ZF-Logic Watchdog driver initializing.\n"); + printk(KERN_INFO PFX ": MachZ ZF-Logic Watchdog driver initializing.\n"); ret = zf_get_ZFL_version(); printk("%#x\n", ret); if((!ret) || (ret != 0xffff)){ - printk(PFX ": no ZF-Logic found\n"); + printk(KERN_WARNING PFX ": no ZF-Logic found\n"); return -ENODEV; } @@ -482,6 +492,7 @@ zf_show_action(action); spin_lock_init(&zf_lock); + spin_lock_init(&zf_port_lock); ret = misc_register(&zf_miscdev); if (ret){ @@ -490,22 +501,12 @@ goto out; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,3) - if(check_region(ZF_IOBASE, 3)){ -#else if(!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")){ -#endif - printk(KERN_ERR "cannot reserve I/O ports at %d\n", ZF_IOBASE); ret = -EBUSY; goto no_region; } - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,3) - request_region(ZF_IOBASE, 3, "MachZ ZFL WDT"); -#define __exit -#endif ret = register_reboot_notifier(&zf_notifier); if(ret){ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/nwbutton.c linux.ac/drivers/char/nwbutton.c --- linux.vanilla/drivers/char/nwbutton.c Sun Aug 13 17:54:15 2000 +++ linux.ac/drivers/char/nwbutton.c Tue May 22 18:23:20 2001 @@ -47,6 +47,8 @@ * Because callbacks can be unregistered at random the list can become * fragmented, so we need to search through the list until we find the first * free entry. + * + * FIXME: Has anyone spotted any locking functions int his code recently ?? */ int button_add_callback (void (*callback) (void), int count) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/nwflash.c linux.ac/drivers/char/nwflash.c --- linux.vanilla/drivers/char/nwflash.c Mon Sep 18 23:15:22 2000 +++ linux.ac/drivers/char/nwflash.c Tue May 22 18:28:19 2001 @@ -127,6 +127,7 @@ { int id; + /* FIXME - suppose an erase is going on ... while we are identing .. */ id = get_flash_id(); if ((id != KFLASH_ID) && (id != KFLASH_ID4)) { printk("Flash: incorrect ID 0x%04X.\n", id); @@ -183,6 +184,7 @@ read = 0; + /* FIXME - suppose an erase is going on ... */ if (copy_to_user(buf, (void *)(FLASH_BASE + p), count)) return -EFAULT; read += count; @@ -246,11 +248,11 @@ temp -= 1; if (flashdebug) - printk("FlashWrite: writing %d block(s) starting at %d.\n", temp, nBlock); + printk(KERN_DEBUG "FlashWrite: writing %d block(s) starting at %d.\n", temp, nBlock); for (; temp; temp--, nBlock++) { if (flashdebug) - printk("FlashWrite: erasing block %d.\n", nBlock); + printk(KERN_DEBUG "FlashWrite: erasing block %d.\n", nBlock); /* * first we have to erase the block(s), where we will write... @@ -265,12 +267,12 @@ if (rc) { if (flashdebug) - printk("FlashWrite: erase error %X. Aborting...\n", rc); + printk(KERN_DEBUG "FlashWrite: erase error %X. Aborting...\n", rc); break; } if (flashdebug) - printk("FlashWrite: writing offset %X, from buf %X, bytes left %X.\n", + printk(KERN_DEBUG "FlashWrite: writing offset %X, from buf %X, bytes left %X.\n", (unsigned int) p, (unsigned int) buf, count - written); /* @@ -297,7 +299,7 @@ } if (rc < 0) { if (flashdebug) - printk("FlashWrite: write error %X. Aborting...\n", rc); + printk(KERN_DEBUG "FlashWrite: write error %X. Aborting...\n", rc); break; } p += rc; @@ -306,7 +308,7 @@ file->f_pos += rc; if (flashdebug) - printk("FlashWrite: written 0x%X bytes OK.\n", written); + printk(KERN_DEBUG "FlashWrite: written 0x%X bytes OK.\n", written); } /* @@ -331,7 +333,7 @@ static long long flash_llseek(struct file *file, long long offset, int orig) { if (flashdebug) - printk("Flash_dev: flash_lseek, offset=0x%X, orig=0x%X.\n", + printk(KERN_DEBUG "Flash_dev: flash_lseek, offset=0x%X, orig=0x%X.\n", (unsigned int) offset, (unsigned int) orig); switch (orig) { @@ -441,7 +443,7 @@ */ if (c1 & 0x20) { if (flashdebug) - printk("Flash_erase: err at %X.\n", (unsigned int) pWritePtr); + printk(KERN_DEBUG "Flash_erase: err at %X.\n", (unsigned int) pWritePtr); /* * reset error */ @@ -460,7 +462,7 @@ for (temp = 0; temp < 16 * 1024; temp++, pWritePtr += 4) { if ((temp1 = *(volatile unsigned int *) pWritePtr) != 0xFFFFFFFF) { if (flashdebug) - printk("Flash_erase: verify err at %X = %X.\n", + printk(KERN_DEBUG "Flash_erase: verify err at %X = %X.\n", (unsigned int) pWritePtr, temp1); return -1; } @@ -591,7 +593,7 @@ */ if (time_before(jiffies, timeout)) { if (flashdebug) - printk("FlashWrite: Retrying write (addr=0x%X)...\n", + printk(KERN_DEBUG "FlashWrite: Retrying write (addr=0x%X)...\n", pWritePtr - FLASH_BASE); /* @@ -609,7 +611,7 @@ goto WriteRetry; } else { - printk("Timeout in flash write! (addr=0x%X) Aborting...\n", + printk(KERN_DEBUG "Timeout in flash write! (addr=0x%X) Aborting...\n", pWritePtr - FLASH_BASE); /* * return error -2 @@ -637,7 +639,7 @@ buf++; if ((c1 = *pWritePtr++) != c) { if (flashdebug) - printk("flash write verify error at 0x%X! (%02X!=%02X) Retrying...\n", + printk(KERN_DEBUG "flash write verify error at 0x%X! (%02X!=%02X) Retrying...\n", (unsigned int) pWritePtr, c1, c); return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/rio/linux_compat.h linux.ac/drivers/char/rio/linux_compat.h --- linux.vanilla/drivers/char/rio/linux_compat.h Fri Aug 11 22:51:33 2000 +++ linux.ac/drivers/char/rio/linux_compat.h Sat May 26 17:50:26 2001 @@ -16,11 +16,13 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include + #define disable(oldspl) save_flags (oldspl) #define restore(oldspl) restore_flags (oldspl) -#define sysbrk(x) kmalloc ((x), GFP_KERNEL) +#define sysbrk(x) kmalloc ((x),in_interrupt()? GFP_ATOMIC : GFP_KERNEL) #define sysfree(p,size) kfree ((p)) #define WBYTE(p,v) writeb(v, &p) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/rio/rio_linux.c linux.ac/drivers/char/rio/rio_linux.c --- linux.vanilla/drivers/char/rio/rio_linux.c Mon Apr 30 15:13:14 2001 +++ linux.ac/drivers/char/rio/rio_linux.c Fri May 25 00:01:14 2001 @@ -58,6 +58,7 @@ #include #include #include +#include #include #include @@ -165,7 +166,7 @@ /* startuptime */ HZ*2, /* how long to wait for card to run */ /* slowcook */ 0, /* TRUE -> always use line disc. */ /* intrpolltime */ 1, /* The frequency of OUR polls */ - /* breakinterval */ 25, /* x10 mS */ + /* breakinterval */ 25, /* x10 mS XXX: units seem to be 1ms not 10! -- REW*/ /* timer */ 10, /* mS */ /* RtaLoadBase */ 0x7000, /* HostLoadBase */ 0x7C00, @@ -203,11 +204,8 @@ unsigned int cmd, unsigned long arg); static int rio_init_drivers(void); - void my_hd (void *addr, int len); - - static struct tty_driver rio_driver, rio_callout_driver; static struct tty_driver rio_driver2, rio_callout_driver2; @@ -248,14 +246,12 @@ long rio_irqmask = -1; #ifndef TWO_ZERO -#ifdef MODULE MODULE_AUTHOR("Rogier Wolff , Patrick van de Lageweg "); MODULE_DESCRIPTION("RIO driver"); MODULE_PARM(rio_poll, "i"); MODULE_PARM(rio_debug, "i"); MODULE_PARM(rio_irqmask, "i"); #endif -#endif static struct real_driver rio_real_driver = { rio_disable_tx_interrupts, @@ -383,8 +379,8 @@ int rio_ismodem (kdev_t device) { - return (MAJOR (device) != RIO_NORMAL_MAJOR0) && - (MAJOR (device) != RIO_NORMAL_MAJOR1); + return (MAJOR (device) == RIO_NORMAL_MAJOR0) || + (MAJOR (device) == RIO_NORMAL_MAJOR1); } @@ -455,7 +451,6 @@ func_enter (); HostP = (struct Host*)ptr; /* &p->RIOHosts[(long)ptr]; */ - rio_dprintk (RIO_DEBUG_IFLOW, "rio: enter rio_interrupt (%d/%d)\n", irq, HostP->Ivec); @@ -624,8 +619,12 @@ /* Nothing special here... */ static void rio_shutdown_port (void * ptr) { + struct Port *PortP; + func_enter(); + PortP = (struct Port *)ptr; + PortP->gs.tty = NULL; #if 0 port->gs.flags &= ~ GS_ACTIVE; if (!port->gs.tty) { @@ -654,8 +653,14 @@ exit minicom. I expect an "oops". -- REW */ static void rio_hungup (void *ptr) { - func_enter (); + struct Port *PortP; + + func_enter(); + + PortP = (struct Port *)ptr; + PortP->gs.tty = NULL; rio_dec_mod_count (); + func_exit (); } @@ -679,9 +684,8 @@ PortP->gs.count = 0; } - + PortP->gs.tty = NULL; rio_dec_mod_count (); - func_exit (); } @@ -700,24 +704,28 @@ return rc; } +extern int RIOShortCommand(struct rio_info *p, struct Port *PortP, + int command, int len, int arg); static int rio_ioctl (struct tty_struct * tty, struct file * filp, unsigned int cmd, unsigned long arg) { -#if 0 int rc; - struct rio_port *port = tty->driver_data; + struct Port *PortP; int ival; - /* func_enter2(); */ + func_enter(); + PortP = (struct Port *)tty->driver_data; rc = 0; switch (cmd) { +#if 0 case TIOCGSOFTCAR: rc = Put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), (unsigned int *) arg); break; +#endif case TIOCSSOFTCAR: if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(int))) == 0) { @@ -730,13 +738,39 @@ case TIOCGSERIAL: if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0) - gs_getserial(&port->gs, (struct serial_struct *) arg); + gs_getserial(&PortP->gs, (struct serial_struct *) arg); + break; + case TCSBRK: + if ( PortP->State & RIO_DELETED ) { + rio_dprintk (RIO_DEBUG_TTY, "BREAK on deleted RTA\n"); + rc = -EIO; + } else { + if (RIOShortCommand(p, PortP, SBREAK, 2, 250) == RIO_FAIL) { + rio_dprintk (RIO_DEBUG_INTR, "SBREAK RIOShortCommand failed\n"); + rc = -EIO; + } + } + break; + case TCSBRKP: + if ( PortP->State & RIO_DELETED ) { + rio_dprintk (RIO_DEBUG_TTY, "BREAK on deleted RTA\n"); + rc = -EIO; + } else { + int l; + l = arg?arg*100:250; + if (l > 255) l = 255; + if (RIOShortCommand(p, PortP, SBREAK, 2, arg?arg*100:250) == RIO_FAIL) { + rio_dprintk (RIO_DEBUG_INTR, "SBREAK RIOShortCommand failed\n"); + rc = -EIO; + } + } break; case TIOCSSERIAL: if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0) - rc = gs_setserial(&port->gs, (struct serial_struct *) arg); + rc = gs_setserial(&PortP->gs, (struct serial_struct *) arg); break; +#if 0 case TIOCMGET: if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) { @@ -768,17 +802,13 @@ ((ival & TIOCM_RTS) ? 1 : 0)); } break; - +#endif default: rc = -ENOIOCTLCMD; break; } - /* func_exit(); */ + func_exit(); return rc; -#else - return -ENOIOCTLCMD; -#endif - } @@ -1029,17 +1059,15 @@ free4:kfree (rio_termios); free3:kfree (p->RIOPortp); free2:kfree (p->RIOHosts); - free1:kfree (p); - free0: - if (p) { - rio_dprintk (RIO_DEBUG_INIT, "Not enough memory! %p %p %p %p %p\n", + free1: + rio_dprintk (RIO_DEBUG_INIT, "Not enough memory! %p %p %p %p %p\n", p, p->RIOHosts, p->RIOPortp, rio_termios, rio_termios); - } + kfree(p); + free0: return -ENOMEM; } -#ifdef MODULE -static void rio_release_drivers(void) +static void __exit rio_release_drivers(void) { func_enter(); tty_unregister_driver (&rio_callout_driver2); @@ -1048,7 +1076,6 @@ tty_unregister_driver (&rio_driver); func_exit(); } -#endif #ifdef TWO_ZERO #define PDEV unsigned char pci_bus, unsigned pci_fun @@ -1101,11 +1128,7 @@ #endif -#ifdef MODULE -#define rio_init init_module -#endif - -int rio_init(void) +static int __init rio_init(void) { int found = 0; int i; @@ -1255,6 +1278,7 @@ hp->Type = RIO_PCI; hp->Copy = rio_pcicopy; hp->Mode = RIO_PCI_BOOT_FROM_RAM; + hp->HostLock = SPIN_LOCK_UNLOCKED; rio_dprintk (RIO_DEBUG_PROBE, "Ivec: %x\n", hp->Ivec); rio_dprintk (RIO_DEBUG_PROBE, "Mode: %x\n", hp->Mode); @@ -1310,6 +1334,7 @@ * Moreover, the ISA card will work with the * special PCI copy anyway. -- REW */ hp->Mode = 0; + hp->HostLock = SPIN_LOCK_UNLOCKED; vpdp = get_VPD_PROM (hp); rio_dprintk (RIO_DEBUG_PROBE, "Got VPD ROM\n"); @@ -1388,8 +1413,7 @@ } -#ifdef MODULE -void cleanup_module(void) +static void __exit rio_exit (void) { int i; struct Host *hp; @@ -1424,9 +1448,9 @@ func_exit(); } -#endif - +module_init(rio_init); +module_exit(rio_exit); /* * Anybody who knows why this doesn't work for me, please tell me -- REW. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/rio/rio_linux.h linux.ac/drivers/char/rio/rio_linux.h --- linux.vanilla/drivers/char/rio/rio_linux.h Mon Dec 11 20:50:43 2000 +++ linux.ac/drivers/char/rio/rio_linux.h Thu May 17 20:54:13 2001 @@ -95,28 +95,27 @@ #if 1 #define rio_spin_lock_irqsave(sem, flags) do { \ rio_dprintk (RIO_DEBUG_SPINLOCK, "spinlockirqsave: %p %s:%d\n", \ - sem, __FILE__, __LINE__);\ - spin_lock_irqsave(sem, flags);\ - } while (0) + sem, __FILE__, __LINE__);\ + spin_lock_irqsave(sem, flags);\ + } while (0) #define rio_spin_unlock_irqrestore(sem, flags) do { \ rio_dprintk (RIO_DEBUG_SPINLOCK, "spinunlockirqrestore: %p %s:%d\n",\ - sem, __FILE__, __LINE__);\ - spin_unlock_irqrestore(sem, flags);\ - } while (0) - + sem, __FILE__, __LINE__);\ + spin_unlock_irqrestore(sem, flags);\ + } while (0) #define rio_spin_lock(sem) do { \ rio_dprintk (RIO_DEBUG_SPINLOCK, "spinlock: %p %s:%d\n",\ - sem, __FILE__, __LINE__);\ - spin_lock(sem);\ - } while (0) + sem, __FILE__, __LINE__);\ + spin_lock(sem);\ + } while (0) #define rio_spin_unlock(sem) do { \ rio_dprintk (RIO_DEBUG_SPINLOCK, "spinunlock: %p %s:%d\n",\ - sem, __FILE__, __LINE__);\ - spin_unlock(sem);\ - } while (0) + sem, __FILE__, __LINE__);\ + spin_unlock(sem);\ + } while (0) #else #define rio_spin_lock_irqsave(sem, flags) \ spin_lock_irqsave(sem, flags) @@ -165,7 +164,7 @@ #define rio_memcpy_fromio memcpy_fromio #endif -#define DEBUG +#define DEBUG 1 /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/rio/rioboot.c linux.ac/drivers/char/rio/rioboot.c --- linux.vanilla/drivers/char/rio/rioboot.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/char/rio/rioboot.c Thu May 17 20:54:13 2001 @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/rio/riocmd.c linux.ac/drivers/char/rio/riocmd.c --- linux.vanilla/drivers/char/rio/riocmd.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/char/rio/riocmd.c Thu May 17 20:54:13 2001 @@ -474,17 +474,18 @@ rio_spin_lock_irqsave(&PortP->portSem, flags); switch( RBYTE(PktCmdP->Command) ) { case BREAK_RECEIVED: - rio_dprintk (RIO_DEBUG_CMD, "Received a break!\n"); + rio_dprintk (RIO_DEBUG_CMD, "Received a break!\n"); /* If the current line disc. is not multi-threading and the current processor is not the default, reset rup_intr and return FALSE to ensure that the command packet is not freed. */ /* Call tmgr HANGUP HERE */ /* Fix this later when every thing works !!!! RAMRAJ */ + gs_got_break (PortP); break; case COMPLETE: - rio_dprintk (RIO_DEBUG_CMD, "Command complete on phb %d host %d\n", + rio_dprintk (RIO_DEBUG_CMD, "Command complete on phb %d host %d\n", RBYTE(PktCmdP->PhbNum), HostP-p->RIOHosts); subCommand = 1; switch (RBYTE(PktCmdP->SubCommand)) { @@ -549,6 +550,8 @@ */ if (PortP->gs.tty == NULL) break; + if (PortP->gs.tty->termios == NULL) + break; if (!(PortP->gs.tty->termios->c_cflag & CLOCAL) && ((PortP->State & (RIO_MOPEN|RIO_WOPEN)))) { @@ -623,7 +626,8 @@ struct CmdBlk *CmdBlkP; CmdBlkP = (struct CmdBlk *)sysbrk(sizeof(struct CmdBlk)); - bzero(CmdBlkP, sizeof(struct CmdBlk)); + if (CmdBlkP) + bzero(CmdBlkP, sizeof(struct CmdBlk)); return CmdBlkP; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/rio/riodrvr.h linux.ac/drivers/char/rio/riodrvr.h --- linux.vanilla/drivers/char/rio/riodrvr.h Thu May 11 00:56:44 2000 +++ linux.ac/drivers/char/rio/riodrvr.h Tue Apr 24 00:01:18 2001 @@ -33,12 +33,13 @@ #ifndef __riodrvr_h #define __riodrvr_h +#include /* for HZ */ + #ifdef SCCS_LABELS static char *_riodrvr_h_sccs_ = "@(#)riodrvr.h 1.3"; #endif #define MEMDUMP_SIZE 32 -#define HZ 100 #define MOD_DISABLE (RIO_NOREAD|RIO_NOWRITE|RIO_NOXPRINT) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/rio/rioinit.c linux.ac/drivers/char/rio/rioinit.c --- linux.vanilla/drivers/char/rio/rioinit.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/char/rio/rioinit.c Thu May 17 20:54:13 2001 @@ -1446,7 +1446,7 @@ } RIODefaultName(p, HostP, rup); } - HostP->UnixRups[rup].RupLock = -1; + HostP->UnixRups[rup].RupLock = SPIN_LOCK_UNLOCKED; } } } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/rio/riotable.c linux.ac/drivers/char/rio/riotable.c --- linux.vanilla/drivers/char/rio/riotable.c Sat Feb 17 00:02:36 2001 +++ linux.ac/drivers/char/rio/riotable.c Thu May 17 20:54:13 2001 @@ -37,6 +37,8 @@ #include #include #include +#include + #include #include #include diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/rio/riotty.c linux.ac/drivers/char/rio/riotty.c --- linux.vanilla/drivers/char/rio/riotty.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/char/rio/riotty.c Thu May 17 20:54:13 2001 @@ -96,7 +96,7 @@ #endif static void RIOClearUp(struct Port *PortP); -static int RIOShortCommand(struct rio_info *p, struct Port *PortP, +int RIOShortCommand(struct rio_info *p, struct Port *PortP, int command, int len, int arg); @@ -452,8 +452,11 @@ PortP->gs.tty->termios->c_state |= WOPEN; */ PortP->State |= RIO_WOPEN; + rio_spin_unlock_irqrestore(&PortP->portSem, flags); + if (RIODelay (PortP, HUNDRED_MS) == RIO_FAIL) #if 0 if ( sleep((caddr_t)&tp->tm.c_canqo, TTIPRI|PCATCH)) +#endif { /* ** ACTION: verify that this is a good thing @@ -471,7 +474,6 @@ func_exit (); return -EINTR; } -#endif } PortP->State &= ~RIO_WOPEN; } @@ -527,8 +529,10 @@ #endif struct Port *PortP =ptr; /* pointer to the port structure */ int deleted = 0; - int try = 25; - int repeat_this = 0xff; + int try = -1; /* Disable the timeouts by setting them to -1 */ + int repeat_this = -1; /* Congrats to those having 15 years of + uptime! (You get to break the driver.) */ + long end_time; struct tty_struct * tty; unsigned long flags; int Modem; @@ -541,6 +545,12 @@ /* tp = PortP->TtyP;*/ /* Get tty */ tty = PortP->gs.tty; rio_dprintk (RIO_DEBUG_TTY, "TTY is at address 0x%x\n",(int)tty); + + if (PortP->gs.closing_wait) + end_time = jiffies + PortP->gs.closing_wait; + else + end_time = jiffies + MAX_SCHEDULE_TIMEOUT; + Modem = rio_ismodem(tty->device); #if 0 /* What F.CKING cache? Even then, a higly idle multiprocessor, @@ -573,7 +583,8 @@ ** clear the open bits for this device */ PortP->State &= (Modem ? ~RIO_MOPEN : ~RIO_LOPEN); - + PortP->State &= ~RIO_CARR_ON; + PortP->ModemState &= ~MSVR1_CD; /* ** If the device was open as both a Modem and a tty line ** then we need to wimp out here, as the port has not really @@ -605,7 +616,6 @@ */ rio_dprintk (RIO_DEBUG_TTY, "Timeout 1 starts\n"); -#if 0 if (!deleted) while ( (PortP->InUse != NOT_INUSE) && !p->RIOHalted && (PortP->TxBufferIn != PortP->TxBufferOut) ) { @@ -626,7 +636,7 @@ } rio_spin_lock_irqsave(&PortP->portSem, flags); } -#endif + PortP->TxBufferIn = PortP->TxBufferOut = 0; repeat_this = 0xff; @@ -662,7 +672,7 @@ if (!deleted) while (try && (PortP->PortState & PORT_ISOPEN)) { try--; - if (try == 0) { + if (time_after (jiffies, end_time)) { rio_dprintk (RIO_DEBUG_TTY, "Run out of tries - force the bugger shut!\n" ); RIOPreemptiveCmd(p, PortP,FCLOSE); break; @@ -674,7 +684,11 @@ RIOClearUp( PortP ); goto close_end; } - RIODelay_ni(PortP, HUNDRED_MS); + if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) { + rio_dprintk (RIO_DEBUG_TTY, "RTA EINTR in delay \n"); + RIOPreemptiveCmd(p, PortP,FCLOSE); + break; + } } rio_spin_lock_irqsave(&PortP->portSem, flags); rio_dprintk (RIO_DEBUG_TTY, "Close: try was %d on completion\n", try ); @@ -779,7 +793,7 @@ ** Other values of len aren't allowed, and will cause ** a panic. */ -static int RIOShortCommand(struct rio_info *p, struct Port *PortP, +int RIOShortCommand(struct rio_info *p, struct Port *PortP, int command, int len, int arg) { PKT *PacketP; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ser_a2232.c linux.ac/drivers/char/ser_a2232.c --- linux.vanilla/drivers/char/ser_a2232.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/ser_a2232.c Thu May 17 21:48:34 2001 @@ -0,0 +1,880 @@ +/* drivers/char/ser_a2232.c */ + +/* $Id: ser_a2232.c,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */ + +/* Linux serial driver for the Amiga A2232 board */ + +/* This driver is MAINTAINED. Before applying any changes, please contact + * the author. + */ + +/* Copyright (c) 2000-2001 Enver Haase + * alias The A2232 driver project + * All rights reserved. + * + * 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. + * + */ +/***************************** Documentation ************************/ +/* + * This driver is in EXPERIMENTAL state. That means I could not find + * someone with five A2232 boards with 35 ports running at 19200 bps + * at the same time and test the machine's behaviour. + * However, I know that you can performance-tweak this driver (see + * the source code). + * One thing to consider is the time this driver consumes during the + * Amiga's vertical blank interrupt. Everything that is to be done + * _IS DONE_ when entering the vertical blank interrupt handler of + * this driver. + * However, it would be more sane to only do the job for only ONE card + * instead of ALL cards at a time; or, more generally, to handle only + * SOME ports instead of ALL ports at a time. + * However, as long as no-one runs into problems I guess I shouldn't + * change the driver as it runs fine for me :) . + * + * Version history of this file: + * 0.4 Resolved licensing issues. + * 0.3 Inclusion in the Linux/m68k tree, small fixes. + * 0.2 Added documentation, minor typo fixes. + * 0.1 Initial release. + * + * TO DO: + * - Handle incoming BREAK events. I guess "Stevens: Advanced + * Programming in the UNIX(R) Environment" is a good reference + * on what is to be done. + * - When installing as a module, don't simply 'printk' text, but + * send it to the TTY used by the user. + * + * THANKS TO: + * - Jukka Marin (65EC02 code). + * - The other NetBSD developers on whose A2232 driver I had a + * pretty close look. However, I didn't copy any code so it + * is okay to put my code under the GPL and include it into + * Linux. + */ +/***************************** End of Documentation *****************/ + +/***************************** Defines ******************************/ +/* + * Enables experimental 115200 (normal) 230400 (turbo) baud rate. + * The A2232 specification states it can only operate at speeds up to + * 19200 bits per second, and I was not able to send a file via + * "sz"/"rz" and a null-modem cable from one A2232 port to another + * at 115200 bits per second. + * However, this might work for you. + */ +#undef A2232_SPEEDHACK +/* + * Default is not to use RTS/CTS so you could be talked to death. + */ +#define A2232_SUPPRESS_RTSCTS_WARNING +/************************* End of Defines ***************************/ + +/***************************** Includes *****************************/ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "ser_a2232.h" +#include "ser_a2232fw.h" +/************************* End of Includes **************************/ + +/***************************** Prototypes ***************************/ +/* Helper functions */ +static __inline__ volatile struct a2232status *a2232stat(unsigned int board, + unsigned int portonboard); +static __inline__ volatile struct a2232memory *a2232mem (unsigned int board); +static __inline__ void a2232_receive_char( struct a2232_port *port, + int ch, int err ); +/* The interrupt service routine */ +static void a2232_vbl_inter(int irq, void *data, struct pt_regs *fp); +/* Initialize the port structures */ +static void a2232_init_portstructs(void); +/* Initialize and register TTY drivers. */ +/* returns 0 IFF successful */ +static int a2232_init_drivers(void); +/* Initialize all A2232 boards; main entry point. */ +int a2232board_init(void); + +/* BEGIN GENERIC_SERIAL PROTOTYPES */ +static void a2232_disable_tx_interrupts(void *ptr); +static void a2232_enable_tx_interrupts(void *ptr); +static void a2232_disable_rx_interrupts(void *ptr); +static void a2232_enable_rx_interrupts(void *ptr); +static int a2232_get_CD(void *ptr); +static void a2232_shutdown_port(void *ptr); +static int a2232_set_real_termios(void *ptr); +static int a2232_chars_in_buffer(void *ptr); +static void a2232_close(void *ptr); +static void a2232_hungup(void *ptr); +/* static void a2232_getserial (void *ptr, struct serial_struct *sp); */ +/* END GENERIC_SERIAL PROTOTYPES */ + +/* Functions that the TTY driver struct expects */ +static int a2232_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); +static void a2232_throttle(struct tty_struct *tty); +static void a2232_unthrottle(struct tty_struct *tty); +static int a2232_open(struct tty_struct * tty, struct file * filp); +/************************* End of Prototypes ************************/ + +/***************************** Global variables *********************/ +/*--------------------------------------------------------------------------- + * Interface from generic_serial.c back here + *--------------------------------------------------------------------------*/ +static struct real_driver a2232_real_driver = { + a2232_disable_tx_interrupts, + a2232_enable_tx_interrupts, + a2232_disable_rx_interrupts, + a2232_enable_rx_interrupts, + a2232_get_CD, + a2232_shutdown_port, + a2232_set_real_termios, + a2232_chars_in_buffer, + a2232_close, + a2232_hungup, + NULL /* a2232_getserial */ +}; + +static void *a2232_driver_ID = &a2232_driver_ID; // Some memory address WE own. + +/* Ports structs */ +static struct a2232_port a2232_ports[MAX_A2232_BOARDS*NUMLINES]; + +/* TTY driver structs */ +static struct tty_driver a2232_driver; +static struct tty_driver a2232_callout_driver; + +/* Variables used by the TTY driver */ +static int a2232_refcount; +static struct tty_struct *a2232_table[MAX_A2232_BOARDS*NUMLINES] = { NULL, }; +static struct termios *a2232_termios[MAX_A2232_BOARDS*NUMLINES]; +static struct termios *a2232_termios_locked[MAX_A2232_BOARDS*NUMLINES]; + +/* nr of cards completely (all ports) and correctly configured */ +static int nr_a2232; + +/* zorro_dev structs for the A2232's */ +static struct zorro_dev *zd_a2232[MAX_A2232_BOARDS]; +/***************************** End of Global variables **************/ + +/***************************** Functions ****************************/ +/*** BEGIN OF REAL_DRIVER FUNCTIONS ***/ + +static void a2232_disable_tx_interrupts(void *ptr) +{ + struct a2232_port *port; + volatile struct a2232status *stat; + unsigned long flags; + + port = ptr; + stat = a2232stat(port->which_a2232, port->which_port_on_a2232); + stat->OutDisable = -1; + + /* Does this here really have to be? */ + save_flags(flags); + cli(); + port->gs.flags &= ~GS_TX_INTEN; + restore_flags(flags); +} + +static void a2232_enable_tx_interrupts(void *ptr) +{ + struct a2232_port *port; + volatile struct a2232status *stat; + unsigned long flags; + + port = ptr; + stat = a2232stat(port->which_a2232, port->which_port_on_a2232); + stat->OutDisable = 0; + + /* Does this here really have to be? */ + save_flags(flags); + cli(); + port->gs.flags |= GS_TX_INTEN; + restore_flags(flags); +} + +static void a2232_disable_rx_interrupts(void *ptr) +{ + struct a2232_port *port; + port = ptr; + port->disable_rx = -1; +} + +static void a2232_enable_rx_interrupts(void *ptr) +{ + struct a2232_port *port; + port = ptr; + port->disable_rx = 0; +} + +static int a2232_get_CD(void *ptr) +{ + return ((struct a2232_port *) ptr)->cd_status; +} + +static void a2232_shutdown_port(void *ptr) +{ + struct a2232_port *port; + volatile struct a2232status *stat; + unsigned long flags; + + port = ptr; + stat = a2232stat(port->which_a2232, port->which_port_on_a2232); + + save_flags(flags); + cli(); + + port->gs.flags &= ~GS_ACTIVE; + + if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) { + /* Set DTR and RTS to Low, flush output. + The NetBSD driver "msc.c" does it this way. */ + stat->Command = ( (stat->Command & ~A2232CMD_CMask) | + A2232CMD_Close ); + stat->OutFlush = -1; + stat->Setup = -1; + } + + restore_flags(flags); + + /* After analyzing control flow, I think a2232_shutdown_port + is actually the last call from the system when at application + level someone issues a "echo Hello >>/dev/ttyY0". + Therefore I think the MOD_DEC_USE_COUNT should be here and + not in "a2232_close()". See the comment in "sx.c", too. + If you run into problems, compile this driver into the + kernel instead of compiling it as a module. */ + MOD_DEC_USE_COUNT; +} + +static int a2232_set_real_termios(void *ptr) +{ + unsigned int cflag, baud, chsize, stopb, parity, softflow; + int rate; + int a2232_param, a2232_cmd; + unsigned long flags; + unsigned int i; + struct a2232_port *port = ptr; + volatile struct a2232status *status; + volatile struct a2232memory *mem; + + if (!port->gs.tty || !port->gs.tty->termios) return 0; + + status = a2232stat(port->which_a2232, port->which_port_on_a2232); + mem = a2232mem(port->which_a2232); + + a2232_param = a2232_cmd = 0; + + // get baud rate + baud = port->gs.baud; + if (baud == 0) { + /* speed == 0 -> drop DTR, do nothing else */ + save_flags(flags); + cli(); + // Clear DTR (and RTS... mhhh). + status->Command = ( (status->Command & ~A2232CMD_CMask) | + A2232CMD_Close ); + status->OutFlush = -1; + status->Setup = -1; + + restore_flags(flags); + return 0; + } + + rate = A2232_BAUD_TABLE_NOAVAIL; + for (i=0; i < A2232_BAUD_TABLE_NUM_RATES * 3; i += 3){ + if (a2232_baud_table[i] == baud){ + if (mem->Common.Crystal == A2232_TURBO) rate = a2232_baud_table[i+2]; + else rate = a2232_baud_table[i+1]; + } + } + if (rate == A2232_BAUD_TABLE_NOAVAIL){ + printk("a2232: Board %d Port %d unsupported baud rate: %d baud. Using another.\n",port->which_a2232,port->which_port_on_a2232,baud); + // This is useful for both (turbo or normal) Crystal versions. + rate = A2232PARAM_B9600; + } + a2232_param |= rate; + + cflag = port->gs.tty->termios->c_cflag; + + // get character size + chsize = cflag & CSIZE; + switch (chsize){ + case CS8: a2232_param |= A2232PARAM_8Bit; break; + case CS7: a2232_param |= A2232PARAM_7Bit; break; + case CS6: a2232_param |= A2232PARAM_6Bit; break; + case CS5: a2232_param |= A2232PARAM_5Bit; break; + default: printk("a2232: Board %d Port %d unsupported character size: %d. Using 8 data bits.\n", + port->which_a2232,port->which_port_on_a2232,chsize); + a2232_param |= A2232PARAM_8Bit; break; + } + + // get number of stop bits + stopb = cflag & CSTOPB; + if (stopb){ // two stop bits instead of one + printk("a2232: Board %d Port %d 2 stop bits unsupported. Using 1 stop bit.\n", + port->which_a2232,port->which_port_on_a2232); + } + + // Warn if RTS/CTS not wanted + if (!(cflag & CRTSCTS)){ +#ifndef A2232_SUPPRESS_RTSCTS_WARNING + printk("a2232: Board %d Port %d cannot switch off firmware-implemented RTS/CTS hardware flow control.\n", + port->which_a2232,port->which_port_on_a2232); +#endif + } + + /* I think this is correct. + However, IXOFF means _input_ flow control and I wonder + if one should care about IXON _output_ flow control, + too. If this makes problems, one should turn the A2232 + firmware XON/XOFF "SoftFlow" flow control off and use + the conventional way of inserting START/STOP characters + by hand in throttle()/unthrottle(). + */ + softflow = !!( port->gs.tty->termios->c_iflag & IXOFF ); + + // get Parity (Enabled/Disabled? If Enabled, Odd or Even?) + parity = cflag & (PARENB | PARODD); + if (parity & PARENB){ + if (parity & PARODD){ + a2232_cmd |= A2232CMD_OddParity; + } + else{ + a2232_cmd |= A2232CMD_EvenParity; + } + } + else a2232_cmd |= A2232CMD_NoParity; + + + /* Hmm. Maybe an own a2232_port structure + member would be cleaner? */ + if (cflag & CLOCAL) + port->gs.flags &= ~ASYNC_CHECK_CD; + else + port->gs.flags |= ASYNC_CHECK_CD; + + + /* Now we have all parameters and can go to set them: */ + save_flags(flags); + cli(); + + status->Param = a2232_param | A2232PARAM_RcvBaud; + status->Command = a2232_cmd | A2232CMD_Open | A2232CMD_Enable; + status->SoftFlow = softflow; + status->OutDisable = 0; + status->Setup = -1; + + restore_flags(flags); + return 0; +} + +static int a2232_chars_in_buffer(void *ptr) +{ + struct a2232_port *port; + volatile struct a2232status *status; + unsigned char ret; /* we need modulo-256 arithmetics */ + port = ptr; + status = a2232stat(port->which_a2232, port->which_port_on_a2232); +#if A2232_IOBUFLEN != 256 +#error "Re-Implement a2232_chars_in_buffer()!" +#endif + ret = (status->OutHead - status->OutTail); + return ret; +} + +static void a2232_close(void *ptr) +{ + a2232_disable_tx_interrupts(ptr); + a2232_disable_rx_interrupts(ptr); + /* see the comment in a2232_shutdown_port above. */ + /* MOD_DEC_USE_COUNT; */ +} + +static void a2232_hungup(void *ptr) +{ + a2232_close(ptr); +} +/*** END OF REAL_DRIVER FUNCTIONS ***/ + +/*** BEGIN FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/ +static int a2232_ioctl( struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + +static void a2232_throttle(struct tty_struct *tty) +{ +/* Throttle: System cannot take another chars: Drop RTS or + send the STOP char or whatever. + The A2232 firmware does RTS/CTS anyway, and XON/XOFF + if switched on. So the only thing we can do at this + layer here is not taking any characters out of the + A2232 buffer any more. */ + struct a2232_port *port = (struct a2232_port *) tty->driver_data; + port->throttle_input = -1; +} + +static void a2232_unthrottle(struct tty_struct *tty) +{ +/* Unthrottle: dual to "throttle()" above. */ + struct a2232_port *port = (struct a2232_port *) tty->driver_data; + port->throttle_input = 0; +} + +static int a2232_open(struct tty_struct * tty, struct file * filp) +{ +/* More or less stolen from other drivers. */ + int line; + int retval; + struct a2232_port *port; + + line = MINOR(tty->device); + port = &a2232_ports[line]; + + tty->driver_data = port; + port->gs.tty = tty; + port->gs.count++; + retval = gs_init_port(&port->gs); + if (retval) { + port->gs.count--; + return retval; + } + port->gs.flags |= GS_ACTIVE; + if (port->gs.count == 1) { + MOD_INC_USE_COUNT; + } + retval = gs_block_til_ready(port, filp); + + if (retval) { + MOD_DEC_USE_COUNT; + port->gs.count--; + return retval; + } + + if ((port->gs.count == 1) && (port->gs.flags & ASYNC_SPLIT_TERMIOS)){ + if (tty->driver.subtype == A2232_TTY_SUBTYPE_NORMAL) + *tty->termios = port->gs.normal_termios; + else + *tty->termios = port->gs.callout_termios; + a2232_set_real_termios (port); + } + + port->gs.session = current->session; + port->gs.pgrp = current->pgrp; + + a2232_enable_rx_interrupts(port); + + return 0; +} +/*** END OF FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/ + +static __inline__ volatile struct a2232status *a2232stat(unsigned int board, unsigned int portonboard) +{ + volatile struct a2232memory *mem = a2232mem(board); + return &(mem->Status[portonboard]); +} + +static __inline__ volatile struct a2232memory *a2232mem (unsigned int board) +{ + return (volatile struct a2232memory *) ZTWO_VADDR( zd_a2232[board]->resource.start ); +} + +static __inline__ void a2232_receive_char( struct a2232_port *port, + int ch, int err ) +{ +/* Mostly stolen from other drivers. + Maybe one could implement a more efficient version by not only + transferring one character at a time. +*/ + struct tty_struct *tty = port->gs.tty; + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; + + tty->flip.count++; + +#if 0 + switch(err) { + case TTY_BREAK: + break; + case TTY_PARITY: + break; + case TTY_OVERRUN: + break; + case TTY_FRAME: + break; + } +#endif + + *tty->flip.flag_buf_ptr++ = err; + *tty->flip.char_buf_ptr++ = ch; + tty_flip_buffer_push(tty); +} + +static void a2232_vbl_inter(int irq, void *data, struct pt_regs *fp) +{ +#if A2232_IOBUFLEN != 256 +#error "Re-Implement a2232_vbl_inter()!" +#endif + +struct a2232_port *port; +volatile struct a2232memory *mem; +volatile struct a2232status *status; +unsigned char newhead; +unsigned char bufpos; /* Must be unsigned char. We need the modulo-256 arithmetics */ +unsigned char ncd, ocd, ccd; /* names consistent with the NetBSD driver */ +volatile u_char *ibuf, *cbuf, *obuf; +int ch, err, n, p; + for (n = 0; n < nr_a2232; n++){ /* for every completely initialized A2232 board */ + mem = a2232mem(n); + for (p = 0; p < NUMLINES; p++){ /* for every port on this board */ + err = 0; + port = &a2232_ports[n*NUMLINES+p]; + if ( port->gs.flags & GS_ACTIVE ){ /* if the port is used */ + + status = a2232stat(n,p); + + if (!port->disable_rx && !port->throttle_input){ /* If input is not disabled */ + newhead = status->InHead; /* 65EC02 write pointer */ + bufpos = status->InTail; + + /* check for input for this port */ + if (newhead != bufpos) { + /* buffer for input chars/events */ + ibuf = mem->InBuf[p]; + + /* data types of bytes in ibuf */ + cbuf = mem->InCtl[p]; + + /* do for all chars */ + while (bufpos != newhead) { + /* which type of input data? */ + switch (cbuf[bufpos]) { + /* switch on input event (CD, BREAK, etc.) */ + case A2232INCTL_EVENT: + switch (ibuf[bufpos++]) { + case A2232EVENT_Break: + /* TODO: Handle BREAK signal */ + break; + /* A2232EVENT_CarrierOn and A2232EVENT_CarrierOff are + handled in a separate queue and should not occur here. */ + case A2232EVENT_Sync: + printk("A2232: 65EC02 software sent SYNC event, don't know what to do. Ignoring."); + break; + default: + printk("A2232: 65EC02 software broken, unknown event type %d occured.\n",ibuf[bufpos-1]); + } /* event type switch */ + break; + case A2232INCTL_CHAR: + /* Receive incoming char */ + a2232_receive_char(port, ibuf[bufpos], err); + bufpos++; + break; + default: + printk("A2232: 65EC02 software broken, unknown data type %d occured.\n",cbuf[bufpos]); + bufpos++; + } /* switch on input data type */ + } /* while there's something in the buffer */ + + status->InTail = bufpos; /* tell 65EC02 what we've read */ + + } /* if there was something in the buffer */ + } /* If input is not disabled */ + + /* Now check if there's something to output */ + obuf = mem->OutBuf[p]; + bufpos = status->OutHead; + while ( (port->gs.xmit_cnt > 0) && + (!port->gs.tty->stopped) && + (!port->gs.tty->hw_stopped) ){ /* While there are chars to transmit */ + if (((bufpos+1) & A2232_IOBUFLENMASK) != status->OutTail) { /* If the A2232 buffer is not full */ + ch = port->gs.xmit_buf[port->gs.xmit_tail]; /* get the next char to transmit */ + port->gs.xmit_tail = (port->gs.xmit_tail+1) & (SERIAL_XMIT_SIZE-1); /* modulo-addition for the gs.xmit_buf ring-buffer */ + obuf[bufpos++] = ch; /* put it into the A2232 buffer */ + port->gs.xmit_cnt--; + } + else{ /* If A2232 the buffer is full */ + break; /* simply stop filling it. */ + } + } + status->OutHead = bufpos; + + /* WakeUp if output buffer runs low */ + if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) { + if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && port->gs.tty->ldisc.write_wakeup){ + (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); + } + wake_up_interruptible(&port->gs.tty->write_wait); + } + } // if the port is used + } // for every port on the board + + /* Now check the CD message queue */ + newhead = mem->Common.CDHead; + bufpos = mem->Common.CDTail; + if (newhead != bufpos){ /* There are CD events in queue */ + ocd = mem->Common.CDStatus; /* get old status bits */ + while (newhead != bufpos){ /* read all events */ + ncd = mem->CDBuf[bufpos++]; /* get one event */ + ccd = ncd ^ ocd; /* mask of changed lines */ + ocd = ncd; /* save new status bits */ + for(p=0; p < NUMLINES; p++){ /* for all ports */ + if (ccd & 1){ /* this one changed */ + + struct a2232_port *port = &a2232_ports[n*7+p]; + port->cd_status = !(ncd & 1); /* ncd&1 <=> CD is now off */ + + if (!(port->gs.flags & ASYNC_CHECK_CD)) + ; /* Don't report DCD changes */ + else if (port->cd_status) { // if DCD on: DCD went UP! + if (~(port->gs.flags & ASYNC_NORMAL_ACTIVE) || + ~(port->gs.flags & ASYNC_CALLOUT_ACTIVE)) { + /* Are we blocking in open?*/ + wake_up_interruptible(&port->gs.open_wait); + } + } + else { // if DCD off: DCD went DOWN! + if (!((port->gs.flags & ASYNC_CALLOUT_ACTIVE) && + (port->gs.flags & ASYNC_CALLOUT_NOHUP))) { + if (port->gs.tty) + tty_hangup (port->gs.tty); + } + } + + } // if CD changed for this port + ccd >>= 1; + ncd >>= 1; /* Shift bits for next line */ + } // for every port + } // while CD events in queue + mem->Common.CDStatus = ocd; /* save new status */ + mem->Common.CDTail = bufpos; /* remove events */ + } // if events in CD queue + + } // for every completely initialized A2232 board +} + +static void a2232_init_portstructs(void) +{ + struct a2232_port *port; + int i; + + for (i = 0; i < MAX_A2232_BOARDS*NUMLINES; i++) { + port = a2232_ports + i; + port->which_a2232 = i/NUMLINES; + port->which_port_on_a2232 = i%NUMLINES; + port->disable_rx = port->throttle_input = port->cd_status = 0; + port->gs.callout_termios = tty_std_termios; + port->gs.normal_termios = tty_std_termios; + port->gs.magic = A2232_MAGIC; + port->gs.close_delay = HZ/2; + port->gs.closing_wait = 30 * HZ; + port->gs.rd = &a2232_real_driver; +#ifdef NEW_WRITE_LOCKING + init_MUTEX(&(port->gs.port_write_sem)); +#endif + init_waitqueue_head(&port->gs.open_wait); + init_waitqueue_head(&port->gs.close_wait); + } +} + +static int a2232_init_drivers(void) +{ + int error; + + memset(&a2232_driver, 0, sizeof(a2232_driver)); + a2232_driver.magic = TTY_DRIVER_MAGIC; + a2232_driver.driver_name = "commodore_a2232"; + a2232_driver.name = "ttyY"; + a2232_driver.major = A2232_NORMAL_MAJOR; + a2232_driver.num = NUMLINES * nr_a2232; + a2232_driver.type = TTY_DRIVER_TYPE_SERIAL; + a2232_driver.subtype = A2232_TTY_SUBTYPE_NORMAL; + a2232_driver.init_termios = tty_std_termios; + a2232_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + a2232_driver.flags = TTY_DRIVER_REAL_RAW; + a2232_driver.refcount = &a2232_refcount; + a2232_driver.table = a2232_table; + a2232_driver.termios = a2232_termios; + a2232_driver.termios_locked = a2232_termios_locked; + + a2232_driver.open = a2232_open; + a2232_driver.close = gs_close; + a2232_driver.write = gs_write; + a2232_driver.put_char = gs_put_char; + a2232_driver.flush_chars = gs_flush_chars; + a2232_driver.write_room = gs_write_room; + a2232_driver.chars_in_buffer = gs_chars_in_buffer; + a2232_driver.flush_buffer = gs_flush_buffer; + a2232_driver.ioctl = a2232_ioctl; + a2232_driver.throttle = a2232_throttle; + a2232_driver.unthrottle = a2232_unthrottle; + a2232_driver.set_termios = gs_set_termios; + a2232_driver.stop = gs_stop; + a2232_driver.start = gs_start; + a2232_driver.hangup = gs_hangup; + + a2232_callout_driver = a2232_driver; + a2232_callout_driver.name = "cuy"; + a2232_callout_driver.major = A2232_CALLOUT_MAJOR; + a2232_callout_driver.subtype = A2232_TTY_SUBTYPE_CALLOUT; + + if ((error = tty_register_driver(&a2232_driver))) { + printk(KERN_ERR "A2232: Couldn't register A2232 driver, error = %d\n", + error); + return 1; + } + if ((error = tty_register_driver(&a2232_callout_driver))) { + tty_unregister_driver(&a2232_driver); + printk(KERN_ERR "A2232: Couldn't register A2232 callout driver, error = %d\n", + error); + return 1; + } + return 0; +} + +int a2232board_init(void) +{ + struct zorro_dev *z; + + unsigned int boardaddr; + int bcount; + short start; + u_char *from; + volatile u_char *to; + volatile struct a2232memory *mem; + +#ifdef __SMP__ + return -ENODEV; /* This driver is not SMP aware. Is there an SMP ZorroII-bus-machine? */ +#endif + + if (!MACH_IS_AMIGA){ + return -ENODEV; + } + + printk("Commodore A2232 driver initializing.\n"); /* Say that we're alive. */ + + z = NULL; + nr_a2232 = 0; + while ( (z = zorro_find_device(ZORRO_WILDCARD, z)) ){ + if ( (z->id != ZORRO_PROD_CBM_A2232_PROTOTYPE) && + (z->id != ZORRO_PROD_CBM_A2232) ){ + continue; // The board found was no A2232 + } + if (!zorro_request_device(z,"A2232 driver")) + continue; + + printk("Commodore A2232 found (#%d).\n",nr_a2232); + + zd_a2232[nr_a2232] = z; + + boardaddr = ZTWO_VADDR( z->resource.start ); + printk("Board is located at address 0x%x, size is 0x%x.\n", boardaddr, (unsigned int) ((z->resource.end+1) - (z->resource.start))); + + mem = (volatile struct a2232memory *) boardaddr; + + (void) mem->Enable6502Reset; /* copy the code across to the board */ + to = (u_char *)mem; from = a2232_65EC02code; bcount = sizeof(a2232_65EC02code) - 2; + start = *(short *)from; + from += sizeof(start); + to += start; + while(bcount--) *to++ = *from++; + printk("65EC02 software uploaded to the A2232 memory.\n"); + + mem->Common.Crystal = A2232_UNKNOWN; /* use automatic speed check */ + + /* start 6502 running */ + (void) mem->ResetBoard; + printk("A2232's 65EC02 CPU up and running.\n"); + + /* wait until speed detector has finished */ + for (bcount = 0; bcount < 2000; bcount++) { + udelay(1000); + if (mem->Common.Crystal) + break; + } + printk((mem->Common.Crystal?"A2232 oscillator crystal detected by 65EC02 software: ":"65EC02 software could not determine A2232 oscillator crystal: ")); + switch (mem->Common.Crystal){ + case A2232_UNKNOWN: + printk("Unknown crystal.\n"); + break; + case A2232_NORMAL: + printk ("Normal crystal.\n"); + break; + case A2232_TURBO: + printk ("Turbo crystal.\n"); + break; + default: + printk ("0x%x. Huh?\n",mem->Common.Crystal); + } + + nr_a2232++; + + } + + printk("Total: %d A2232 boards initialized.\n.", nr_a2232); /* Some status report if no card was found */ + + a2232_init_portstructs(); + + /* + a2232_init_drivers also registers the drivers. Must be here because all boards + have to be detected first. + */ + if (a2232_init_drivers()) return -ENODEV; // maybe we should use a different -Exxx? + + request_irq(IRQ_AMIGA_VERTB, a2232_vbl_inter, 0, "A2232 serial VBL", a2232_driver_ID); + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + return a2232board_init(); +} + +void cleanup_module(void) +{ + int i; + + for (i = 0; i < nr_a2232; i++) { + zorro_release_device(zd_a2232[i]); + } + + tty_unregister_driver(&a2232_driver); + tty_unregister_driver(&a2232_callout_driver); + free_irq(IRQ_AMIGA_VERTB, a2232_driver_ID); +} +#endif +/***************************** End of Functions *********************/ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ser_a2232.h linux.ac/drivers/char/ser_a2232.h --- linux.vanilla/drivers/char/ser_a2232.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/ser_a2232.h Tue May 22 19:04:19 2001 @@ -0,0 +1,206 @@ +/* drivers/char/ser_a2232.h */ + +/* $Id: ser_a2232.h,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */ + +/* Linux serial driver for the Amiga A2232 board */ + +/* This driver is MAINTAINED. Before applying any changes, please contact + * the author. + */ + +/* Copyright (c) 2000-2001 Enver Haase + * alias The A2232 driver project + * All rights reserved. + * + * 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 _SER_A2232_H_ +#define _SER_A2232_H_ + +/* + How many boards are to be supported at maximum; + "up to five A2232 Multiport Serial Cards may be installed in a + single Amiga 2000" states the A2232 User's Guide. If you have + more slots available, you might want to change the value below. +*/ +#define MAX_A2232_BOARDS 5 + +#ifndef A2232_NORMAL_MAJOR +/* This allows overriding on the compiler commandline, or in a "major.h" + include or something like that */ +#define A2232_NORMAL_MAJOR 224 /* /dev/ttyY* */ +#define A2232_CALLOUT_MAJOR 225 /* /dev/cuy* */ +#endif + +/* Some magic is always good - Who knows :) */ +#define A2232_MAGIC 0x000a2232 + +/* for the tty_struct subtype field */ +#define A2232_TTY_SUBTYPE_NORMAL 1 +#define A2232_TTY_SUBTYPE_CALLOUT 2 + +/* A2232 port structure to keep track of the + status of every single line used */ +struct a2232_port{ + struct gs_port gs; + unsigned int which_a2232; + unsigned int which_port_on_a2232; + short disable_rx; + short throttle_input; + short cd_status; +}; + +#define NUMLINES 7 /* number of lines per board */ +#define A2232_IOBUFLEN 256 /* number of bytes per buffer */ +#define A2232_IOBUFLENMASK 0xff /* mask for maximum number of bytes */ + + +#define A2232_UNKNOWN 0 /* crystal not known */ +#define A2232_NORMAL 1 /* normal A2232 (1.8432 MHz oscillator) */ +#define A2232_TURBO 2 /* turbo A2232 (3.6864 MHz oscillator) */ + + +struct a2232common { + char Crystal; /* normal (1) or turbo (2) board? */ + u_char Pad_a; + u_char TimerH; /* timer value after speed check */ + u_char TimerL; + u_char CDHead; /* head pointer for CD message queue */ + u_char CDTail; /* tail pointer for CD message queue */ + u_char CDStatus; + u_char Pad_b; +}; + +struct a2232status { + u_char InHead; /* input queue head */ + u_char InTail; /* input queue tail */ + u_char OutDisable; /* disables output */ + u_char OutHead; /* output queue head */ + u_char OutTail; /* output queue tail */ + u_char OutCtrl; /* soft flow control character to send */ + u_char OutFlush; /* flushes output buffer */ + u_char Setup; /* causes reconfiguration */ + u_char Param; /* parameter byte - see A2232PARAM */ + u_char Command; /* command byte - see A2232CMD */ + u_char SoftFlow; /* enables xon/xoff flow control */ + /* private 65EC02 fields: */ + u_char XonOff; /* stores XON/XOFF enable/disable */ +}; + +#define A2232_MEMPAD1 \ + (0x0200 - NUMLINES * sizeof(struct a2232status) - \ + sizeof(struct a2232common)) +#define A2232_MEMPAD2 (0x2000 - NUMLINES * A2232_IOBUFLEN - A2232_IOBUFLEN) + +struct a2232memory { + struct a2232status Status[NUMLINES]; /* 0x0000-0x006f status areas */ + struct a2232common Common; /* 0x0070-0x0077 common flags */ + u_char Dummy1[A2232_MEMPAD1]; /* 0x00XX-0x01ff */ + u_char OutBuf[NUMLINES][A2232_IOBUFLEN];/* 0x0200-0x08ff output bufs */ + u_char InBuf[NUMLINES][A2232_IOBUFLEN]; /* 0x0900-0x0fff input bufs */ + u_char InCtl[NUMLINES][A2232_IOBUFLEN]; /* 0x1000-0x16ff control data */ + u_char CDBuf[A2232_IOBUFLEN]; /* 0x1700-0x17ff CD event buffer */ + u_char Dummy2[A2232_MEMPAD2]; /* 0x1800-0x2fff */ + u_char Code[0x1000]; /* 0x3000-0x3fff code area */ + u_short InterruptAck; /* 0x4000 intr ack */ + u_char Dummy3[0x3ffe]; /* 0x4002-0x7fff */ + u_short Enable6502Reset; /* 0x8000 Stop board, */ + /* 6502 RESET line held low */ + u_char Dummy4[0x3ffe]; /* 0x8002-0xbfff */ + u_short ResetBoard; /* 0xc000 reset board & run, */ + /* 6502 RESET line held high */ +}; + +#undef A2232_MEMPAD1 +#undef A2232_MEMPAD2 + +#define A2232INCTL_CHAR 0 /* corresponding byte in InBuf is a character */ +#define A2232INCTL_EVENT 1 /* corresponding byte in InBuf is an event */ + +#define A2232EVENT_Break 1 /* break set */ +#define A2232EVENT_CarrierOn 2 /* carrier raised */ +#define A2232EVENT_CarrierOff 3 /* carrier dropped */ +#define A2232EVENT_Sync 4 /* don't know, defined in 2232.ax */ + +#define A2232CMD_Enable 0x1 /* enable/DTR bit */ +#define A2232CMD_Close 0x2 /* close the device */ +#define A2232CMD_Open 0xb /* open the device */ +#define A2232CMD_CMask 0xf /* command mask */ +#define A2232CMD_RTSOff 0x0 /* turn off RTS */ +#define A2232CMD_RTSOn 0x8 /* turn on RTS */ +#define A2232CMD_Break 0xd /* transmit a break */ +#define A2232CMD_RTSMask 0xc /* mask for RTS stuff */ +#define A2232CMD_NoParity 0x00 /* don't use parity */ +#define A2232CMD_OddParity 0x20 /* odd parity */ +#define A2232CMD_EvenParity 0x60 /* even parity */ +#define A2232CMD_ParityMask 0xe0 /* parity mask */ + +#define A2232PARAM_B115200 0x0 /* baud rates */ +#define A2232PARAM_B50 0x1 +#define A2232PARAM_B75 0x2 +#define A2232PARAM_B110 0x3 +#define A2232PARAM_B134 0x4 +#define A2232PARAM_B150 0x5 +#define A2232PARAM_B300 0x6 +#define A2232PARAM_B600 0x7 +#define A2232PARAM_B1200 0x8 +#define A2232PARAM_B1800 0x9 +#define A2232PARAM_B2400 0xa +#define A2232PARAM_B3600 0xb +#define A2232PARAM_B4800 0xc +#define A2232PARAM_B7200 0xd +#define A2232PARAM_B9600 0xe +#define A2232PARAM_B19200 0xf +#define A2232PARAM_BaudMask 0xf /* baud rate mask */ +#define A2232PARAM_RcvBaud 0x10 /* enable receive baud rate */ +#define A2232PARAM_8Bit 0x00 /* numbers of bits */ +#define A2232PARAM_7Bit 0x20 +#define A2232PARAM_6Bit 0x40 +#define A2232PARAM_5Bit 0x60 +#define A2232PARAM_BitMask 0x60 /* numbers of bits mask */ + + +/* Standard speeds tables, -1 means unavailable, -2 means 0 baud: switch off line */ +#define A2232_BAUD_TABLE_NOAVAIL -1 +#define A2232_BAUD_TABLE_NUM_RATES (18) +static int a2232_baud_table[A2232_BAUD_TABLE_NUM_RATES*3] = { + //Baud //Normal //Turbo + 50, A2232PARAM_B50, A2232_BAUD_TABLE_NOAVAIL, + 75, A2232PARAM_B75, A2232_BAUD_TABLE_NOAVAIL, + 110, A2232PARAM_B110, A2232_BAUD_TABLE_NOAVAIL, + 134, A2232PARAM_B134, A2232_BAUD_TABLE_NOAVAIL, + 150, A2232PARAM_B150, A2232PARAM_B75, + 200, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL, + 300, A2232PARAM_B300, A2232PARAM_B150, + 600, A2232PARAM_B600, A2232PARAM_B300, + 1200, A2232PARAM_B1200, A2232PARAM_B600, + 1800, A2232PARAM_B1800, A2232_BAUD_TABLE_NOAVAIL, + 2400, A2232PARAM_B2400, A2232PARAM_B1200, + 4800, A2232PARAM_B4800, A2232PARAM_B2400, + 9600, A2232PARAM_B9600, A2232PARAM_B4800, + 19200, A2232PARAM_B19200, A2232PARAM_B9600, + 38400, A2232_BAUD_TABLE_NOAVAIL, A2232PARAM_B19200, + 57600, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL, +#ifdef A2232_SPEEDHACK + 115200, A2232PARAM_B115200, A2232_BAUD_TABLE_NOAVAIL, + 230400, A2232_BAUD_TABLE_NOAVAIL, A2232PARAM_B115200 +#else + 115200, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL, + 230400, A2232_BAUD_TABLE_NOAVAIL, A2232_BAUD_TABLE_NOAVAIL +#endif +}; +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/ser_a2232fw.ax linux.ac/drivers/char/ser_a2232fw.ax --- linux.vanilla/drivers/char/ser_a2232fw.ax Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/ser_a2232fw.ax Tue May 22 19:04:19 2001 @@ -0,0 +1,529 @@ +;.lib "axm" +; +;begin +;title "A2232 serial board driver" +; +;set modules "2232" +;set executable "2232.bin" +; +;;;;set nolink +; +;set temporary directory "t:" +; +;set assembly options "-m6502 -l60:t:list" +;set link options "bin"; loadadr" +;;;bin2c 2232.bin msc6502.h msc6502code +;end +; +; +; ### Commodore A2232 serial board driver for NetBSD by JM v1.3 ### +; +; - Created 950501 by JM - +; +; +; Serial board driver software. +; +; +% Copyright (c) 1995 Jukka Marin . +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions +% are met: +% 1. Redistributions of source code must retain the above copyright +% notice, and the entire permission notice in its entirety, +% including the disclaimer of warranties. +% 2. Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the +% documentation and/or other materials provided with the distribution. +% 3. The name of the author may not be used to endorse or promote +% products derived from this software without specific prior +% written permission. +% +% ALTERNATIVELY, this product may be distributed under the terms of +% the GNU General Public License, in which case the provisions of the +% GPL are required INSTEAD OF the above restrictions. (This clause is +% necessary due to a potential bad interaction between the GPL and +% the restrictions contained in a BSD-style copyright.) +% +% THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED +% WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +% OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +% DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +% INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +% (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +% SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +% HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +% STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +% OF THE POSSIBILITY OF SUCH DAMAGE. +; +; +; Bugs: +; +; - Can't send a break yet +; +; +; +; Edited: +; +; - 950501 by JM -> v0.1 - Created this file. +; - 951029 by JM -> v1.3 - Carrier Detect events now queued in a separate +; queue. +; +; + + +CODE equ $3800 ; start address for program code + + +CTL_CHAR equ $00 ; byte in ibuf is a character +CTL_EVENT equ $01 ; byte in ibuf is an event + +EVENT_BREAK equ $01 +EVENT_CDON equ $02 +EVENT_CDOFF equ $03 +EVENT_SYNC equ $04 + +XON equ $11 +XOFF equ $13 + + +VARBASE macro *starting_address ; was VARINIT +_varbase set \1 + endm + +VARDEF macro *name space_needs +\1 equ _varbase +_varbase set _varbase+\2 + endm + + +stz macro * address + db $64,\1 + endm + +stzax macro * address + db $9e,<\1,>\1 + endm + + +biti macro * immediate value + db $89,\1 + endm + +smb0 macro * address + db $87,\1 + endm +smb1 macro * address + db $97,\1 + endm +smb2 macro * address + db $a7,\1 + endm +smb3 macro * address + db $b7,\1 + endm +smb4 macro * address + db $c7,\1 + endm +smb5 macro * address + db $d7,\1 + endm +smb6 macro * address + db $e7,\1 + endm +smb7 macro * address + db $f7,\1 + endm + + + +;-----------------------------------------------------------------------; +; ; +; stuff common for all ports, non-critical (run once / loop) ; +; ; +DO_SLOW macro * port_number ; + .local ; ; + lda CIA+C_PA ; check all CD inputs ; + cmp CommonCDo ; changed from previous accptd? ; + beq =over ; nope, do nothing else here ; + ; ; + cmp CommonCDb ; bouncing? ; + beq =nobounce ; nope -> ; + ; ; + sta CommonCDb ; save current state ; + lda #64 ; reinitialize counter ; + sta CommonCDc ; ; + jmp =over ; skip CD save ; + ; ; +=nobounce dec CommonCDc ; no, decrement bounce counter ; + bpl =over ; not done yet, so skip CD save ; + ; ; +=saveCD ldx CDHead ; get write index ; + sta cdbuf,x ; save status in buffer ; + inx ; ; + cpx CDTail ; buffer full? ; + .if ne ; no: preserve status: ; + stx CDHead ; update index in RAM ; + sta CommonCDo ; save state for the next check ; + .end ; ; +=over .end local ; + endm ; + ; +;-----------------------------------------------------------------------; + + +; port specific stuff (no data transfer) + +DO_PORT macro * port_number + .local ; ; + lda SetUp\1 ; reconfiguration request? ; + .if ne ; yes: ; + lda SoftFlow\1 ; get XON/XOFF flag ; + sta XonOff\1 ; save it ; + lda Param\1 ; get parameter ; + ora #%00010000 ; use baud generator for Rx ; + sta ACIA\1+A_CTRL ; store in control register ; + stz OutDisable\1 ; enable transmit output ; + stz SetUp\1 ; no reconfiguration no more ; + .end ; ; + ; ; + lda InHead\1 ; get write index ; + sbc InTail\1 ; buffer full soon? ; + cmp #200 ; 200 chars or more in buffer? ; + lda Command\1 ; get Command reg value ; + and #%11110011 ; turn RTS OFF by default ; + .if cc ; still room in buffer: ; + ora #%00001000 ; turn RTS ON ; + .end ; ; + sta ACIA\1+A_CMD ; set/clear RTS ; + ; ; + lda OutFlush\1 ; request to flush output buffer; + .if ne ; yessh! ; + lda OutHead\1 ; get head ; + sta OutTail\1 ; save as tail ; + stz OutDisable\1 ; enable transmit output ; + stz OutFlush\1 ; clear request ; + .end + .end local + endm + + +DO_DATA macro * port number + .local + lda ACIA\1+A_SR ; read ACIA status register ; + biti [1<<3] ; something received? ; + .if ne ; yes: ; + biti [1<<1] ; framing error? ; + .if ne ; yes: ; + lda ACIA\1+A_DATA ; read received character ; + bne =SEND ; not break -> ignore it ; + ldx InHead\1 ; get write pointer ; + lda #CTL_EVENT ; get type of byte ; + sta ictl\1,x ; save it in InCtl buffer ; + lda #EVENT_BREAK ; event code ; + sta ibuf\1,x ; save it as well ; + inx ; ; + cpx InTail\1 ; still room in buffer? ; + .if ne ; absolutely: ; + stx InHead\1 ; update index in memory ; + .end ; ; + jmp =SEND ; go check if anything to send ; + .end ; ; + ; normal char received: ; + ldx InHead\1 ; get write index ; + lda ACIA\1+A_DATA ; read received character ; + sta ibuf\1,x ; save char in buffer ; + stzax ictl\1 ; set type to CTL_CHAR ; + inx ; ; + cpx InTail\1 ; buffer full? ; + .if ne ; no: preserve character: ; + stx InHead\1 ; update index in RAM ; + .end ; ; + and #$7f ; mask off parity if any ; + cmp #XOFF ; XOFF from remote host? ; + .if eq ; yes: ; + lda XonOff\1 ; if XON/XOFF handshaking.. ; + sta OutDisable\1 ; ..disable transmitter ; + .end ; ; + .end ; ; + ; ; + ; BUFFER FULL CHECK WAS HERE ; + ; ; +=SEND lda ACIA\1+A_SR ; transmit register empty? ; + and #[1<<4] ; ; + .if ne ; yes: ; + ldx OutCtrl\1 ; sending out XON/XOFF? ; + .if ne ; yes: ; + lda CIA+C_PB ; check CTS signal ; + and #[1<<\1] ; (for this port only) ; + bne =DONE ; not allowed to send -> done ; + stx ACIA\1+A_DATA ; transmit control char ; + stz OutCtrl\1 ; clear flag ; + jmp =DONE ; and we're done ; + .end ; ; + ; ; + ldx OutTail\1 ; anything to transmit? ; + cpx OutHead\1 ; ; + .if ne ; yes: ; + lda OutDisable\1 ; allowed to transmit? ; + .if eq ; yes: ; + lda CIA+C_PB ; check CTS signal ; + and #[1<<\1] ; (for this port only) ; + bne =DONE ; not allowed to send -> done ; + lda obuf\1,x ; get a char from buffer ; + sta ACIA\1+A_DATA ; send it away ; + inc OutTail\1 ; update read index ; + .end ; ; + .end ; ; + .end ; ; +=DONE .end local + endm + + + +PORTVAR macro * port number + VARDEF InHead\1 1 + VARDEF InTail\1 1 + VARDEF OutDisable\1 1 + VARDEF OutHead\1 1 + VARDEF OutTail\1 1 + VARDEF OutCtrl\1 1 + VARDEF OutFlush\1 1 + VARDEF SetUp\1 1 + VARDEF Param\1 1 + VARDEF Command\1 1 + VARDEF SoftFlow\1 1 + ; private: + VARDEF XonOff\1 1 + endm + + + VARBASE 0 ; start variables at address $0000 + PORTVAR 0 ; define variables for port 0 + PORTVAR 1 ; define variables for port 1 + PORTVAR 2 ; define variables for port 2 + PORTVAR 3 ; define variables for port 3 + PORTVAR 4 ; define variables for port 4 + PORTVAR 5 ; define variables for port 5 + PORTVAR 6 ; define variables for port 6 + + + + VARDEF Crystal 1 ; 0 = unknown, 1 = normal, 2 = turbo + VARDEF Pad_a 1 + VARDEF TimerH 1 + VARDEF TimerL 1 + VARDEF CDHead 1 + VARDEF CDTail 1 + VARDEF CDStatus 1 + VARDEF Pad_b 1 + + VARDEF CommonCDo 1 ; for carrier detect optimization + VARDEF CommonCDc 1 ; for carrier detect debouncing + VARDEF CommonCDb 1 ; for carrier detect debouncing + + + VARBASE $0200 + VARDEF obuf0 256 ; output data (characters only) + VARDEF obuf1 256 + VARDEF obuf2 256 + VARDEF obuf3 256 + VARDEF obuf4 256 + VARDEF obuf5 256 + VARDEF obuf6 256 + + VARDEF ibuf0 256 ; input data (characters, events etc - see ictl) + VARDEF ibuf1 256 + VARDEF ibuf2 256 + VARDEF ibuf3 256 + VARDEF ibuf4 256 + VARDEF ibuf5 256 + VARDEF ibuf6 256 + + VARDEF ictl0 256 ; input control information (type of data in ibuf) + VARDEF ictl1 256 + VARDEF ictl2 256 + VARDEF ictl3 256 + VARDEF ictl4 256 + VARDEF ictl5 256 + VARDEF ictl6 256 + + VARDEF cdbuf 256 ; CD event queue + + +ACIA0 equ $4400 +ACIA1 equ $4c00 +ACIA2 equ $5400 +ACIA3 equ $5c00 +ACIA4 equ $6400 +ACIA5 equ $6c00 +ACIA6 equ $7400 + +A_DATA equ $00 +A_SR equ $02 +A_CMD equ $04 +A_CTRL equ $06 +; 00 write transmit data read received data +; 02 reset ACIA read status register +; 04 write command register read command register +; 06 write control register read control register + +CIA equ $7c00 ; 8520 CIA +C_PA equ $00 ; port A data register +C_PB equ $02 ; port B data register +C_DDRA equ $04 ; data direction register for port A +C_DDRB equ $06 ; data direction register for port B +C_TAL equ $08 ; timer A +C_TAH equ $0a +C_TBL equ $0c ; timer B +C_TBH equ $0e +C_TODL equ $10 ; TOD LSB +C_TODM equ $12 ; TOD middle byte +C_TODH equ $14 ; TOD MSB +C_DATA equ $18 ; serial data register +C_INTCTRL equ $1a ; interrupt control register +C_CTRLA equ $1c ; control register A +C_CTRLB equ $1e ; control register B + + + + + + section main,code,CODE-2 + + db >CODE,. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* This is the 65EC02 code by Jukka Marin that is executed by + the A2232's 65EC02 processor (base address: 0x3800) + Source file: ser_a2232fw.ax + Version: 1.3 (951029) + Known Bugs: Cannot send a break yet +*/ +static unsigned char a2232_65EC02code[] = { + 0x38, 0x00, 0xA2, 0xFF, 0x9A, 0xD8, 0xA2, 0x00, + 0xA9, 0x00, 0xA0, 0x54, 0x95, 0x00, 0xE8, 0x88, + 0xD0, 0xFA, 0x64, 0x5C, 0x64, 0x5E, 0x64, 0x58, + 0x64, 0x59, 0xA9, 0x00, 0x85, 0x55, 0xA9, 0xAA, + 0xC9, 0x64, 0x90, 0x02, 0xE6, 0x55, 0xA5, 0x54, + 0xF0, 0x03, 0x4C, 0x92, 0x38, 0xA9, 0x98, 0x8D, + 0x06, 0x44, 0xA9, 0x0B, 0x8D, 0x04, 0x44, 0xAD, + 0x02, 0x44, 0xA9, 0x80, 0x8D, 0x1A, 0x7C, 0xA9, + 0xFF, 0x8D, 0x08, 0x7C, 0x8D, 0x0A, 0x7C, 0xA2, + 0x00, 0x8E, 0x00, 0x44, 0xEA, 0xEA, 0xAD, 0x02, + 0x44, 0xEA, 0xEA, 0x8E, 0x00, 0x44, 0xAD, 0x02, + 0x44, 0x29, 0x10, 0xF0, 0xF9, 0xA9, 0x11, 0x8E, + 0x00, 0x44, 0x8D, 0x1C, 0x7C, 0xAD, 0x02, 0x44, + 0x29, 0x10, 0xF0, 0xF9, 0x8E, 0x1C, 0x7C, 0xAD, + 0x08, 0x7C, 0x85, 0x57, 0xAD, 0x0A, 0x7C, 0x85, + 0x56, 0xC9, 0xD0, 0x90, 0x05, 0xA9, 0x02, 0x4C, + 0x82, 0x38, 0xA9, 0x01, 0x85, 0x54, 0xA9, 0x00, + 0x8D, 0x02, 0x44, 0x8D, 0x06, 0x44, 0x8D, 0x04, + 0x44, 0x4C, 0x92, 0x38, 0xAD, 0x00, 0x7C, 0xC5, + 0x5C, 0xF0, 0x1F, 0xC5, 0x5E, 0xF0, 0x09, 0x85, + 0x5E, 0xA9, 0x40, 0x85, 0x5D, 0x4C, 0xB8, 0x38, + 0xC6, 0x5D, 0x10, 0x0E, 0xA6, 0x58, 0x9D, 0x00, + 0x17, 0xE8, 0xE4, 0x59, 0xF0, 0x04, 0x86, 0x58, + 0x85, 0x5C, 0x20, 0x23, 0x3A, 0xA5, 0x07, 0xF0, + 0x0F, 0xA5, 0x0A, 0x85, 0x0B, 0xA5, 0x08, 0x09, + 0x10, 0x8D, 0x06, 0x44, 0x64, 0x02, 0x64, 0x07, + 0xA5, 0x00, 0xE5, 0x01, 0xC9, 0xC8, 0xA5, 0x09, + 0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04, + 0x44, 0xA5, 0x06, 0xF0, 0x08, 0xA5, 0x03, 0x85, + 0x04, 0x64, 0x02, 0x64, 0x06, 0x20, 0x23, 0x3A, + 0xA5, 0x13, 0xF0, 0x0F, 0xA5, 0x16, 0x85, 0x17, + 0xA5, 0x14, 0x09, 0x10, 0x8D, 0x06, 0x4C, 0x64, + 0x0E, 0x64, 0x13, 0xA5, 0x0C, 0xE5, 0x0D, 0xC9, + 0xC8, 0xA5, 0x15, 0x29, 0xF3, 0xB0, 0x02, 0x09, + 0x08, 0x8D, 0x04, 0x4C, 0xA5, 0x12, 0xF0, 0x08, + 0xA5, 0x0F, 0x85, 0x10, 0x64, 0x0E, 0x64, 0x12, + 0x20, 0x23, 0x3A, 0xA5, 0x1F, 0xF0, 0x0F, 0xA5, + 0x22, 0x85, 0x23, 0xA5, 0x20, 0x09, 0x10, 0x8D, + 0x06, 0x54, 0x64, 0x1A, 0x64, 0x1F, 0xA5, 0x18, + 0xE5, 0x19, 0xC9, 0xC8, 0xA5, 0x21, 0x29, 0xF3, + 0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04, 0x54, 0xA5, + 0x1E, 0xF0, 0x08, 0xA5, 0x1B, 0x85, 0x1C, 0x64, + 0x1A, 0x64, 0x1E, 0x20, 0x23, 0x3A, 0xA5, 0x2B, + 0xF0, 0x0F, 0xA5, 0x2E, 0x85, 0x2F, 0xA5, 0x2C, + 0x09, 0x10, 0x8D, 0x06, 0x5C, 0x64, 0x26, 0x64, + 0x2B, 0xA5, 0x24, 0xE5, 0x25, 0xC9, 0xC8, 0xA5, + 0x2D, 0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D, + 0x04, 0x5C, 0xA5, 0x2A, 0xF0, 0x08, 0xA5, 0x27, + 0x85, 0x28, 0x64, 0x26, 0x64, 0x2A, 0x20, 0x23, + 0x3A, 0xA5, 0x37, 0xF0, 0x0F, 0xA5, 0x3A, 0x85, + 0x3B, 0xA5, 0x38, 0x09, 0x10, 0x8D, 0x06, 0x64, + 0x64, 0x32, 0x64, 0x37, 0xA5, 0x30, 0xE5, 0x31, + 0xC9, 0xC8, 0xA5, 0x39, 0x29, 0xF3, 0xB0, 0x02, + 0x09, 0x08, 0x8D, 0x04, 0x64, 0xA5, 0x36, 0xF0, + 0x08, 0xA5, 0x33, 0x85, 0x34, 0x64, 0x32, 0x64, + 0x36, 0x20, 0x23, 0x3A, 0xA5, 0x43, 0xF0, 0x0F, + 0xA5, 0x46, 0x85, 0x47, 0xA5, 0x44, 0x09, 0x10, + 0x8D, 0x06, 0x6C, 0x64, 0x3E, 0x64, 0x43, 0xA5, + 0x3C, 0xE5, 0x3D, 0xC9, 0xC8, 0xA5, 0x45, 0x29, + 0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04, 0x6C, + 0xA5, 0x42, 0xF0, 0x08, 0xA5, 0x3F, 0x85, 0x40, + 0x64, 0x3E, 0x64, 0x42, 0x20, 0x23, 0x3A, 0xA5, + 0x4F, 0xF0, 0x0F, 0xA5, 0x52, 0x85, 0x53, 0xA5, + 0x50, 0x09, 0x10, 0x8D, 0x06, 0x74, 0x64, 0x4A, + 0x64, 0x4F, 0xA5, 0x48, 0xE5, 0x49, 0xC9, 0xC8, + 0xA5, 0x51, 0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08, + 0x8D, 0x04, 0x74, 0xA5, 0x4E, 0xF0, 0x08, 0xA5, + 0x4B, 0x85, 0x4C, 0x64, 0x4A, 0x64, 0x4E, 0x20, + 0x23, 0x3A, 0x4C, 0x92, 0x38, 0xAD, 0x02, 0x44, + 0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B, + 0xAD, 0x00, 0x44, 0xD0, 0x32, 0xA6, 0x00, 0xA9, + 0x01, 0x9D, 0x00, 0x10, 0xA9, 0x01, 0x9D, 0x00, + 0x09, 0xE8, 0xE4, 0x01, 0xF0, 0x02, 0x86, 0x00, + 0x4C, 0x65, 0x3A, 0xA6, 0x00, 0xAD, 0x00, 0x44, + 0x9D, 0x00, 0x09, 0x9E, 0x00, 0x10, 0xE8, 0xE4, + 0x01, 0xF0, 0x02, 0x86, 0x00, 0x29, 0x7F, 0xC9, + 0x13, 0xD0, 0x04, 0xA5, 0x0B, 0x85, 0x02, 0xAD, + 0x02, 0x44, 0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x05, + 0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x01, 0xD0, + 0x21, 0x8E, 0x00, 0x44, 0x64, 0x05, 0x4C, 0x98, + 0x3A, 0xA6, 0x04, 0xE4, 0x03, 0xF0, 0x13, 0xA5, + 0x02, 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x01, + 0xD0, 0x08, 0xBD, 0x00, 0x02, 0x8D, 0x00, 0x44, + 0xE6, 0x04, 0xAD, 0x02, 0x4C, 0x89, 0x08, 0xF0, + 0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00, 0x4C, + 0xD0, 0x32, 0xA6, 0x0C, 0xA9, 0x01, 0x9D, 0x00, + 0x11, 0xA9, 0x01, 0x9D, 0x00, 0x0A, 0xE8, 0xE4, + 0x0D, 0xF0, 0x02, 0x86, 0x0C, 0x4C, 0xDA, 0x3A, + 0xA6, 0x0C, 0xAD, 0x00, 0x4C, 0x9D, 0x00, 0x0A, + 0x9E, 0x00, 0x11, 0xE8, 0xE4, 0x0D, 0xF0, 0x02, + 0x86, 0x0C, 0x29, 0x7F, 0xC9, 0x13, 0xD0, 0x04, + 0xA5, 0x17, 0x85, 0x0E, 0xAD, 0x02, 0x4C, 0x29, + 0x10, 0xF0, 0x2C, 0xA6, 0x11, 0xF0, 0x0F, 0xAD, + 0x02, 0x7C, 0x29, 0x02, 0xD0, 0x21, 0x8E, 0x00, + 0x4C, 0x64, 0x11, 0x4C, 0x0D, 0x3B, 0xA6, 0x10, + 0xE4, 0x0F, 0xF0, 0x13, 0xA5, 0x0E, 0xD0, 0x0F, + 0xAD, 0x02, 0x7C, 0x29, 0x02, 0xD0, 0x08, 0xBD, + 0x00, 0x03, 0x8D, 0x00, 0x4C, 0xE6, 0x10, 0xAD, + 0x02, 0x54, 0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02, + 0xF0, 0x1B, 0xAD, 0x00, 0x54, 0xD0, 0x32, 0xA6, + 0x18, 0xA9, 0x01, 0x9D, 0x00, 0x12, 0xA9, 0x01, + 0x9D, 0x00, 0x0B, 0xE8, 0xE4, 0x19, 0xF0, 0x02, + 0x86, 0x18, 0x4C, 0x4F, 0x3B, 0xA6, 0x18, 0xAD, + 0x00, 0x54, 0x9D, 0x00, 0x0B, 0x9E, 0x00, 0x12, + 0xE8, 0xE4, 0x19, 0xF0, 0x02, 0x86, 0x18, 0x29, + 0x7F, 0xC9, 0x13, 0xD0, 0x04, 0xA5, 0x23, 0x85, + 0x1A, 0xAD, 0x02, 0x54, 0x29, 0x10, 0xF0, 0x2C, + 0xA6, 0x1D, 0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, + 0x04, 0xD0, 0x21, 0x8E, 0x00, 0x54, 0x64, 0x1D, + 0x4C, 0x82, 0x3B, 0xA6, 0x1C, 0xE4, 0x1B, 0xF0, + 0x13, 0xA5, 0x1A, 0xD0, 0x0F, 0xAD, 0x02, 0x7C, + 0x29, 0x04, 0xD0, 0x08, 0xBD, 0x00, 0x04, 0x8D, + 0x00, 0x54, 0xE6, 0x1C, 0xAD, 0x02, 0x5C, 0x89, + 0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD, + 0x00, 0x5C, 0xD0, 0x32, 0xA6, 0x24, 0xA9, 0x01, + 0x9D, 0x00, 0x13, 0xA9, 0x01, 0x9D, 0x00, 0x0C, + 0xE8, 0xE4, 0x25, 0xF0, 0x02, 0x86, 0x24, 0x4C, + 0xC4, 0x3B, 0xA6, 0x24, 0xAD, 0x00, 0x5C, 0x9D, + 0x00, 0x0C, 0x9E, 0x00, 0x13, 0xE8, 0xE4, 0x25, + 0xF0, 0x02, 0x86, 0x24, 0x29, 0x7F, 0xC9, 0x13, + 0xD0, 0x04, 0xA5, 0x2F, 0x85, 0x26, 0xAD, 0x02, + 0x5C, 0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x29, 0xF0, + 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x08, 0xD0, 0x21, + 0x8E, 0x00, 0x5C, 0x64, 0x29, 0x4C, 0xF7, 0x3B, + 0xA6, 0x28, 0xE4, 0x27, 0xF0, 0x13, 0xA5, 0x26, + 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x08, 0xD0, + 0x08, 0xBD, 0x00, 0x05, 0x8D, 0x00, 0x5C, 0xE6, + 0x28, 0xAD, 0x02, 0x64, 0x89, 0x08, 0xF0, 0x3B, + 0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00, 0x64, 0xD0, + 0x32, 0xA6, 0x30, 0xA9, 0x01, 0x9D, 0x00, 0x14, + 0xA9, 0x01, 0x9D, 0x00, 0x0D, 0xE8, 0xE4, 0x31, + 0xF0, 0x02, 0x86, 0x30, 0x4C, 0x39, 0x3C, 0xA6, + 0x30, 0xAD, 0x00, 0x64, 0x9D, 0x00, 0x0D, 0x9E, + 0x00, 0x14, 0xE8, 0xE4, 0x31, 0xF0, 0x02, 0x86, + 0x30, 0x29, 0x7F, 0xC9, 0x13, 0xD0, 0x04, 0xA5, + 0x3B, 0x85, 0x32, 0xAD, 0x02, 0x64, 0x29, 0x10, + 0xF0, 0x2C, 0xA6, 0x35, 0xF0, 0x0F, 0xAD, 0x02, + 0x7C, 0x29, 0x10, 0xD0, 0x21, 0x8E, 0x00, 0x64, + 0x64, 0x35, 0x4C, 0x6C, 0x3C, 0xA6, 0x34, 0xE4, + 0x33, 0xF0, 0x13, 0xA5, 0x32, 0xD0, 0x0F, 0xAD, + 0x02, 0x7C, 0x29, 0x10, 0xD0, 0x08, 0xBD, 0x00, + 0x06, 0x8D, 0x00, 0x64, 0xE6, 0x34, 0xAD, 0x02, + 0x6C, 0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0, + 0x1B, 0xAD, 0x00, 0x6C, 0xD0, 0x32, 0xA6, 0x3C, + 0xA9, 0x01, 0x9D, 0x00, 0x15, 0xA9, 0x01, 0x9D, + 0x00, 0x0E, 0xE8, 0xE4, 0x3D, 0xF0, 0x02, 0x86, + 0x3C, 0x4C, 0xAE, 0x3C, 0xA6, 0x3C, 0xAD, 0x00, + 0x6C, 0x9D, 0x00, 0x0E, 0x9E, 0x00, 0x15, 0xE8, + 0xE4, 0x3D, 0xF0, 0x02, 0x86, 0x3C, 0x29, 0x7F, + 0xC9, 0x13, 0xD0, 0x04, 0xA5, 0x47, 0x85, 0x3E, + 0xAD, 0x02, 0x6C, 0x29, 0x10, 0xF0, 0x2C, 0xA6, + 0x41, 0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x20, + 0xD0, 0x21, 0x8E, 0x00, 0x6C, 0x64, 0x41, 0x4C, + 0xE1, 0x3C, 0xA6, 0x40, 0xE4, 0x3F, 0xF0, 0x13, + 0xA5, 0x3E, 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, + 0x20, 0xD0, 0x08, 0xBD, 0x00, 0x07, 0x8D, 0x00, + 0x6C, 0xE6, 0x40, 0xAD, 0x02, 0x74, 0x89, 0x08, + 0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00, + 0x74, 0xD0, 0x32, 0xA6, 0x48, 0xA9, 0x01, 0x9D, + 0x00, 0x16, 0xA9, 0x01, 0x9D, 0x00, 0x0F, 0xE8, + 0xE4, 0x49, 0xF0, 0x02, 0x86, 0x48, 0x4C, 0x23, + 0x3D, 0xA6, 0x48, 0xAD, 0x00, 0x74, 0x9D, 0x00, + 0x0F, 0x9E, 0x00, 0x16, 0xE8, 0xE4, 0x49, 0xF0, + 0x02, 0x86, 0x48, 0x29, 0x7F, 0xC9, 0x13, 0xD0, + 0x04, 0xA5, 0x53, 0x85, 0x4A, 0xAD, 0x02, 0x74, + 0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x4D, 0xF0, 0x0F, + 0xAD, 0x02, 0x7C, 0x29, 0x40, 0xD0, 0x21, 0x8E, + 0x00, 0x74, 0x64, 0x4D, 0x4C, 0x56, 0x3D, 0xA6, + 0x4C, 0xE4, 0x4B, 0xF0, 0x13, 0xA5, 0x4A, 0xD0, + 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x40, 0xD0, 0x08, + 0xBD, 0x00, 0x08, 0x8D, 0x00, 0x74, 0xE6, 0x4C, + 0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xD0, 0x00, 0x38, + 0xCE, 0xC0, +}; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/serial.c linux.ac/drivers/char/serial.c --- linux.vanilla/drivers/char/serial.c Sat May 26 16:53:03 2001 +++ linux.ac/drivers/char/serial.c Wed May 23 00:09:12 2001 @@ -59,8 +59,8 @@ * */ -static char *serial_version = "5.05a"; -static char *serial_revdate = "2001-03-20"; +static char *serial_version = "5.05b"; +static char *serial_revdate = "2001-05-03"; /* * Serial driver configuration section. Here are the various options: @@ -3512,7 +3512,7 @@ struct serial_state *state, unsigned long flags) { - unsigned char scratch, scratch2, scratch3; + unsigned char scratch, scratch2, scratch3, scratch4; /* * First we check to see if it's an Oxford Semiconductor UART. @@ -3556,17 +3556,32 @@ * XR16C854. * */ + + /* Save the DLL and DLM */ + serial_outp(info, UART_LCR, UART_LCR_DLAB); + scratch3 = serial_inp(info, UART_DLL); + scratch4 = serial_inp(info, UART_DLM); + serial_outp(info, UART_DLL, 0); serial_outp(info, UART_DLM, 0); - state->revision = serial_inp(info, UART_DLL); + scratch2 = serial_inp(info, UART_DLL); scratch = serial_inp(info, UART_DLM); serial_outp(info, UART_LCR, 0); + if (scratch == 0x10 || scratch == 0x14) { + if (scratch == 0x10) + state->revision = scratch2; state->type = PORT_16850; return; } + /* Restore the DLL and DLM */ + + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, scratch3); + serial_outp(info, UART_DLM, scratch4); + serial_outp(info, UART_LCR, 0); /* * We distinguish between the '654 and the '650 by counting * how many bytes are in the FIFO. I'm using this for now, @@ -3917,14 +3932,14 @@ if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) { printk("serial: PNP device '"); - printk_pnp_dev_id(board->vendor, board->device); + printk_pnp_dev_id(dev->vendor, dev->device); printk("' prepare failed\n"); return; } if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) { printk("serial: PNP device '"); - printk_pnp_dev_id(board->vendor, board->device); + printk_pnp_dev_id(dev->vendor, dev->device); printk("' activate failed\n"); return; } @@ -4112,7 +4127,7 @@ { unsigned long oldval; - if (!(board->subdevice & 0x1000)) + if (!(pci_get_subvendor(dev) & 0x1000)) return(-1); if (!enable) /* is there something to deinit? */ @@ -4187,530 +4202,661 @@ return 0; } +static int +#ifndef MODULE +__devinit +#endif +pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/10); + return 0; +} /* * This is the configuration table for all of the PCI serial boards - * which we support. - */ + * which we support. It is directly indexed by the pci_board_num_t enum + * value, which is encoded in the pci_device_id PCI probe table's + * driver_data member. + */ +enum pci_board_num_t { + pbn_b0_1_115200, + pbn_default = 0, + + pbn_b0_2_115200, + pbn_b0_4_115200, + + pbn_b0_1_921600, + pbn_b0_2_921600, + pbn_b0_4_921600, + + pbn_b0_bt_1_115200, + pbn_b0_bt_2_115200, + pbn_b0_bt_1_460800, + pbn_b0_bt_2_460800, + + pbn_b1_1_115200, + pbn_b1_2_115200, + pbn_b1_4_115200, + pbn_b1_8_115200, + + pbn_b1_2_921600, + pbn_b1_4_921600, + pbn_b1_8_921600, + + pbn_b1_2_1382400, + pbn_b1_4_1382400, + pbn_b1_8_1382400, + + pbn_b2_8_115200, + pbn_b2_4_460800, + pbn_b2_8_460800, + pbn_b2_16_460800, + pbn_b2_4_921600, + pbn_b2_8_921600, + + pbn_b2_bt_1_115200, + pbn_b2_bt_2_115200, + pbn_b2_bt_4_115200, + pbn_b2_bt_2_921600, + + pbn_panacom, + pbn_panacom2, + pbn_panacom4, + pbn_plx_romulus, + pbn_oxsemi, + pbn_timedia, + pbn_intel_i960, + pbn_sgi_ioc3, +#ifdef CONFIG_DDB5074 + pbn_nec_nile4, +#endif +#if 0 + pbn_dci_pccom8, +#endif + pbn_xircom_combo, + + pbn_siig10x_0, + pbn_siig10x_1, + pbn_siig10x_2, + pbn_siig10x_4, + pbn_siig20x_0, + pbn_siig20x_2, + pbn_siig20x_4, + + pbn_computone_4, + pbn_computone_6, + pbn_computone_8, +}; + static struct pci_board pci_boards[] __devinitdata = { /* - * Vendor ID, Device ID, - * Subvendor ID, Subdevice ID, * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, * Offset to get to next UART's registers, * Register shift to use for memory-mapped I/O, * Initialization function, first UART offset */ + + /* Generic serial board, pbn_b0_1_115200, pbn_default */ + { SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, + pbn_default */ + + { SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ + { SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ + + { SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ + { SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ + { SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ + + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ + + { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ + { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ + { SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ + { SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ + + { SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ + { SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ + { SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */ + + { SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ + { SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ + { SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ + + { SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ + { SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ + { SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */ + { SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */ + { SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */ + { SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */ + + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */ + + { SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ + 0x20, 2, pci_plx9050_fn, 0x03 }, + /* This board uses the size of PCI Base region 0 to + * signal now many ports are available */ + { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ + { SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ + 0, 0, pci_timedia_fn }, + /* EKF addition for i960 Boards form EKF with serial port */ + { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ + 8<<2, 2, pci_inteli960ni_fn, 0x10000}, + { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */ + 1, 458333, 0, 0, 0, 0x20178 }, +#ifdef CONFIG_DDB5074 + /* + * NEC Vrc-5074 (Nile 4) builtin UART. + * Conditionally compiled in since this is a motherboard device. + */ + { SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ + 64, 3, NULL, 0x300 }, +#endif +#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ /* pbn_dci_pccom8 */ + { SPCI_FL_BASE3, 8, 115200, 8 }, +#endif + { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ + 0, 0, pci_xircom_fn }, + + { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ + 0, 0, pci_siig20x_fn }, + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ + 0, 0, pci_siig20x_fn }, + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ + 0, 0, pci_siig20x_fn }, + + { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ + 0x40, 2, NULL, 0x200 }, + { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ + 0x40, 2, NULL, 0x200 }, + { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ + 0x40, 2, NULL, 0x200 }, +}; + +/* + * Given a complete unknown PCI device, try to use some heuristics to + * guess what the configuration might be, based on the pitiful PCI + * serial specs. Returns 0 on success, 1 on failure. + */ +static int __devinit serial_pci_guess_board(struct pci_dev *dev, + struct pci_board *board) +{ + int num_iomem = 0, num_port = 0, first_port = -1; + int i; + + /* + * If it is not a communications device or the programming + * interface is greater than 6, give up. + * + * (Should we try to make guesses for multiport serial devices + * later?) + */ + if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && + ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || + (dev->class & 0xff) > 6) + return 1; + + for (i=0; i < 6; i++) { + if (IS_PCI_REGION_IOPORT(dev, i)) { + num_port++; + if (first_port == -1) + first_port = i; + } + if (IS_PCI_REGION_IOMEM(dev, i)) + num_iomem++; + } + + /* + * If there is 1 or 0 iomem regions, and exactly one port, use + * it. + */ + if (num_iomem <= 1 && num_port == 1) { + board->flags = first_port; + return 0; + } + return 1; +} + +static int __devinit serial_init_one(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + struct pci_board *board, tmp; + int rc; + + board = &pci_boards[ent->driver_data]; + + rc = pci_enable_device(dev); + if (rc) return rc; + + if (ent->driver_data == pbn_default && + serial_pci_guess_board(dev, board)) + return -ENODEV; + else if (serial_pci_guess_board(dev, &tmp) == 0) { + printk(KERN_INFO "Redundant entry in serial pci_table. " + "Please send the output of\n" + "lspci -vv, this message (%d,%d,%d,%d)\n" + "and the manufacturer and name of " + "serial board or modem board\n" + "to serial-pci-info@lists.sourceforge.net.\n", + dev->vendor, dev->device, + pci_get_subvendor(dev), pci_get_subdevice(dev)); + } + + start_pci_pnp_board(dev, board); + + return 0; +} + +static void __devexit serial_remove_one(struct pci_dev *dev) +{ + int i; + + /* + * Iterate through all of the ports finding those that belong + * to this PCI device. + */ + for(i = 0; i < NR_PORTS; i++) { + if (rs_table[i].dev != dev) + continue; + unregister_serial(i); + rs_table[i].dev = 0; + } + /* + * Now execute any board-specific shutdown procedure + */ + for (i=0; i < NR_PCI_BOARDS; i++) { + struct pci_board_inst *brd = &serial_pci_board[i]; + + if (serial_pci_board[i].dev != dev) + continue; + if (brd->board.init_fn) + (brd->board.init_fn)(brd->dev, &brd->board, 0); + if (DEACTIVATE_FUNC(brd->dev)) + (DEACTIVATE_FUNC(brd->dev))(brd->dev); + serial_pci_board[i].dev = 0; + } +} + + +static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, - SPCI_FL_BASE1, 8, 1382400 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, - SPCI_FL_BASE1, 4, 1382400 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, - SPCI_FL_BASE1, 2, 1382400 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, - SPCI_FL_BASE1, 8, 1382400 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, - SPCI_FL_BASE1, 4, 1382400 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, - SPCI_FL_BASE1, 2, 1382400 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, - SPCI_FL_BASE1, 8, 921600 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, + pbn_b1_8_921600 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, - SPCI_FL_BASE1, 8, 921600 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, + pbn_b1_8_921600 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, - SPCI_FL_BASE1, 4, 921600 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, + pbn_b1_4_921600 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, - SPCI_FL_BASE1, 4, 921600 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, + pbn_b1_4_921600 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, - SPCI_FL_BASE1, 2, 921600 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, + pbn_b1_2_921600 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, - SPCI_FL_BASE1, 8, 921600 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, + pbn_b1_8_921600 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, - SPCI_FL_BASE1, 8, 921600 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, + pbn_b1_8_921600 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, - SPCI_FL_BASE1, 4, 921600 }, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_1_115200 }, { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2, 8, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, /* VScom SPCOM800, from sl@s.pl */ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2, 8, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_921600 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2, 4, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_4_921600 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_KEYSPAN, - PCI_SUBDEVICE_ID_KEYSPAN_SX2, - SPCI_FL_BASE2, 2, 921600, /* IOMEM */ - 0x400, 7, pci_plx9050_fn }, + PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, + pbn_panacom }, { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, - 0x400, 7, pci_plx9050_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom4 }, { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, - 0x400, 7, pci_plx9050_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom2 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST4, - SPCI_FL_BASE2, 4, 460800 }, + PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, + pbn_b2_4_460800 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST8, - SPCI_FL_BASE2, 8, 460800 }, + PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, + pbn_b2_8_460800 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16, - SPCI_FL_BASE2, 16, 460800 }, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, + pbn_b2_16_460800 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, - SPCI_FL_BASE2, 16, 460800 }, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, + pbn_b2_16_460800 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS4, - SPCI_FL_BASE2, 4, 460800 }, + PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, + pbn_b2_4_460800 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS8, - SPCI_FL_BASE2, 8, 460800 }, + PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, + pbn_b2_8_460800 }, /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ /* (Exoray@isys.ca) */ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, - 0x10b5, 0x106a, - SPCI_FL_BASE2, 4, 921600, - 0x20, 2, pci_plx9050_fn, 0x03 }, + 0x10b5, 0x106a, 0, 0, + pbn_plx_romulus }, { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1, 4, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1, 2, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1, 8, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1, 8, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, - SPCI_FL_BASE0 , 4, 921600 }, + PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0, + pbn_b0_4_921600 }, { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 , 4, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_115200 }, { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 , 2, 115200 }, - /* This board uses the size of PCI Base region 0 to - * signal now many ports are available */ - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, - { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, - PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, - SPCI_FL_BASE_TABLE, 1, 921600, - 0, 0, pci_timedia_fn }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_115200 }, + + /* Digitan DS560-558, from jimd@esoft.com */ + { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_115200 }, + + /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ + { PCI_VENDOR_ID_USR, 0x1008, + PCI_ANY_ID, PCI_ANY_ID, }, + + /* Titan Electronic cards */ + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2, 1, 460800, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2, 1, 460800, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2, 1, 460800, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2, 1, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2, 1, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2, 1, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, - 0, 0, pci_siig10x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, - 0, 0, pci_siig20x_fn }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, + /* Computone devices submitted by Doug McNash dmcnash@computone.com */ { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, - SPCI_FL_BASE0, 4, 921600, /* IOMEM */ - 0x40, 2, NULL, 0x200 }, + 0, 0, pbn_computone_4 }, { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, - SPCI_FL_BASE0, 8, 921600, /* IOMEM */ - 0x40, 2, NULL, 0x200 }, + 0, 0, pbn_computone_8 }, { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, - SPCI_FL_BASE0, 6, 921600, /* IOMEM */ - 0x40, 2, NULL, 0x200 }, - /* Digitan DS560-558, from jimd@esoft.com */ - { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1, 1, 115200 }, - /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ - { PCI_VENDOR_ID_USR, 0x1008, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 115200 }, - /* Titan Electronic cards */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 2, 921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 4, 921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 4, 921600 }, - /* EKF addition for i960 Boards form EKF with serial port */ - { PCI_VENDOR_ID_INTEL, 0x1960, - 0xE4BF, PCI_ANY_ID, - SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ - 8<<2, 2, pci_inteli960ni_fn, 0x10000}, + 0, 0, pbn_computone_6 }, + + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi }, + { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia }, + + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_460800 }, + /* RAStel 2 port modem, gerg@moreton.com.au */ { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + + /* EKF addition for i960 Boards form EKF with serial port */ + { PCI_VENDOR_ID_INTEL, 0x1960, + 0xE4BF, PCI_ANY_ID, 0, 0, + pbn_intel_i960 }, + + /* Xircom Cardbus/Ethernet combos */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_xircom_combo }, + /* * Untested PCI modems, sent in from various folks... */ + /* Elsa Model 56K PCI Modem, from Andreas Rath */ { PCI_VENDOR_ID_ROCKWELL, 0x1004, - 0x1048, 0x1500, - SPCI_FL_BASE1, 1, 115200 }, + 0x1048, 0x1500, 0, 0, + pbn_b1_1_115200 }, + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, - 0xFF00, 0, SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, - 1, 458333, 0, 0, 0, 0x20178 }, -#if CONFIG_DDB5074 + 0xFF00, 0, 0, 0, + pbn_sgi_ioc3 }, + +#ifdef CONFIG_DDB5074 /* * NEC Vrc-5074 (Nile 4) builtin UART. * Conditionally compiled in since this is a motherboard device. */ { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE0, 1, 520833, - 64, 3, NULL, 0x300 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_nec_nile4 }, #endif + #if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE3, 8, 115200, - 8 }, -#endif - /* Generic serial board */ - { 0, 0, - 0, 0, - SPCI_FL_BASE0, 1, 115200 }, -}; - -/* - * Given a complete unknown PCI device, try to use some heuristics to - * guess what the configuration might be, based on the pitiful PCI - * serial specs. Returns 0 on success, 1 on failure. - */ -static int _INLINE_ serial_pci_guess_board(struct pci_dev *dev, - struct pci_board *board) -{ - int num_iomem = 0, num_port = 0, first_port = -1; - int i; - - /* - * If it is not a communications device or the programming - * interface is greater than 6, give up. - * - * (Should we try to make guesses for multiport serial devices - * later?) - */ - if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && - ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || - (dev->class & 0xff) > 6) - return 1; - - for (i=0; i < 6; i++) { - if (IS_PCI_REGION_IOPORT(dev, i)) { - num_port++; - if (first_port == -1) - first_port = i; - } - if (IS_PCI_REGION_IOMEM(dev, i)) - num_iomem++; - } - - /* - * If there is 1 or 0 iomem regions, and exactly one port, use - * it. - */ - if (num_iomem <= 1 && num_port == 1) { - board->flags = first_port; - return 0; - } - return 1; -} - -static int __devinit serial_init_one(struct pci_dev *dev, - const struct pci_device_id *ent) -{ - struct pci_board *board, tmp; - int rc; - - for (board = pci_boards; board->vendor; board++) { - if (board->vendor != (unsigned short) PCI_ANY_ID && - dev->vendor != board->vendor) - continue; - if (board->device != (unsigned short) PCI_ANY_ID && - dev->device != board->device) - continue; - if (board->subvendor != (unsigned short) PCI_ANY_ID && - pci_get_subvendor(dev) != board->subvendor) - continue; - if (board->subdevice != (unsigned short) PCI_ANY_ID && - pci_get_subdevice(dev) != board->subdevice) - continue; - break; - } - - rc = pci_enable_device(dev); - if (rc) return rc; - - if (board->vendor == 0 && serial_pci_guess_board(dev, board)) - return -ENODEV; - else if (serial_pci_guess_board(dev, &tmp) == 0) { - printk(KERN_INFO "Redundant entry in serial pci_table. " - "Please send the output of\n" - "lspci -vv, this message (%d,%d,%d,%d)\n" - "and the manufacturer and name of " - "serial board or modem board\n" - "to serial-pci-info@lists.sourceforge.net.\n", - dev->vendor, dev->device, - pci_get_subvendor(dev), pci_get_subdevice(dev)); - } - - start_pci_pnp_board(dev, board); - - return 0; -} - -static void __devexit serial_remove_one(struct pci_dev *dev) -{ - int i; - - /* - * Iterate through all of the ports finding those that belong - * to this PCI device. - */ - for(i = 0; i < NR_PORTS; i++) { - if (rs_table[i].dev != dev) - continue; - unregister_serial(i); - rs_table[i].dev = 0; - } - /* - * Now execute any board-specific shutdown procedure - */ - for (i=0; i < NR_PCI_BOARDS; i++) { - struct pci_board_inst *brd = &serial_pci_board[i]; - - if (serial_pci_board[i].dev != dev) - continue; - if (brd->board.init_fn) - (brd->board.init_fn)(brd->dev, &brd->board, 0); - if (DEACTIVATE_FUNC(brd->dev)) - (DEACTIVATE_FUNC(brd->dev))(brd->dev); - serial_pci_board[i].dev = 0; - } -} - + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_dci_pccom8 }, +#endif -static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00, }, { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, @@ -5153,11 +5299,9 @@ break; if (pnp_board->vendor) { - board.vendor = pnp_board->vendor; - board.device = pnp_board->device; /* Special case that's more efficient to hardcode */ - if ((board.vendor == ISAPNP_VENDOR('A', 'K', 'Y') && - board.device == ISAPNP_DEVICE(0x1021))) + if ((pnp_board->vendor == ISAPNP_VENDOR('A', 'K', 'Y') && + pnp_board->device == ISAPNP_DEVICE(0x1021))) board.flags |= SPCI_FL_NO_SHIRQ; } else { if (serial_pnp_guess_board(dev, &board)) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/serial167.c linux.ac/drivers/char/serial167.c --- linux.vanilla/drivers/char/serial167.c Sat May 26 16:53:03 2001 +++ linux.ac/drivers/char/serial167.c Thu May 17 14:05:36 2001 @@ -2743,7 +2743,7 @@ * Of course, once the console has been registered, we had better ensure * that serial167_init() doesn't leave the chip non-functional. * - * The console_lock must be held when we get here. + * The console must be locked when we get here. */ void serial167_console_write(struct console *co, const char *str, unsigned count) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/serial_amba.c linux.ac/drivers/char/serial_amba.c --- linux.vanilla/drivers/char/serial_amba.c Fri Feb 9 19:30:22 2001 +++ linux.ac/drivers/char/serial_amba.c Tue Apr 3 17:54:40 2001 @@ -1881,7 +1881,7 @@ * Print a string to the serial port trying not to disturb * any possible real use of the port... * - * The console_lock must be held when we get here. + * The console must be locked when we get here. */ static void ambauart_console_write(struct console *co, const char *s, u_int count) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/sx.c linux.ac/drivers/char/sx.c --- linux.vanilla/drivers/char/sx.c Mon Apr 30 15:13:14 2001 +++ linux.ac/drivers/char/sx.c Thu May 17 20:54:13 2001 @@ -918,7 +918,6 @@ SP_DCEN); sx_write_channel_byte (port, hi_break, - I_OTHER(port->gs.tty) ? 0: (I_IGNBRK(port->gs.tty)?BR_IGN:0 | I_BRKINT(port->gs.tty)?BR_INT:0)); @@ -1140,9 +1139,7 @@ sx_dprintk (SX_DEBUG_MODEMSIGNALS, "got a break.\n"); sx_write_channel_byte (port, hi_state, hi_state); - if (port->gs.flags & ASYNC_SAK) { - do_SAK (port->gs.tty); - } + gs_got_break (port); } if (hi_state & ST_DCD) { hi_state &= ~ST_DCD; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/sysrq.c linux.ac/drivers/char/sysrq.c --- linux.vanilla/drivers/char/sysrq.c Fri Feb 9 19:30:22 2001 +++ linux.ac/drivers/char/sysrq.c Fri May 4 17:15:53 2001 @@ -6,6 +6,10 @@ * * (c) 1997 Martin Mares * based on ideas by Pavel Machek + * + * (c) 2000 Crutcher Dunnavant + * overhauled to use key registration + * based upon discusions in irc://irc.openprojects.net/#kernelnewbies */ #include @@ -23,6 +27,9 @@ #include #include #include +#include + +#include #include @@ -36,136 +43,83 @@ /* Machine specific power off function */ void (*sysrq_power_off)(void); -EXPORT_SYMBOL(sysrq_power_off); - -/* Send a signal to all user processes */ +/* Loglevel sysrq handler */ +static void sysrq_handle_loglevel(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + int i; + i = key - '0'; + console_loglevel = 7; + printk("%d\n", i); + console_loglevel = i; +} +static struct sysrq_key_op sysrq_loglevel_op = { + handler: sysrq_handle_loglevel, + help_msg: "loglevel0-8", + action_msg: "Loglevel set to ", +}; -static void send_sig_all(int sig, int even_init) -{ - struct task_struct *p; - for_each_task(p) { - if (p->mm) { /* Not swapper nor kernel thread */ - if (p->pid == 1 && even_init) /* Ugly hack to kill init */ - p->pid = 0x8000; - force_sig(sig, p); - } - } +/* SAK sysrq handler */ +#ifdef CONFIG_VT +static void sysrq_handle_SAK(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + if (tty) + do_SAK(tty); + reset_vc(fg_console); } +static struct sysrq_key_op sysrq_SAK_op = { + handler: sysrq_handle_SAK, + help_msg: "saK", + action_msg: "SAK\n", +}; +#endif -/* - * This function is called by the keyboard handler when SysRq is pressed - * and any other keycode arrives. - */ -void handle_sysrq(int key, struct pt_regs *pt_regs, - struct kbd_struct *kbd, struct tty_struct *tty) -{ - int orig_log_level = console_loglevel; +/* unraw sysrq handler */ +static void sysrq_handle_unraw(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + if (kbd) + kbd->kbdmode = VC_XLATE; +} +static struct sysrq_key_op sysrq_unraw_op = { + handler: sysrq_handle_unraw, + help_msg: "unRaw", + action_msg: "Keyboard mode set to XLATE\n", +}; - if (!sysrq_enabled) - return; - if (!key) - return; +/* reboot sysrq handler */ +static void sysrq_handle_reboot(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + machine_restart(NULL); +} +static struct sysrq_key_op sysrq_reboot_op = { + handler: sysrq_handle_reboot, + help_msg: "reBoot", + action_msg: "Resetting\n", +}; - console_loglevel = 7; - printk(KERN_INFO "SysRq: "); - switch (key) { - case 'r': /* R -- Reset raw mode */ - if (kbd) { - kbd->kbdmode = VC_XLATE; - printk("Keyboard mode set to XLATE\n"); - } - break; -#ifdef CONFIG_VT - case 'k': /* K -- SAK */ - printk("SAK\n"); - if (tty) - do_SAK(tty); - reset_vc(fg_console); - break; -#endif - case 'b': /* B -- boot immediately */ - printk("Resetting\n"); - machine_restart(NULL); - break; - case 'o': /* O -- power off */ - if (sysrq_power_off) { - printk("Power off\n"); - sysrq_power_off(); - } - break; - case 's': /* S -- emergency sync */ - printk("Emergency Sync\n"); - emergency_sync_scheduled = EMERG_SYNC; - wakeup_bdflush(0); - break; - case 'u': /* U -- emergency remount R/O */ - printk("Emergency Remount R/O\n"); - emergency_sync_scheduled = EMERG_REMOUNT; - wakeup_bdflush(0); - break; - case 'p': /* P -- show PC */ - printk("Show Regs\n"); - if (pt_regs) - show_regs(pt_regs); - break; - case 't': /* T -- show task info */ - printk("Show State\n"); - show_state(); - break; - case 'm': /* M -- show memory info */ - printk("Show Memory\n"); - show_mem(); - break; - case '0' ... '9': /* 0-9 -- set console logging level */ - orig_log_level = key - '0'; - printk("Log level set to %d\n", orig_log_level); - break; - case 'e': /* E -- terminate all user processes */ - printk("Terminate All Tasks\n"); - send_sig_all(SIGTERM, 0); - orig_log_level = 8; /* We probably have killed syslogd */ - break; - case 'i': /* I -- kill all user processes */ - printk("Kill All Tasks\n"); - send_sig_all(SIGKILL, 0); - orig_log_level = 8; - break; - case 'l': /* L -- kill all processes including init */ - printk("Kill ALL Tasks (even init)\n"); - send_sig_all(SIGKILL, 1); - orig_log_level = 8; - break; - default: /* Unknown: help */ - if (kbd) - printk("unRaw "); -#ifdef CONFIG_VT - if (tty) - printk("saK "); -#endif - printk("Boot "); - if (sysrq_power_off) - printk("Off "); - printk("Sync Unmount showPc showTasks showMem loglevel0-8 tErm kIll killalL\n"); - /* Don't use 'A' as it's handled specially on the Sparc */ - } - console_loglevel = orig_log_level; -} -/* Aux routines for the syncer */ +/* SYNC SYSRQ HANDLERS BLOCK */ -static int is_local_disk(kdev_t dev) /* Guess if the device is a local hard drive */ -{ - unsigned int major = MAJOR(dev); +/* do_emergency_sync helper function */ +/* Guesses if the device is a local hard drive */ +static int is_local_disk(kdev_t dev) { + unsigned int major; + major = MAJOR(dev); switch (major) { case IDE0_MAJOR: case IDE1_MAJOR: case IDE2_MAJOR: case IDE3_MAJOR: + case IDE4_MAJOR: + case IDE5_MAJOR: + case IDE6_MAJOR: + case IDE7_MAJOR: + case IDE8_MAJOR: + case IDE9_MAJOR: case SCSI_DISK0_MAJOR: case SCSI_DISK1_MAJOR: case SCSI_DISK2_MAJOR: @@ -174,19 +128,24 @@ case SCSI_DISK5_MAJOR: case SCSI_DISK6_MAJOR: case SCSI_DISK7_MAJOR: + case XT_DISK_MAJOR: return 1; default: return 0; } } +/* do_emergency_sync helper function */ static void go_sync(struct super_block *sb, int remount_flag) { + int orig_loglevel; + orig_loglevel = console_loglevel; + console_loglevel = 7; printk(KERN_INFO "%sing device %s ... ", remount_flag ? "Remount" : "Sync", kdevname(sb->s_dev)); - if (remount_flag) { /* Remount R/O */ + if (remount_flag) { /* Remount R/O */ int ret, flags; struct list_head *p; @@ -216,12 +175,12 @@ } } else printk("nothing to do\n"); - } else { - fsync_dev(sb->s_dev); /* Sync only */ + } else { /* Sync only */ + fsync_dev(sb->s_dev); printk("OK\n"); } + console_loglevel = orig_loglevel; } - /* * Emergency Sync or Unmount. We cannot do it directly, so we set a special * flag and wake up the bdflush kernel thread which immediately calls this function. @@ -231,10 +190,10 @@ int emergency_sync_scheduled; -void do_emergency_sync(void) -{ +void do_emergency_sync(void) { struct super_block *sb; int remount_flag; + int orig_loglevel; lock_kernel(); remount_flag = (emergency_sync_scheduled == EMERG_REMOUNT); @@ -253,5 +212,276 @@ go_sync(sb, remount_flag); unlock_kernel(); + + orig_loglevel = console_loglevel; + console_loglevel = 7; printk(KERN_INFO "Done.\n"); + console_loglevel = orig_loglevel; +} + +static void sysrq_handle_sync(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + emergency_sync_scheduled = EMERG_SYNC; + wakeup_bdflush(0); +} +static struct sysrq_key_op sysrq_sync_op = { + handler: sysrq_handle_sync, + help_msg: "Sync", + action_msg: "Emergency Sync\n", +}; + +static void sysrq_handle_mountro(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + emergency_sync_scheduled = EMERG_REMOUNT; + wakeup_bdflush(0); +} +static struct sysrq_key_op sysrq_mountro_op = { + handler: sysrq_handle_mountro, + help_msg: "Unmount", + action_msg: "Emergency Remount R/0\n", +}; + +/* END SYNC SYSRQ HANDLERS BLOCK */ + + +/* SHOW SYSRQ HANDLERS BLOCK */ + +static void sysrq_handle_showregs(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + if (pt_regs) + show_regs(pt_regs); +} +static struct sysrq_key_op sysrq_showregs_op = { + handler: sysrq_handle_showregs, + help_msg: "showPc", + action_msg: "Show Regs\n", +}; + + +static void sysrq_handle_showstate(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + show_state(); +} +static struct sysrq_key_op sysrq_showstate_op = { + handler: sysrq_handle_showstate, + help_msg: "showTasks", + action_msg: "Show State\n", +}; + + +static void sysrq_handle_showmem(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + show_mem(); +} +static struct sysrq_key_op sysrq_showmem_op = { + handler: sysrq_handle_showmem, + help_msg: "showMem", + action_msg: "Show Memory\n", +}; + +/* SHOW SYSRQ HANDLERS BLOCK */ + + +/* SIGNAL SYSRQ HANDLERS BLOCK */ + +/* signal sysrq helper function + * Sends a signal to all user processes */ +static void send_sig_all(int sig, int even_init) +{ + struct task_struct *p; + + for_each_task(p) { + if (p->mm) { /* Not swapper nor kernel thread */ + if (p->pid == 1 && even_init) + /* Ugly hack to kill init */ + p->pid = 0x8000; + force_sig(sig, p); + } + } +} + +static void sysrq_handle_term(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + send_sig_all(SIGTERM, 0); + console_loglevel = 8; +} +static struct sysrq_key_op sysrq_term_op = { + handler: sysrq_handle_term, + help_msg: "tErm", + action_msg: "Terminate All Tasks\n", +}; + +static void sysrq_handle_kill(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + send_sig_all(SIGKILL, 0); + console_loglevel = 8; +} +static struct sysrq_key_op sysrq_kill_op = { + handler: sysrq_handle_kill, + help_msg: "kIll", + action_msg: "Kill All Tasks\n", +}; + +static void sysrq_handle_killall(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + send_sig_all(SIGKILL, 1); + console_loglevel = 8; +} +static struct sysrq_key_op sysrq_killall_op = { + handler: sysrq_handle_killall, + help_msg: "killalL", + action_msg: "Kill All Tasks (even init)\n", +}; + +/* END SIGNAL SYSRQ HANDLERS BLOCK */ + + +/* Key Operations table and lock */ +spinlock_t sysrq_key_table_lock = SPIN_LOCK_UNLOCKED; +#define SYSRQ_KEY_TABLE_LENGTH 36 +static struct sysrq_key_op *sysrq_key_table[SYSRQ_KEY_TABLE_LENGTH] = { +/* 0 */ &sysrq_loglevel_op, +/* 1 */ &sysrq_loglevel_op, +/* 2 */ &sysrq_loglevel_op, +/* 3 */ &sysrq_loglevel_op, +/* 4 */ &sysrq_loglevel_op, +/* 5 */ &sysrq_loglevel_op, +/* 6 */ &sysrq_loglevel_op, +/* 7 */ &sysrq_loglevel_op, +/* 8 */ &sysrq_loglevel_op, +/* 9 */ &sysrq_loglevel_op, +/* a */ NULL, /* Don't use for system provided sysrqs, + it is handled specially on the spark + and will never arive */ +/* b */ &sysrq_reboot_op, +/* c */ NULL, +/* d */ NULL, +/* e */ &sysrq_term_op, +/* f */ NULL, +/* g */ NULL, +/* h */ NULL, +/* i */ &sysrq_kill_op, +/* j */ NULL, +#ifdef CONFIG_VT +/* k */ &sysrq_SAK_op, +#else +/* k */ NULL, +#endif +/* l */ &sysrq_killall_op, +/* m */ &sysrq_showmem_op, +/* n */ NULL, +/* o */ NULL, /* This will often be registered + as 'Off' at init time */ +/* p */ &sysrq_showregs_op, +/* q */ NULL, +/* r */ &sysrq_unraw_op, +/* s */ &sysrq_sync_op, +/* t */ &sysrq_showstate_op, +/* u */ &sysrq_mountro_op, +/* v */ NULL, +/* w */ NULL, +/* x */ NULL, +/* w */ NULL, +/* z */ NULL +}; + +/* key2index calculation, -1 on invalid index */ +static __inline__ int sysrq_key_table_key2index(int key) { + int retval; + if ((key >= '0') & (key <= '9')) { + retval = key - '0'; + } else if ((key >= 'a') & (key <= 'z')) { + retval = key + 10 - 'a'; + } else { + retval = -1; + } + return retval; +} + +/* + * table lock and unlocking functions, exposed to modules + */ + +void __sysrq_lock_table (void) { spin_lock(&sysrq_key_table_lock); } + +void __sysrq_unlock_table (void) { spin_unlock(&sysrq_key_table_lock); } + +/* + * get and put functions for the table, exposed to modules. + */ + +struct sysrq_key_op *__sysrq_get_key_op (int key) { + struct sysrq_key_op *op_p; + int i; + + i = sysrq_key_table_key2index(key); + op_p = (i == -1) ? NULL : sysrq_key_table[i]; + return op_p; } + +void __sysrq_put_key_op (int key, struct sysrq_key_op *op_p) { + int i; + + i = sysrq_key_table_key2index(key); + if (i != -1) + sysrq_key_table[i] = op_p; +} + +/* + * This function is called by the keyboard handler when SysRq is pressed + * and any other keycode arrives. + */ + +void handle_sysrq(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + if (!sysrq_enabled) + return; + + __sysrq_lock_table(); + __handle_sysrq_nolock(key, pt_regs, kbd, tty); + __sysrq_unlock_table(); +} + +/* + * This is the non-locking version of handle_sysrq + * It must/can only be called by sysrq key handlers, + * as they are inside of the lock + */ + +void __handle_sysrq_nolock(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, struct tty_struct *tty) { + struct sysrq_key_op *op_p; + int orig_log_level; + int i, j; + + if (!sysrq_enabled) + return; + + orig_log_level = console_loglevel; + console_loglevel = 7; + printk(KERN_INFO "SysRq : "); + + op_p = __sysrq_get_key_op(key); + if (op_p) { + printk ("%s", op_p->action_msg); + op_p->handler(key, pt_regs, kbd, tty); + } else { + printk("HELP : "); + /* Only print the help msg once per handler */ + for (i=0; ihelp_msg); + } + printk ("\n"); + } + console_loglevel = orig_log_level; +} + +EXPORT_SYMBOL(handle_sysrq); +EXPORT_SYMBOL(__handle_sysrq_nolock); +EXPORT_SYMBOL(__sysrq_lock_table); +EXPORT_SYMBOL(__sysrq_unlock_table); +EXPORT_SYMBOL(__sysrq_get_key_op); +EXPORT_SYMBOL(__sysrq_put_key_op); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/tty_io.c linux.ac/drivers/char/tty_io.c --- linux.vanilla/drivers/char/tty_io.c Mon Apr 30 15:13:14 2001 +++ linux.ac/drivers/char/tty_io.c Sat May 26 00:24:54 2001 @@ -148,6 +148,7 @@ extern long serial167_console_init(void); extern void console_8xx_init(void); extern int rs_8xx_init(void); +extern void mac_scc_console_init(void); extern void hwc_console_init(void); extern void hwc_tty_init(void); extern void con3215_init(void); @@ -158,6 +159,8 @@ extern void sa1100_rs_console_init(void); extern void sgi_serial_console_init(void); extern void sci_console_init(void); +extern void viocons_init(void); +extern void viocons_init2(void); #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -1197,7 +1200,7 @@ * We've decremented tty->count, so we should zero out * filp->private_data, to break the link between the tty and * the file descriptor. Otherwise if filp_close() blocks before - * the the file descriptor is removed from the inuse_filp + * the file descriptor is removed from the inuse_filp * list, check_tty_count() could observe a discrepancy and * printk a warning message to the user. */ @@ -2054,6 +2057,7 @@ EXPORT_SYMBOL(tty_register_devfs); EXPORT_SYMBOL(tty_unregister_devfs); +EXPORT_SYMBOL(tty_register_ldisc); /* * Called by a tty driver to register itself. @@ -2178,12 +2182,21 @@ * set up the console device so that later boot sequences can * inform about problems etc.. */ + +#ifdef CONFIG_VIOCONS + viocons_init(); +#endif + #ifdef CONFIG_VT con_init(); #endif #ifdef CONFIG_SERIAL_CONSOLE #if (defined(CONFIG_8xx) || defined(CONFIG_8260)) console_8xx_init(); +#elif defined(CONFIG_MAC_SERIAL) + mac_scc_console_init(); +#elif defined(CONFIG_PARISC) + pdc_console_init(); #elif defined(CONFIG_SERIAL) serial_console_init(); #endif /* CONFIG_8xx */ @@ -2209,6 +2222,9 @@ #ifdef CONFIG_HWC hwc_console_init(); #endif +#ifdef CONFIG_STDIO_CONSOLE + stdio_console_init(); +#endif #ifdef CONFIG_SERIAL_21285_CONSOLE rs285_console_init(); #endif @@ -2268,6 +2284,10 @@ /* console calls tty_register_driver() before kmalloc() works. * Thus, we can't devfs_register() then. Do so now, instead. */ +#ifdef CONFIG_VIOCONS + viocons_init2(); +#endif + #ifdef CONFIG_VT con_init_devfs(); #endif @@ -2298,6 +2318,7 @@ kbd_init(); #endif + #ifdef CONFIG_ESPSERIAL /* init ESP before rs, so rs doesn't see the port */ espserial_init(); #endif @@ -2358,5 +2379,8 @@ #endif #ifdef CONFIG_HWC hwc_tty_init(); +#endif +#ifdef CONFIG_A2232 + a2232board_init(); #endif } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/tty_ioctl.c linux.ac/drivers/char/tty_ioctl.c --- linux.vanilla/drivers/char/tty_ioctl.c Thu Mar 16 03:48:06 2000 +++ linux.ac/drivers/char/tty_ioctl.c Tue Apr 3 17:54:41 2001 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -519,3 +520,5 @@ return -ENOIOCTLCMD; } } + +EXPORT_SYMBOL(n_tty_ioctl); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/vc_screen.c linux.ac/drivers/char/vc_screen.c --- linux.vanilla/drivers/char/vc_screen.c Mon Oct 16 20:58:51 2000 +++ linux.ac/drivers/char/vc_screen.c Tue Apr 3 17:54:41 2001 @@ -109,7 +109,7 @@ /* Select the proper current console and verify * sanity of the situation under the console lock. */ - spin_lock_irq(&console_lock); + acquire_console_sem(); attr = (currcons & 128); currcons = (currcons & 127); @@ -232,13 +232,16 @@ } } - /* Finally, temporarily drop the console lock and push + /* Finally, release the console semaphore while we push * all the data to userspace from our temporary buffer. + * + * AKPM: Even though it's a semaphore, we should drop it because + * the pagefault handling code may want to call printk(). */ - spin_unlock_irq(&console_lock); + release_console_sem(); ret = copy_to_user(buf, con_buf_start, orig_count); - spin_lock_irq(&console_lock); + acquire_console_sem(); if (ret) { read += (orig_count - ret); @@ -254,7 +257,7 @@ if (read) ret = read; unlock_out: - spin_unlock_irq(&console_lock); + release_console_sem(); up(&con_buf_sem); return ret; } @@ -276,7 +279,7 @@ /* Select the proper current console and verify * sanity of the situation under the console lock. */ - spin_lock_irq(&console_lock); + acquire_console_sem(); attr = (currcons & 128); currcons = (currcons & 127); @@ -310,9 +313,9 @@ /* Temporarily drop the console lock so that we can read * in the write data from userspace safely. */ - spin_unlock_irq(&console_lock); + release_console_sem(); ret = copy_from_user(con_buf, buf, this_round); - spin_lock_irq(&console_lock); + acquire_console_sem(); if (ret) { this_round -= ret; @@ -436,7 +439,7 @@ ret = written; unlock_out: - spin_unlock_irq(&console_lock); + release_console_sem(); up(&con_buf_sem); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/viocons.c linux.ac/drivers/char/viocons.c --- linux.vanilla/drivers/char/viocons.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/viocons.c Sat May 26 00:24:54 2001 @@ -0,0 +1,1464 @@ +/* + * drivers/char/viocons.c + * + * iSeries Virtual Terminal + * + * Author: Dave Boutcher + * (C) Copyright 2000 IBM Corporation + * + * 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) anyu later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif + +#ifndef _HVLPEVENT_H +#include +#endif + +#ifndef _HVCALLEVENT_H +#include "asm/iSeries/HvCallEvent.h" +#endif +#ifndef _HVLPCONFIG_H +#include "asm/iSeries/HvLpConfig.h" +#endif +#include "asm/iSeries/HvCall.h" +#ifndef _ISERIES_PROC_H +#include +#endif + +/*************************************************************************** + * Check that the tty_driver_data actually points to our stuff + ***************************************************************************/ +#define VIOTTY_PARANOIA_CHECK 1 +#define VIOTTY_MAGIC (0x0DCB) + +static int debug; + +static DECLARE_WAIT_QUEUE_HEAD(viocons_wait_queue); + +static int viotty_major = 229; + +#define VTTY_PORTS 10 + +static u64 sndMsgSeq[VTTY_PORTS]; +static u64 sndMsgAck[VTTY_PORTS]; + +static spinlock_t consolelock; + +/*************************************************************************** + * When we get writes faster than we can send it to the partition, + * buffer the data here. There is one set of buffers for each virtual + * port. + ***************************************************************************/ +/* Note that bufferUsed is a bit map of used buffers. + * It had better have enough bits to hold NUM_BUF + * the bitops assume it is a multiple of unsigned long + */ +#define NUM_BUF (8) +#define OVERFLOW_SIZE VIOCHAR_MAX_DATA + +static struct overflowBuffers { + unsigned long bufferUsed; + u8 *buffer[NUM_BUF]; + int bufferBytes[NUM_BUF]; + int curbuf; + int bufferOverflow; + int overflowMessage; +} overflow[VTTY_PORTS]; + +static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp); + +static struct tty_driver viotty_driver; +static int viotty_refcount; + +static struct tty_struct *viotty_table[VTTY_PORTS]; +static struct termios *viotty_termios[VTTY_PORTS]; +static struct termios *viotty_termios_locked[VTTY_PORTS]; + +static void hvlog(char *fmt, ...) +{ + int i; + static char buf[256]; + va_list args; + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + HvCall_writeLogBuffer(buf, i); + HvCall_writeLogBuffer("\r",1); + +} + +/*************************************************************************** + * Our port information. We store a pointer to one entry in the + * tty_driver_data + ***************************************************************************/ +static struct port_info_tag { + int magic; + int count; + struct tty_struct *tty; + HvLpIndex lp; + u8 vcons; + u8 port; +} port_info[VTTY_PORTS]; + +/*************************************************************************** + * Make sure we're pointing to a valid port_info structure. Shamelessly + * plagerized from serial.c + ***************************************************************************/ +static inline int viotty_paranoia_check(struct port_info_tag *pi, + kdev_t device, const char *routine) +{ +#ifdef VIOTTY_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for port_info struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null port_info for (%s) in %s\n"; + + if (!pi) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (pi->magic != VIOTTY_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + +/*************************************************************************** + * Handle reads from the proc file system. Right now we just dump the + * state of the first TTY + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + struct tty_struct *tty = viotty_table[0]; + struct termios *termios; + if (tty == NULL) + { + len += sprintf(buf+len,"no tty\n"); + *eof = 1; + return len; + } + + len += sprintf(buf+len,"tty info: COOK_OUT %ld COOK_IN %ld, NO_WRITE_SPLIT %ld\n", + tty->flags & TTY_HW_COOK_OUT, + tty->flags & TTY_HW_COOK_IN, + tty->flags & TTY_NO_WRITE_SPLIT); + + termios = tty->termios; + if (termios == NULL) + { + len += sprintf(buf+len,"no termios\n"); + *eof = 1; + return len; + } +len+= sprintf(buf+len,"INTR_CHAR %2.2x\n",INTR_CHAR(tty)); +len+= sprintf(buf+len,"QUIT_CHAR %2.2x\n",QUIT_CHAR(tty)); +len+= sprintf(buf+len,"ERASE_CHAR %2.2x\n",ERASE_CHAR(tty)); +len+= sprintf(buf+len,"KILL_CHAR %2.2x\n",KILL_CHAR(tty)); +len+= sprintf(buf+len,"EOF_CHAR %2.2x\n",EOF_CHAR(tty)); +len+= sprintf(buf+len,"TIME_CHAR %2.2x\n",TIME_CHAR(tty)); +len+= sprintf(buf+len,"MIN_CHAR %2.2x\n",MIN_CHAR(tty)); +len+= sprintf(buf+len,"SWTC_CHAR %2.2x\n",SWTC_CHAR(tty)); +len+= sprintf(buf+len,"START_CHAR %2.2x\n",START_CHAR(tty)); +len+= sprintf(buf+len,"STOP_CHAR %2.2x\n",STOP_CHAR(tty)); +len+= sprintf(buf+len,"SUSP_CHAR %2.2x\n",SUSP_CHAR(tty)); +len+= sprintf(buf+len,"EOL_CHAR %2.2x\n",EOL_CHAR(tty)); +len+= sprintf(buf+len,"REPRINT_CHAR %2.2x\n",REPRINT_CHAR(tty)); +len+= sprintf(buf+len,"DISCARD_CHAR %2.2x\n",DISCARD_CHAR(tty)); +len+= sprintf(buf+len,"WERASE_CHAR %2.2x\n",WERASE_CHAR(tty)); +len+= sprintf(buf+len,"LNEXT_CHAR %2.2x\n",LNEXT_CHAR(tty)); +len+= sprintf(buf+len,"EOL2_CHAR %2.2x\n",EOL2_CHAR(tty)); + +len+= sprintf(buf+len,"I_IGNBRK %4.4x\n",I_IGNBRK(tty)); +len+= sprintf(buf+len,"I_BRKINT %4.4x\n",I_BRKINT(tty)); +len+= sprintf(buf+len,"I_IGNPAR %4.4x\n",I_IGNPAR(tty)); +len+= sprintf(buf+len,"I_PARMRK %4.4x\n",I_PARMRK(tty)); +len+= sprintf(buf+len,"I_INPCK %4.4x\n",I_INPCK(tty)); +len+= sprintf(buf+len,"I_ISTRIP %4.4x\n",I_ISTRIP(tty)); +len+= sprintf(buf+len,"I_INLCR %4.4x\n",I_INLCR(tty)); +len+= sprintf(buf+len,"I_IGNCR %4.4x\n",I_IGNCR(tty)); +len+= sprintf(buf+len,"I_ICRNL %4.4x\n",I_ICRNL(tty)); +len+= sprintf(buf+len,"I_IUCLC %4.4x\n",I_IUCLC(tty)); +len+= sprintf(buf+len,"I_IXON %4.4x\n",I_IXON(tty)); +len+= sprintf(buf+len,"I_IXANY %4.4x\n",I_IXANY(tty)); +len+= sprintf(buf+len,"I_IXOFF %4.4x\n",I_IXOFF(tty)); +len+= sprintf(buf+len,"I_IMAXBEL %4.4x\n",I_IMAXBEL(tty)); + +len+= sprintf(buf+len,"O_OPOST %4.4x\n",O_OPOST(tty)); +len+= sprintf(buf+len,"O_OLCUC %4.4x\n",O_OLCUC(tty)); +len+= sprintf(buf+len,"O_ONLCR %4.4x\n",O_ONLCR(tty)); +len+= sprintf(buf+len,"O_OCRNL %4.4x\n",O_OCRNL(tty)); +len+= sprintf(buf+len,"O_ONOCR %4.4x\n",O_ONOCR(tty)); +len+= sprintf(buf+len,"O_ONLRET %4.4x\n",O_ONLRET(tty)); +len+= sprintf(buf+len,"O_OFILL %4.4x\n",O_OFILL(tty)); +len+= sprintf(buf+len,"O_OFDEL %4.4x\n",O_OFDEL(tty)); +len+= sprintf(buf+len,"O_NLDLY %4.4x\n",O_NLDLY(tty)); +len+= sprintf(buf+len,"O_CRDLY %4.4x\n",O_CRDLY(tty)); +len+= sprintf(buf+len,"O_TABDLY %4.4x\n",O_TABDLY(tty)); +len+= sprintf(buf+len,"O_BSDLY %4.4x\n",O_BSDLY(tty)); +len+= sprintf(buf+len,"O_VTDLY %4.4x\n",O_VTDLY(tty)); +len+= sprintf(buf+len,"O_FFDLY %4.4x\n",O_FFDLY(tty)); + +len+= sprintf(buf+len,"C_BAUD %4.4x\n",C_BAUD(tty)); +len+= sprintf(buf+len,"C_CSIZE %4.4x\n",C_CSIZE(tty)); +len+= sprintf(buf+len,"C_CSTOPB %4.4x\n",C_CSTOPB(tty)); +len+= sprintf(buf+len,"C_CREAD %4.4x\n",C_CREAD(tty)); +len+= sprintf(buf+len,"C_PARENB %4.4x\n",C_PARENB(tty)); +len+= sprintf(buf+len,"C_PARODD %4.4x\n",C_PARODD(tty)); +len+= sprintf(buf+len,"C_HUPCL %4.4x\n",C_HUPCL(tty)); +len+= sprintf(buf+len,"C_CLOCAL %4.4x\n",C_CLOCAL(tty)); +len+= sprintf(buf+len,"C_CRTSCTS %4.4x\n",C_CRTSCTS(tty)); + +len+= sprintf(buf+len,"L_ISIG %4.4x\n",L_ISIG(tty)); +len+= sprintf(buf+len,"L_ICANON %4.4x\n",L_ICANON(tty)); +len+= sprintf(buf+len,"L_XCASE %4.4x\n",L_XCASE(tty)); +len+= sprintf(buf+len,"L_ECHO %4.4x\n",L_ECHO(tty)); +len+= sprintf(buf+len,"L_ECHOE %4.4x\n",L_ECHOE(tty)); +len+= sprintf(buf+len,"L_ECHOK %4.4x\n",L_ECHOK(tty)); +len+= sprintf(buf+len,"L_ECHONL %4.4x\n",L_ECHONL(tty)); +len+= sprintf(buf+len,"L_NOFLSH %4.4x\n",L_NOFLSH(tty)); +len+= sprintf(buf+len,"L_TOSTOP %4.4x\n",L_TOSTOP(tty)); +len+= sprintf(buf+len,"L_ECHOCTL %4.4x\n",L_ECHOCTL(tty)); +len+= sprintf(buf+len,"L_ECHOPRT %4.4x\n",L_ECHOPRT(tty)); +len+= sprintf(buf+len,"L_ECHOKE %4.4x\n",L_ECHOKE(tty)); +len+= sprintf(buf+len,"L_FLUSHO %4.4x\n",L_FLUSHO(tty)); +len+= sprintf(buf+len,"L_PENDIN %4.4x\n",L_PENDIN(tty)); +len+= sprintf(buf+len,"L_IEXTEN %4.4x\n",L_IEXTEN(tty)); + + *eof = 1; + return len; +} + +/*************************************************************************** + * Handle writes to our proc file system. Right now just turns on and off + * our debug flag + ***************************************************************************/ +static int proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + if (count) + { + if (buffer[0] == '1') + { + printk("viocons: debugging on\n"); + debug = 1; + } + else + { + printk("viocons: debugging off\n"); + debug = 0; + } + } + return count; +} + +/*************************************************************************** + * setup our proc file system entries + ***************************************************************************/ +void viocons_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = create_proc_entry("viocons", S_IFREG|S_IRUSR, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/*************************************************************************** + * clean up our proc file system entries + ***************************************************************************/ +void viocons_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viocons", iSeries_proc); +} + +/*************************************************************************** + * Add data to our pending-send buffers + ***************************************************************************/ +static int bufferAdd(u8 port, const char *buf, size_t len, int userFlag) +{ + size_t bleft = len; + size_t curlen; + char *cbuf = (char *)buf; + int nextbuf; + + while (bleft > 0) + { + /* + * If there is no space left in the current buffer, we have + * filled everything up, so return. If we filled the previous + * buffer we would already have moved to the next one. + */ + if (overflow[port].bufferBytes[overflow[port].curbuf] == OVERFLOW_SIZE) + { + hvlog("buffer %d full. no more space\n",overflow[port].curbuf); + overflow[port].bufferOverflow++; + overflow[port].overflowMessage = 1; + return len - bleft; + } + + /* + * Turn on the "used" bit for this buffer. If it's already on, that's + * fine. + */ + set_bit(overflow[port].curbuf,&overflow[port].bufferUsed); + + /* + * See if this buffer has been allocated. If not, allocate it + */ + if (overflow[port].buffer[overflow[port].curbuf] == NULL) + overflow[port].buffer[overflow[port].curbuf] = + kmalloc(OVERFLOW_SIZE, GFP_ATOMIC); + + /* + * Figure out how much we can copy into this buffer + */ + if (bleft < (OVERFLOW_SIZE - overflow[port].bufferBytes[overflow[port].curbuf])) + curlen = bleft; + else + curlen = OVERFLOW_SIZE - overflow[port].bufferBytes[overflow[port].curbuf]; + + /* + * Copy the data into the buffer + */ + if (userFlag) + copy_from_user(overflow[port].buffer[overflow[port].curbuf]+ + overflow[port].bufferBytes[overflow[port].curbuf],cbuf,curlen); + else + memcpy(overflow[port].buffer[overflow[port].curbuf]+ + overflow[port].bufferBytes[overflow[port].curbuf],cbuf,curlen); + + overflow[port].bufferBytes[overflow[port].curbuf] += curlen; + cbuf += curlen; + bleft -= curlen; + + /* + * Now see if we've filled this buffer + */ + if (overflow[port].bufferBytes[overflow[port].curbuf] == VIOCHAR_MAX_DATA) + { + nextbuf = (overflow[port].curbuf+1)%NUM_BUF; + + /* + * Move to the next buffer if it hasn't been used yet + */ + if (test_bit(nextbuf,&overflow[port].bufferUsed) == 0) + { + overflow[port].curbuf = nextbuf; + } + } + } + return len; +} + +/*************************************************************************** + * Send pending data + ***************************************************************************/ +void sendBuffers(u8 port, HvLpIndex lp) +{ + HvLpEvent_Rc hvrc; + int nextbuf; + struct viocharlpevent *viochar = (struct viocharlpevent *)vio_char_event_buffer; + int flags; + + spin_lock_irqsave(&consolelock, + flags); + + if (overflow[port].bufferUsed == 0) + { + if (debug) printk("in sendbuffers, but no buffers used\n"); + spin_unlock_irqrestore(&consolelock, + flags); + + return; + } + + /* + * curbuf points to the buffer we're filling. We want to start sending AFTER + * this one. + */ + nextbuf = (overflow[port].curbuf + 1) % NUM_BUF; + + /* + * Loop until we find a buffer with the bufferUsed bit on + */ + while (test_bit(nextbuf,&overflow[port].bufferUsed) == 0) + nextbuf = (nextbuf + 1) % NUM_BUF; + + initDataEvent(viochar, lp); + + /* + * While we have buffers with data, and our send window is open, send them + */ + while ((test_bit(nextbuf, &overflow[port].bufferUsed)) && + ((sndMsgSeq[port] - sndMsgAck[port]) immediateDataLen = overflow[port].bufferBytes[nextbuf]; + viochar->event.xCorrelationToken = sndMsgSeq[port]++; + + memcpy(viochar->immediateData,overflow[port].buffer[nextbuf],viochar->immediateDataLen); + + hvrc = HvCallEvent_signalLpEvent(&viochar->event); + if (hvrc) + { + printk("viocons: error sending event! %d\n",(int)hvrc); + } + + /* + * clear the bufferUsed bit, zero the number of bytes in this buffer, + * and move to the next buffer + */ + clear_bit(nextbuf, &overflow[port].bufferUsed); + overflow[port].bufferBytes[nextbuf] = 0; + nextbuf = (nextbuf + 1) % NUM_BUF; + } + + + /* + * If we have emptied all the buffers, start at 0 again. + * this will re-use any allocated buffers + */ + if (overflow[port].bufferUsed == 0) + { + overflow[port].curbuf = 0; + + if (overflow[port].overflowMessage) + overflow[port].overflowMessage = 0; + + if (port_info[port].tty) + { + if ((port_info[port].tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + (port_info[port].tty->ldisc.write_wakeup)) + (port_info[port].tty->ldisc.write_wakeup) (port_info[port].tty); + wake_up_interruptible(&port_info[port].tty->write_wait); + } + } + + spin_unlock_irqrestore(&consolelock, + flags); + +} + +/*************************************************************************** + * Our internal writer. Gets called both from the console device and + * the tty device. the tty pointer will be NULL if called from the console. + ***************************************************************************/ +static int internal_write(struct tty_struct * tty, const char *buf, size_t len, int userFlag) +{ + HvLpEvent_Rc hvrc; + size_t bleft = len; + size_t curlen; + const char *curbuf = buf; + struct viocharlpevent *viochar = (struct viocharlpevent *)vio_char_event_buffer; + unsigned long flags; + struct port_info_tag * pi = NULL; + HvLpIndex lp; + u8 port; + + if (tty) + { + pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_internal_write")) + return -ENODEV; + + lp = pi->lp; + port = pi->port; + } + else + { + /* + * If this is the console device, use the lp from the first port entry + */ + port = 0; + lp = port_info[0].lp; + } + + /* + * Always put console output in the hypervisor console log + */ + if (port == 0) + HvCall_writeLogBuffer(buf, len); + + /* + * If the path to this LP is closed, don't bother doing anything more. + * just dump the data on the floor + */ + if (!viopath_isactive(lp)) + return len; + + /* + * If there is already data queued for this port, send it + */ + if (overflow[port].bufferUsed) + sendBuffers(port, lp); + + spin_lock_irqsave(&consolelock, + flags); + + initDataEvent(viochar, lp); + + /* Got the lock, don't cause console output */ + while ((bleft > 0) && + (overflow[port].bufferUsed == 0) && + ((sndMsgSeq[port] - sndMsgAck[port]) VIOCHAR_MAX_DATA) + curlen = VIOCHAR_MAX_DATA; + else + curlen = bleft; + + viochar->immediateDataLen = curlen; + viochar->event.xCorrelationToken = sndMsgSeq[port]++; + + if (userFlag) + copy_from_user(viochar->immediateData,curbuf,curlen); + else + memcpy(viochar->immediateData,curbuf,curlen); + + viochar->event.xSizeMinus1 = offsetof(struct viocharlpevent, immediateData) + + curlen; + + hvrc = HvCallEvent_signalLpEvent(&viochar->event); + if (hvrc) + { + printk("viocons: error sending event! %d\n",(int)hvrc); + } + + curbuf += curlen; + bleft -= curlen; + } + + /* + * If we didn't send it all, buffer it + */ + if (bleft > 0) + { + bleft -= bufferAdd(lp, curbuf,bleft, userFlag); + } + spin_unlock_irqrestore(&consolelock, + flags); + + if ((bleft) && (debug)) + printk("Unable to write all data\n"); + + return len - bleft; +} + +/*************************************************************************** + * Initialize the common fields in a charLpEvent + ***************************************************************************/ +static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp) +{ + memset(viochar, 0x00, sizeof(struct viocharlpevent)); + + viochar->event.xFlags.xValid = 1; + viochar->event.xFlags.xFunction = HvLpEvent_Function_Int; + viochar->event.xFlags.xAckInd = HvLpEvent_AckInd_NoAck; + viochar->event.xFlags.xAckType = HvLpEvent_AckType_DeferredAck; + viochar->event.xType = HvLpEvent_Type_VirtualIo; + viochar->event.xSubtype = viomajorsubtype_chario | viochardata; + viochar->event.xSourceLp = HvLpConfig_getLpIndex(); + viochar->event.xTargetLp = lp; + viochar->event.xSizeMinus1 = sizeof(struct viocharlpevent); + viochar->event.xSourceInstanceId = viopath_sourceinst(lp); + viochar->event.xTargetInstanceId = viopath_targetinst(lp); +} + + +/*************************************************************************** + * console device write + ***************************************************************************/ +static void viocons_write(struct console *co, const char *s, + unsigned count) +{ + /*** Ryan S. Arnold : ryanarn@us.ibm.com *** 05/03/2001 *********************** + * This parser will ensure that all single instances of either \n or \r are + * matched into carriage return/line feed combinations. It also allows for + * instances where there already exist \n\r combinations as well as the + * reverse, \r\n combinations. + */ + + int index; + int foundcr; + int foundlf; + int match; + int slicelen; + int slicebegin; + char charptr[1]; + char vioconswrite[14]; + + index = 0; + foundcr = 0; + foundlf = 0; + match = 0; + slicelen = 0; + slicebegin = 0; + +// strcpy( vioconswrite, "viocons_write:"); +// internal_write( NULL, vioconswrite, 14, 0 ); + + for( index = 0; index < count; index++ ) + { + if ( foundlf && s[index] != 0x0d ) + { + if ( !match ) + { + internal_write( NULL, &s[slicebegin], slicelen, 0 ); + charptr[0] = '\r'; + internal_write( NULL, charptr, 1, 0 ); + slicebegin = index; + slicelen = 1; + } + else //else match = 1; + { + match = 0; + slicelen++; + } + } + else if ( foundcr && s[index] == 0x0a && !match ) + { + match = 1; + slicelen++; + } + else if ( foundlf && s[index] == 0x0d && !match ) + { + match = 1; + slicelen++; + } + else + { + slicelen++; + } + + if ( s[index] == 0x0d ) + { + foundcr = 1; + foundlf = 0; + } + else if ( s[index] == 0x0a ) + { + foundlf = 1; + foundcr = 0; + } + else + { + foundcr = 0; + foundlf = 0; + } + } + + internal_write( NULL, &s[slicebegin], slicelen, 0 ); + + if ( foundlf && !match ) + { + charptr[0] = '\r'; + internal_write( NULL, charptr, 1, 0 ); + } + else if ( foundcr && !match ) + { + charptr[0] = '\n'; + internal_write( NULL, charptr, 1, 0 ); + } +/* + int index; + char charptr[1]; + int foundcr; + int slicebegin; + int sliceend; + + foundcr = 0; + slicebegin = 0; + sliceend = 0; + + for ( index = 0; index < count; index++ ) + { + if( !foundcr && s[index] == 0x0a ) + { + if ( (slicebegin - sliceend > 0) && sliceend < count ) + { + //internal_write(NULL, &s[slicebegin],sliceend - slicebegin +1, 0 ); + internal_write(NULL, &s[slicebegin],sliceend - slicebegin, 0 ); + //slicebegin = sliceend + 1; + slicebegin = sliceend; + } + charptr[0] = '\r'; + internal_write(NULL, charptr, 1, 0); + } + if( foundcr && s[index] != 0x0a ) + { + if ( ( index - 2 ) >= 0 ) + { + if ( s[index - 2] != 0x0a ) + { + //internal_write(NULL, &s[slicebegin],sliceend - slicebegin + 1, 0 ); + internal_write(NULL, &s[slicebegin],sliceend - slicebegin, 0 ); + //slicebegin = sliceend + 1; + slicebegin = sliceend; + charptr[0] = '\n'; + internal_write(NULL, charptr, 1, 0); + } + } + } + sliceend++; + + if( s[index] == 0x0d ) + foundcr = 1; + else + foundcr = 0; + } + + internal_write(NULL, &s[slicebegin], sliceend - slicebegin + 1, 0 ); + //internal_write(NULL, &s[slicebegin], count - slicebegin, 0 ); + + if ( count > 1) + { + if ( foundcr == 1 && s[count-1] != 0x0a ) + { + charptr[0] = '\n'; + internal_write(NULL, charptr, 1, 0); + } + else if ( s[count-1] == 0x0a && s[count-2] != 0x0d ) + { + + charptr[0] = '\r'; + internal_write(NULL, charptr, 1, 0); + } + } +*/ + +} + +/*************************************************************************** + * Work out a the device associate with this console + ***************************************************************************/ +static kdev_t viocons_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, c->index + viotty_driver.minor_start); +} + +/*************************************************************************** + * console device read method + ***************************************************************************/ +static int viocons_read( struct console *co, const char *s, + unsigned count ) +{ + printk("In viocons_read\n"); + // Implement me + interruptible_sleep_on( &viocons_wait_queue ); + return 0; +} + +/*************************************************************************** + * console device wait until a key is pressed + ***************************************************************************/ +static int viocons_wait_key( struct console *co ) +{ + printk("In viocons_wait_key\n"); + // Implement me + interruptible_sleep_on( &viocons_wait_queue ); + return 0; +} + +/*************************************************************************** + * Do console device setup + ***************************************************************************/ +static int __init viocons_setup(struct console *co, char *options) +{ + printk("viocons: in viocons_setup\n"); + spin_lock_init(&consolelock); + return 0; +} + +/*************************************************************************** + * console device I/O methods + ***************************************************************************/ +static struct console viocons = { + name: "ttyS", + write: viocons_write, + read: viocons_read, + device: viocons_device, + wait_key: viocons_wait_key, + setup: viocons_setup, + flags: CON_PRINTBUFFER, +}; + + +/*************************************************************************** + * TTY Open method + ***************************************************************************/ +static int viotty_open(struct tty_struct *tty, struct file * filp) +{ + int port; + unsigned long flags; + MOD_INC_USE_COUNT; + port = MINOR(tty->device) - tty->driver.minor_start; + if ((port < 0) || (port >= VTTY_PORTS)) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + spin_lock_irqsave(&consolelock, + flags); + tty->driver_data = &port_info[port]; + port_info[port].tty = tty; + port_info[port].count++; + spin_unlock_irqrestore(&consolelock, + flags); + return 0; +} + +/*************************************************************************** + * TTY Close method + ***************************************************************************/ +static void viotty_close(struct tty_struct *tty, struct file * filp) +{ + unsigned long flags; + int badCountFlag = 0; + struct port_info_tag * pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_close")) + return; + + spin_lock_irqsave(&consolelock, + flags); + if ((tty->count == 1) && (pi->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. pi->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + badCountFlag = 1; + pi->count = 1; + } + if (--pi->count < 0) { + badCountFlag = 1; + pi->count = 0; + } + + spin_unlock_irqrestore(&consolelock, + flags); + + if (badCountFlag) + printk("viotty_close: bad port count\n"); + MOD_DEC_USE_COUNT; +} + +/*************************************************************************** + * TTY Write method + ***************************************************************************/ +static int viotty_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + return internal_write(tty, buf, count, from_user); +} + +/*************************************************************************** + * TTY put_char method + ***************************************************************************/ +static void viotty_put_char(struct tty_struct *tty, unsigned char ch) +{ + internal_write(tty, &ch, 1, 0); +} + +/*************************************************************************** + * TTY flush_chars method + ***************************************************************************/ +static void viotty_flush_chars(struct tty_struct *tty) +{ +} + +/*************************************************************************** + * TTY write_room method + ***************************************************************************/ +static int viotty_write_room(struct tty_struct *tty) +{ + int i; + int room = 0; + struct port_info_tag * pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_sendbuffers")) + return 0; + + // If no buffers are used, return the max size + if (overflow[pi->port].bufferUsed == 0) + return VIOCHAR_MAX_DATA * NUM_BUF; + + for (i=0; ((iport].bufferBytes[i]); + } + + if (room > VIOCHAR_MAX_DATA) + return VIOCHAR_MAX_DATA; + else + return room; +} + +/*************************************************************************** + * TTY chars_in_buffer_room method + ***************************************************************************/ +static int viotty_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + +static void viotty_flush_buffer(struct tty_struct *tty) +{ +} + +static int viotty_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) + { + /* the ioctls below read/set the flags usually shown in the leds */ + /* don't use them - they will go away without warning */ + case KDGETLED: + case KDGKBLED: + return put_user(0, (char*)arg); + + case KDSKBLED: + return 0; + } + + return n_tty_ioctl(tty,file,cmd,arg); +} + +static void viotty_throttle(struct tty_struct * tty) +{ +} + +static void viotty_unthrottle(struct tty_struct * tty) +{ +} + +static void viotty_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ +} + +static void viotty_stop(struct tty_struct *tty) +{ +} + +static void viotty_start(struct tty_struct *tty) +{ +} + +static void viotty_hangup(struct tty_struct *tty) +{ +} + +static void viotty_break(struct tty_struct *tty, int break_state) +{ +} + +static void viotty_send_xchar(struct tty_struct *tty, char ch) +{ +} + +static void viotty_wait_until_sent(struct tty_struct *tty, int timeout) +{ +} + +/*************************************************************************** + * Handle an open charLpEvent. Could be either interrupt or ack + ***************************************************************************/ +static void vioHandleOpenEvent(struct HvLpEvent *event) +{ + unsigned long flags; + u8 eventRc; + u16 eventSubtypeRc; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtualDevice; + + if (event->xFlags.xFunction == HvLpEvent_Function_Ack) + { + if (port >= VTTY_PORTS) + return; + + spin_lock_irqsave(&consolelock, + flags); + /* Got the lock, don't cause console output */ + + if (event->xRc == HvLpEvent_Rc_Good) + { + sndMsgSeq[port] = sndMsgAck[port] = 0; + } + + port_info[port].lp = event->xTargetLp; + + spin_unlock_irqrestore(&consolelock, + flags); + + if(event->xCorrelationToken != 0) + { + unsigned long semptr = event->xCorrelationToken; + up((struct semaphore *)semptr); + } + else + printk("viocons: wierd...got open ack without semaphore\n"); + } + else + { + /* This had better require an ack, otherwise complain + */ + if (event->xFlags.xAckInd != HvLpEvent_AckInd_DoAck) + { + printk("viocons: viocharopen without ack bit!\n"); + return; + } + + spin_lock_irqsave(&consolelock, + flags); + /* Got the lock, don't cause console output */ + + /* Make sure this is a good virtual tty */ + if (port >= VTTY_PORTS) + { + eventRc = HvLpEvent_Rc_SubtypeError; + eventSubtypeRc = viorc_openRejected; + } + + /* If this is tty is already connected to a different + partition, fail */ + else if ((port_info[port].lp != HvLpIndexInvalid) && + (port_info[port].lp != event->xSourceLp)) + { + eventRc = HvLpEvent_Rc_SubtypeError; + eventSubtypeRc = viorc_openRejected; + } + else + { + port_info[port].lp = event->xSourceLp; + eventRc = HvLpEvent_Rc_Good; + eventSubtypeRc = viorc_good; + sndMsgSeq[port] = sndMsgAck[port] = 0; + } + + spin_unlock_irqrestore(&consolelock, + flags); + + /* Return the acknowledgement */ + HvCallEvent_ackLpEvent(event); + } +} + +/*************************************************************************** + * Handle a close open charLpEvent. Could be either interrupt or ack + ***************************************************************************/ +static void vioHandleCloseEvent(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtualDevice; + + if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + if (port >= VTTY_PORTS) + return; + + /* For closes, just mark the console partition invalid */ + spin_lock_irqsave(&consolelock, + flags); + /* Got the lock, don't cause console output */ + + if (port_info[port].lp == event->xSourceLp) + port_info[port].lp = HvLpIndexInvalid; + + spin_unlock_irqrestore(&consolelock, + flags); + printk("viocons: console close from %d\n", event->xSourceLp); + } + else + { + printk("viocons: got unexpected close ack\n"); + } +} + +/*************************************************************************** + * Handle a config charLpEvent. Could be either interrupt or ack + ***************************************************************************/ +static void vioHandleConfig( struct HvLpEvent *event ) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + int len; + + printk("inside of vioHandleConfig!\n"); + + len = cevent->immediateDataLen; + HvCall_writeLogBuffer(cevent->immediateData, cevent->immediateDataLen); + + if ( cevent->immediateData[0] == 0x01 ) + { + printk("viocons: window resized to %d: %d: %d: %d\n", + cevent->immediateData[1], + cevent->immediateData[2], + cevent->immediateData[3], + cevent->immediateData[4]); + } + else + { + printk("viocons: unknown config event\n"); + } + return; +} + +/*************************************************************************** + * Handle a data charLpEvent. + ***************************************************************************/ +static void vioHandleData(struct HvLpEvent *event) +{ + struct tty_struct *tty; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + struct port_info_tag * pi; + int len; + u8 port = cevent->virtualDevice; + + if (port >= VTTY_PORTS) + { + printk("viocons: data on invalid virtual device\n"); + return; + } + + tty = port_info[port].tty; + + if (tty == NULL) + { + printk("viocons: no tty for virtual device\n"); + return; + } + + if (tty->magic != TTY_MAGIC) + { + printk("viocons: tty bad magic\n"); + return; + } + + /* + * Just to be paranoid, make sure the tty points back to this port + */ + pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "vioHandleData")) + return; + + len = cevent->immediateDataLen; + + /* + * Log port 0 data to the hypervisor log + */ + if (port == 0) + HvCall_writeLogBuffer(cevent->immediateData, cevent->immediateDataLen); + + if (len == 0) + return; + + /* Don't copy more bytes than there is room for in the buffer */ + if (tty->flip.count + len > TTY_FLIPBUF_SIZE) + { + len = TTY_FLIPBUF_SIZE - tty->flip.count; + printk("viocons: flip buffer overflow!\n"); + } + + memcpy (tty->flip.char_buf_ptr, + cevent->immediateData, + len); + memset(tty->flip.flag_buf_ptr, TTY_NORMAL, len); + + /* Update the kernel buffer end */ + tty->flip.count += len; + tty->flip.char_buf_ptr += len; + + tty->flip.flag_buf_ptr += len; + + tty_flip_buffer_push (tty); +} + +/*************************************************************************** + * Handle an ack charLpEvent. + ***************************************************************************/ +static void vioHandleAck(struct HvLpEvent *event) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + unsigned long flags; + u8 port = cevent->virtualDevice; + + if (port >= VTTY_PORTS) + { + printk("viocons: data on invalid virtual device\n"); + return; + } + + spin_lock_irqsave(&consolelock, + flags); + sndMsgAck[port] = event->xCorrelationToken; + spin_unlock_irqrestore(&consolelock, + flags); + + if (overflow[port].bufferUsed) + sendBuffers(port, port_info[port].lp); +} + +/*************************************************************************** + * Handle charLpEvents and route to the appropriate routine + ***************************************************************************/ +static void vioHandleCharEvent(struct HvLpEvent *event) +{ + int charminor; + + if (event == NULL) + { + return; + } + charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; + switch (charminor) + { + case viocharopen: + vioHandleOpenEvent(event); + break; + case viocharclose: + vioHandleCloseEvent(event); + break; + case viochardata: + vioHandleData(event); + break; + case viocharack: + vioHandleAck(event); + break; + case viocharconfig: + vioHandleConfig(event); + break; + default: + } +} + +/*************************************************************************** + * Send an open event + ***************************************************************************/ +static int viocons_sendOpen(HvLpIndex remoteLp, u8 port, void *sem) +{ + return HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_chario | viocharopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(remoteLp), + viopath_targetinst(remoteLp), + (u64)(unsigned long)sem, + VIOVERSION << 16, + ((u64)port << 48), + 0, 0, 0); + +} + +int __init viocons_init2(void) +{ +// extern char saved_command_line[]; + DECLARE_MUTEX_LOCKED(Semaphore); + int rc; + + /* + * Now open to the primary LP + */ + printk("viocons: init2 - open path to primary\n"); + rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), viomajorsubtype_chario); + if (rc) + { + printk("viocons: error on viopath_open to primary %d\n",rc); + } + + /* + * And if the primary is not the same as the hosting LP, open to the + * hosting lp + */ + if ((viopath_hostLp != HvLpIndexInvalid) && + (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) + { + printk("viocons: init2 - open path to hosting (%d) (%d)\n", + viopath_hostLp, HvLpConfig_getPrimaryLpIndex()); + rc = viopath_open(viopath_hostLp, viomajorsubtype_chario); + if (rc) + { + printk("viocons: error on viopath_open to hostlp %d\n",rc); + } + } + + vio_setCharHandler(vioHandleCharEvent); + + printk("viocons major is %d\n",TTY_MAJOR); + + /* First, try to open the console to the hosting lp. + * Wait on a semaphore for the response. + */ + if ((viopath_isactive(viopath_hostLp)) && + (viocons_sendOpen(viopath_hostLp, 0, &Semaphore) == 0)) + { + printk("viocons: init2 - open cons to hosting\n"); + down(&Semaphore); + } + + /* + * If we don't have an active console, try the primary + */ + if ((!viopath_isactive(port_info[0].lp)) && + (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) && + (viocons_sendOpen(HvLpConfig_getPrimaryLpIndex(), 0, &Semaphore) == 0)) + { + printk("viocons: init2 - open cons to primary\n"); + down(&Semaphore); + } + + printk("viocons: init2 - initializing tty\n"); + + printk("Ryan's test with slash-n at the end.\n"); + printk("Ryan's test with slash-n\nin the middle and the end.\n"); + printk("Ryan's test with slash-r slash-n\r\nin the middle and the end.\r\n"); + printk("Ryan's test with slash-r slash-n\r\nin the middle and just \n at the end.\n"); + + /* Initialize the tty_driver structure */ + memset(&viotty_driver, 0, sizeof(struct tty_driver)); + viotty_driver.magic = TTY_DRIVER_MAGIC; + viotty_driver.driver_name = "vioconsole"; +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + viotty_driver.name = "tty%d"; +#else + viotty_driver.name = "tty"; +#endif + viotty_driver.major = TTY_MAJOR; + viotty_driver.minor_start = 1; + viotty_driver.name_base = 1; + viotty_driver.num = VTTY_PORTS; + viotty_driver.type = TTY_DRIVER_TYPE_CONSOLE; + viotty_driver.subtype = 1; + viotty_driver.init_termios = tty_std_termios; + viotty_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; + viotty_driver.refcount = &viotty_refcount; + viotty_driver.table = viotty_table; + viotty_driver.termios = viotty_termios; + viotty_driver.termios_locked = viotty_termios_locked; + + viotty_driver.open = viotty_open; + viotty_driver.close = viotty_close; + viotty_driver.write = viotty_write; + viotty_driver.put_char = viotty_put_char; + viotty_driver.flush_chars = viotty_flush_chars; + viotty_driver.write_room = viotty_write_room; + viotty_driver.chars_in_buffer = viotty_chars_in_buffer; + viotty_driver.flush_buffer = viotty_flush_buffer; + viotty_driver.ioctl = viotty_ioctl; + viotty_driver.throttle = viotty_throttle; + viotty_driver.unthrottle = viotty_unthrottle; + viotty_driver.set_termios = viotty_set_termios; + viotty_driver.stop = viotty_stop; + viotty_driver.start = viotty_start; + viotty_driver.hangup = viotty_hangup; + viotty_driver.break_ctl = viotty_break; + viotty_driver.send_xchar = viotty_send_xchar; + viotty_driver.wait_until_sent = viotty_wait_until_sent; + + if (tty_register_driver(&viotty_driver)) + { + printk("Couldn't register viotty driver\n"); + } + + // Now create the vcs and vcsa devfs entries so mingetty works +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + { + struct tty_driver temp_driver = viotty_driver; + int i; + + temp_driver.name = "vcs%d"; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs (&temp_driver, + 0, + i + temp_driver.minor_start); + + temp_driver.name = "vcsa%d"; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs (&temp_driver, + 0, + i + temp_driver.minor_start); + + // For compatibility with some earlier code only! + // This will go away!!! + temp_driver.name = "viocons/%d"; + temp_driver.name_base = 0; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs (&temp_driver, + 0, + i + temp_driver.minor_start); + } +#endif + + /* + * Create the proc entry + */ + iSeries_proc_callback(&viocons_proc_init); + + return 0; +} + +void __init viocons_init(void) +{ + int i; + printk("viocons registering console\n"); + + memset(&port_info, 0x00, sizeof(port_info)); + for (i=0; i + * (C) Copyright 2000 IBM Corporation + * + * 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) anyu later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *************************************************************************** + * This routine provides access to tape drives owned and managed by an OS/400 + * partition running on the same box as this Linux partition. + * + * All tape operations are performed by sending messages back and forth to + * the OS/400 partition. The format of the messages is defined in + * include/asm-ppc/iSeries/vio.h + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif + +#ifndef _HVLPEVENT_H +#include +#endif + +#ifndef _HVCALLEVENT_H +#include "asm/iSeries/HvCallEvent.h" +#endif +#ifndef _HVLPCONFIG_H +#include "asm/iSeries/HvLpConfig.h" +#endif + +static int viotape_major = 230; +static int viotape_numdev = 0; + +static u64 sndMsgSeq; +static u64 sndMsgAck; +static u64 rcvMsgSeq; +static u64 rcvMsgAck; + +/*************************************************************************** + * The minor number follows the conventions of the SCSI tape drives. The + * rewind and mode are encoded in the minor #. We use this struct to break + * them out + ***************************************************************************/ +struct viot_devinfo_struct { + int major; + int minor; + int devno; + int mode; + int rewind; +}; + +/*************************************************************************** + * Maximum # tapes we support + ***************************************************************************/ +#define VIOTAPE_MAX_TAPE 8 + +/*************************************************************************** + * Our info on the tapes + ***************************************************************************/ +static struct { + char rsrcname[10]; + char type[4]; + char model[3]; +} viotape_unitinfo[VIOTAPE_MAX_TAPE]; + +static struct mtget viomtget[VIOTAPE_MAX_TAPE]; + +/*************************************************************************** + * We single-thread + ***************************************************************************/ +static struct semaphore reqSem; + +/*************************************************************************** + * When we send a request, we use this struct to get the response back + * from the interrupt handler + ***************************************************************************/ +struct opStruct { + void *buffer; + dma_addr_t dmaaddr; + size_t count; + int rc; + struct semaphore *sem; + struct opStruct *free; +}; + +static spinlock_t opStructListLock; +static struct opStruct *opStructList; + +/*************************************************************************** + *************************************************************************** + * CODE STARTS HERE + *************************************************************************** + ***************************************************************************/ + +/*************************************************************************** + * Decode the kdev_t into its parts + ***************************************************************************/ +void getDevInfo(kdev_t dev, struct viot_devinfo_struct *devi) +{ + devi->major = MAJOR(dev); + devi->minor = MINOR(dev); + devi->devno = devi->minor & 0x1F; + devi->mode = (devi->minor & 0x60) >> 5; + devi->rewind = devi->minor & 0x80; + + printk("viotape dev: major %d, minor %d, devno %d, mode %d, rewind %d\n", + devi->major, + devi->minor, + devi->devno, + devi->mode, + devi->rewind); +} + + +/*************************************************************************** + * Allocate an op structure from our pool + ***************************************************************************/ +static struct opStruct *getOpStruct(void) +{ + struct opStruct *newOpStruct; + spin_lock(&opStructListLock); + + if (opStructList == NULL) + { + newOpStruct = kmalloc(sizeof(struct opStruct), GFP_KERNEL); + + } + else + { + newOpStruct = opStructList; + opStructList = opStructList->free; + } + + if (newOpStruct) + memset(newOpStruct,0x00,sizeof(struct opStruct)); + + spin_unlock(&opStructListLock); + + return newOpStruct; +} + +/*************************************************************************** + * Return an op structure to our pool + ***************************************************************************/ +static void freeOpStruct(struct opStruct *opStruct) +{ + spin_lock(&opStructListLock); + opStruct->free = opStructList; + opStructList = opStruct; + spin_unlock(&opStructListLock); +} + +/*************************************************************************** + * Get info on all tapes from OS/400 + ***************************************************************************/ +static void get_viotape_info(void) +{ + dma_addr_t dmaaddr; + HvLpEvent_Rc hvrc; + int i; + struct opStruct *op = getOpStruct(); + DECLARE_MUTEX_LOCKED(Semaphore); + if (op == NULL) + return; + + memset(viotape_unitinfo, 0x00, sizeof(viotape_unitinfo)); + + op->sem = &Semaphore; + + dmaaddr = pci_map_single(NULL, &viotape_unitinfo, + sizeof(viotape_unitinfo), + PCI_DMA_FROMDEVICE); + if (dmaaddr == 0xFFFFFFFF) + { + printk("viotape error allocating tce\n"); + return; + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapegetinfo, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + dmaaddr, + sizeof(viotape_unitinfo), + 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + } + + down(&Semaphore); + + freeOpStruct(op); + + for (i=0; + ((i < VIOTAPE_MAX_TAPE) && (viotape_unitinfo[i].rsrcname[0])); + i++) + { + printk("found a tape %10.10s\n",viotape_unitinfo[i].rsrcname); + viotape_numdev++; + } +} + +/*************************************************************************** + * Don't support seek + ***************************************************************************/ +static long long viotap_llseek(struct file *file, long long offset, int origin) +{ + printk("viotap_llseek: not supported\n"); + return -EINVAL; /* not supported */ +} + +/*************************************************************************** + * Write + ***************************************************************************/ +static ssize_t viotap_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + HvLpEvent_Rc hvrc; + kdev_t dev = file->f_dentry->d_inode->i_rdev; + unsigned short flags = file->f_flags; + struct opStruct *op = getOpStruct(); + int noblock = ((flags & O_NONBLOCK) != 0); + int err; + struct viot_devinfo_struct devi; + DECLARE_MUTEX_LOCKED(Semaphore); + + if (op == NULL) + return -ENOMEM; + + getDevInfo(dev, &devi); + + /* We need to make sure we can send a request. We use + * a semaphore to keep track of # requests in use. If + * we are non-blocking, make sure we don't block on the + * semaphore + */ + if (noblock) + { + if (down_trylock(&reqSem)) + { + freeOpStruct(op); + return -EWOULDBLOCK; + } + } + else + { + down(&reqSem); + } + + /* Allocate a DMA buffer */ + op->buffer = pci_alloc_consistent(NULL, + count, + &op->dmaaddr); + + if ((op->dmaaddr == 0xFFFFFFFF) || (op->buffer == NULL)) + { + printk("viotape error allocating dma buffer for len %d\n", + count); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + op->count = count; + + /* Copy the data into the buffer */ + err = copy_from_user( op->buffer, (const void *) buf, count); + if (err) + { + printk("viotape: error on copy from user\n"); + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + if (noblock) + { + op->sem = NULL; + } + else + { + op->sem = &Semaphore; + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapewrite, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48) | + op->dmaaddr, + count, + 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + + if (noblock) + return count; + + down(&Semaphore); + + err = op->rc; + + printk("viotape write: rc %d, count %d, result %d\n", + err, count, op->count); + + count = op->count; + + /* Free the buffer */ + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + if (err) + return -EIO; + else + return count; +} + +/*************************************************************************** + * read + ***************************************************************************/ +static ssize_t viotap_read(struct file *file, char *buf, size_t count, loff_t *ptr) +{ + HvLpEvent_Rc hvrc; + kdev_t dev = file->f_dentry->d_inode->i_rdev; + unsigned short flags = file->f_flags; + struct opStruct *op = getOpStruct(); + int noblock = ((flags & O_NONBLOCK) != 0); + int err; + struct viot_devinfo_struct devi; + DECLARE_MUTEX_LOCKED(Semaphore); + + if (op == NULL) + return -ENOMEM; + + getDevInfo(dev, &devi); + + /* We need to make sure we can send a request. We use + * a semaphore to keep track of # requests in use. If + * we are non-blocking, make sure we don't block on the + * semaphore + */ + if (noblock) + { + if (down_trylock(&reqSem)) + { + freeOpStruct(op); + return -EWOULDBLOCK; + } + } + else + { + down(&reqSem); + } + + + /* Allocate a DMA buffer */ + op->buffer = pci_alloc_consistent(NULL, + count, + &op->dmaaddr); + + if ((op->dmaaddr == 0xFFFFFFFF) || (op->buffer == NULL)) + { + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + op->count = count; + + op->sem = &Semaphore; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotaperead, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48) | + op->dmaaddr, + count, + 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + + down(&Semaphore); + + if (op->rc == 0) + { + /* If we got data back */ + if (op->count) + { + /* Copy the data into the buffer */ + err = copy_to_user( buf, op->buffer, count); + if (err) + { + printk("error on copy_to_user\n"); + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + } + } + + err = op->rc; + + printk("viotape read: rc %d, count %d, result %d\n", + err, count, op->count); + count = op->count; + + /* Free the buffer */ + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + if (err) + return -EIO; + else + return count; +} + +/*************************************************************************** + * read + ***************************************************************************/ +static int viotap_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + HvLpEvent_Rc hvrc; + int err; + DECLARE_MUTEX_LOCKED(Semaphore); + kdev_t dev = file->f_dentry->d_inode->i_rdev; + struct opStruct *op = getOpStruct(); + struct viot_devinfo_struct devi; + if (op == NULL) + return -ENOMEM; + + getDevInfo(dev, &devi); + + down(&reqSem); + + switch (cmd) + { + case MTIOCTOP: + { + struct mtop mtc; + u32 myOp; + + if (copy_from_user((char *) &mtc, (char *) arg, sizeof(struct mtop))) + { + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + switch(mtc.mt_op) + { + case MTRESET: myOp = VIOTAPOP_RESET; break; + case MTFSF: myOp = VIOTAPOP_FSF; break; + case MTBSF: myOp = VIOTAPOP_BSF; break; + case MTFSR: myOp = VIOTAPOP_FSR; break; + case MTBSR: myOp = VIOTAPOP_BSR; break; + case MTWEOF: myOp = VIOTAPOP_WEOF; break; + case MTREW: myOp = VIOTAPOP_REW; break; + case MTNOP: myOp = VIOTAPOP_NOP; break; + case MTEOM: myOp = VIOTAPOP_EOM; break; + case MTERASE: myOp = VIOTAPOP_ERASE; break; + case MTSETBLK: myOp = VIOTAPOP_SETBLK; break; + case MTSETDENSITY: myOp = VIOTAPOP_SETDENSITY; break; + case MTTELL: myOp = VIOTAPOP_GETPOS; break; + case MTSEEK: myOp = VIOTAPOP_SETPOS; break; + case MTSETPART: myOp = VIOTAPOP_SETPART; break; + default: + return -EIO; + } + + op->sem = &Semaphore; + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapeop, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48), + 0, + (((u64)myOp) << 32) | mtc.mt_count, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + down(&Semaphore); + + if (op->rc) + { + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + else + { + freeOpStruct(op); + up(&reqSem); + return 0; + } + break; + } + + case MTIOCGET: + op->sem = &Semaphore; + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapegetstatus , + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48), + 0, 0, 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + down(&Semaphore); + up(&reqSem); + if (op->rc) + { + freeOpStruct(op); + return -EIO; + } + else + { + freeOpStruct(op); + err = copy_to_user((void *)arg, &viomtget[dev], sizeof(viomtget[0])); + if (err) + { + freeOpStruct(op); + return -EFAULT; + } + return 0; + } + break; + case MTIOCPOS: + printk("Got an MTIOCPOS\n"); + default: + return -ENOSYS; + } + return 0; +} + +/*************************************************************************** + * Open + ***************************************************************************/ +static int viotap_open(struct inode *inode, struct file *file) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + kdev_t dev = file->f_dentry->d_inode->i_rdev; + HvLpEvent_Rc hvrc; + struct opStruct *op = getOpStruct(); + struct viot_devinfo_struct devi; + if (op == NULL) + return -ENOMEM; + + getDevInfo(dev, &devi); + + // Note: We currently only support one mode! + if ((devi.devno >= viotape_numdev) || + (devi.mode)) + { + freeOpStruct(op); + return -ENODEV; + } + + op->sem = &Semaphore; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapeopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + printk("viotape bad rc on signalLpEvent %d\n",(int)hvrc); + freeOpStruct(op); + return -EIO; + } + + down(&Semaphore); + + if (op->rc) + { + printk("viotape: open failed\n"); + freeOpStruct(op); + return -EIO; + } + else + { + printk("viotape: open worked\n"); + freeOpStruct(op); + return 0; + } +} + +/*************************************************************************** + * Release + ***************************************************************************/ +static int viotap_release(struct inode *inode, struct file *file) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + kdev_t dev = file->f_dentry->d_inode->i_rdev; + HvLpEvent_Rc hvrc; + struct viot_devinfo_struct devi; + struct opStruct *op = getOpStruct(); + + if (op == NULL) + return -ENOMEM; + + getDevInfo(dev, &devi); + + if (devi.devno >= viotape_numdev) + { + freeOpStruct(op); + return -ENODEV; + } + + op->sem = &Semaphore; + + if (devi.rewind) + { + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapeop, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48), + 0, + ((u64)VIOTAPOP_REW) << 32, + 0); + down(&Semaphore); + + if (op->rc) + { + printk("viotape: rewind failed\n"); + } + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapeclose, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)devi.devno << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + printk("viotape bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + down(&Semaphore); + + if (op->rc) + { + printk("viotape: close failed\n"); + } + return 0; +} + +struct file_operations viotap_fops = { + owner: THIS_MODULE, + llseek: viotap_llseek, + read: viotap_read, + write: viotap_write, + ioctl: viotap_ioctl, + open: viotap_open, + release: viotap_release, +}; + +/*************************************************************************** + * Handle interrupt events for tape + ***************************************************************************/ +static void vioHandleTapeEvent(struct HvLpEvent *event) +{ + int tapeminor; + struct opStruct *op; + struct viotapelpevent *tevent = (struct viotapelpevent *)event; + + if (event == NULL) + { + /* Notification that a partition went away! */ + if (!viopath_isactive(viopath_hostLp)) + { + /* TODO! Clean up */ + } + return; + } + tapeminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; + switch (tapeminor) + { + case viotapegetinfo: + case viotapeopen: + case viotapeclose: + op = (struct opStruct *)(u32)event->xCorrelationToken; + op->rc = event->xRc; + up(op->sem); + break; + case viotaperead: + case viotapewrite: + op = (struct opStruct *)(u32)event->xCorrelationToken; + op->rc = event->xRc; + op->count = tevent->mLen; + + if (op->sem) + { + up(op->sem); + } + else + { + pci_free_consistent(NULL, op->count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + } + break; + case viotapeop: + case viotapegetpos: + case viotapesetpos: + case viotapegetstatus: + op = (struct opStruct *)(u32)event->xCorrelationToken; + if (op) + { + op->count = tevent->u.tapeOp.mCount; + op->rc = event->xRc; + + if (op->sem) + { + up(op->sem); + } + } + break; + default: + printk("viotape: wierd ack\n"); + } +} + + +/*************************************************************************** + * Do initialization + ***************************************************************************/ +int __init viotap_init(void) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + int rc; + char tapename[32]; + int i; + + sndMsgSeq = sndMsgAck = 0; + rcvMsgSeq = rcvMsgAck = 0; + opStructList = NULL; + spin_lock_init(&opStructListLock); + + sema_init(&reqSem, VIOTAPE_MAXREQ); + + /* + * Open to our hosing lp + */ + if (viopath_hostLp == HvLpIndexInvalid) + return -1; + + printk("viotape: init - open path to hosting (%d)\n", + viopath_hostLp); + + rc = viopath_open(viopath_hostLp, viomajorsubtype_tape); + if (rc) + { + printk("viotape: error on viopath_open to hostlp %d\n",rc); + } + + vio_setTapeHandler(vioHandleTapeEvent); + + printk("viotape major is %d\n",viotape_major); + + get_viotape_info(); + + if (devfs_register_chrdev(viotape_major, "viotape", &viotap_fops)) + { + printk("Error registering viotape device\n"); + return -1; + } + + for (i=0; i #include #include +#include #include #include @@ -833,9 +834,9 @@ * make sure we are atomic with respect to * other console switches.. */ - spin_lock_irq(&console_lock); + acquire_console_sem(); complete_change_console(newvt); - spin_unlock_irq(&console_lock); + release_console_sem(); } } @@ -1169,7 +1170,8 @@ vt_cons[new_console]->vt_mode.frsig = 0; vt_cons[new_console]->vt_pid = -1; vt_cons[new_console]->vt_newvt = -1; - reset_palette (new_console) ; + if (!in_interrupt()) /* Via keyboard.c:SAK() - akpm */ + reset_palette (new_console) ; } /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/char/w83877f_wdt.c linux.ac/drivers/char/w83877f_wdt.c --- linux.vanilla/drivers/char/w83877f_wdt.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/char/w83877f_wdt.c Thu May 17 21:46:38 2001 @@ -0,0 +1,347 @@ +/* + * W83877F Computer Watchdog Timer driver for Linux 2.4.x + * + * Based on acquirewdt.c by Alan Cox, + * and sbc60xxwdt.c by Jakob Oestergaard + * + * 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. + * + * The authors do NOT admit liability nor provide warranty for + * any of this software. This material is provided "AS-IS" in + * the hope that it may be useful for others. + * + * (c) Copyright 2001 Scott Jennings + * + * 4/19 - 2001 [Initial revision] + * + * + * Theory of operation: + * A Watchdog Timer (WDT) is a hardware circuit that can + * reset the computer system in case of a software fault. + * You probably knew that already. + * + * Usually a userspace daemon will notify the kernel WDT driver + * via the /proc/watchdog special device file that userspace is + * still alive, at regular intervals. When such a notification + * occurs, the driver will usually tell the hardware watchdog + * that everything is in order, and that the watchdog should wait + * for yet another little while to reset the system. + * If userspace fails (RAM error, kernel bug, whatever), the + * notifications cease to occur, and the hardware watchdog will + * reset the system (causing a reboot) after the timeout occurs. + * + * This WDT driver is different from most other Linux WDT + * drivers in that the driver will ping the watchdog by itself, + * because this particular WDT has a very short timeout (1.6 + * seconds) and it would be insane to count on any userspace + * daemon always getting scheduled within that time frame. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OUR_NAME "w83877f_wdt" + +#define ENABLE_W83877F_PORT 0x3F0 +#define ENABLE_W83877F 0x87 +#define DISABLE_W83877F 0xAA +#define WDT_PING 0x443 +#define WDT_REGISTER 0x14 +#define WDT_ENABLE 0x9C +#define WDT_DISABLE 0x8C + +/* + * The W83877F seems to be fixed at 1.6s timeout (at least on the + * EMACS PC-104 board I'm using). If we reset the watchdog every + * ~250ms we should be safe. */ + +#define WDT_INTERVAL (HZ/4+1) + +/* + * We must not require too good response from the userspace daemon. + * Here we require the userspace daemon to send us a heartbeat + * char to /dev/watchdog every 30 seconds. + */ + +#define WDT_HEARTBEAT (HZ * 30) + +static void wdt_timer_ping(unsigned long); +static struct timer_list timer; +static unsigned long next_heartbeat; +static int wdt_is_open; +static int wdt_expect_close; + +/* + * Whack the dog + */ + +static void wdt_timer_ping(unsigned long data) +{ + /* If we got a heartbeat pulse within the WDT_US_INTERVAL + * we agree to ping the WDT + */ + if(time_before(jiffies, next_heartbeat)) + { + /* Ping the WDT by reading from WDT_PING */ + inb_p(WDT_PING); + /* Re-set the timer interval */ + timer.expires = jiffies + WDT_INTERVAL; + add_timer(&timer); + } else { + printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n"); + } +} + +/* + * Utility routines + */ + +static void wdt_change(int writeval) +{ + /* buy some time */ + inb_p(WDT_PING); + + /* make W83877F available */ + outb_p(ENABLE_W83877F,ENABLE_W83877F_PORT); + outb_p(ENABLE_W83877F,ENABLE_W83877F_PORT); + + /* enable watchdog */ + outb_p(WDT_REGISTER,ENABLE_W83877F_PORT); + outb_p(writeval,ENABLE_W83877F_PORT+1); + + /* lock the W8387FF away */ + outb_p(DISABLE_W83877F,ENABLE_W83877F_PORT); +} + +static void wdt_startup(void) +{ + next_heartbeat = jiffies + WDT_HEARTBEAT; + + /* Start the timer */ + timer.expires = jiffies + WDT_INTERVAL; + add_timer(&timer); + + wdt_change(WDT_ENABLE); + + printk(OUR_NAME ": Watchdog timer is now enabled.\n"); +} + +static void wdt_turnoff(void) +{ + /* Stop the timer */ + del_timer(&timer); + + wdt_change(WDT_DISABLE); + + printk(OUR_NAME ": Watchdog timer is now disabled...\n"); +} + + +/* + * /dev/watchdog handling + */ + +static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos) +{ + /* We can't seek */ + if(ppos != &file->f_pos) + return -ESPIPE; + + /* See if we got the magic character */ + if(count) + { + size_t ofs; + + /* note: just in case someone wrote the magic character + * five months ago... */ + wdt_expect_close = 0; + + /* now scan */ + for(ofs = 0; ofs != count; ofs++) + if(buf[ofs] == 'V') + wdt_expect_close = 1; + + /* someone wrote to us, we should restart timer */ + next_heartbeat = jiffies + WDT_HEARTBEAT; + return 1; + }; + return 0; +} + +static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos) +{ + /* No can do */ + return -EINVAL; +} + +static int fop_open(struct inode * inode, struct file * file) +{ + switch(MINOR(inode->i_rdev)) + { + case WATCHDOG_MINOR: + /* Just in case we're already talking to someone... */ + if(wdt_is_open) + return -EBUSY; + /* Good, fire up the show */ + wdt_is_open = 1; + wdt_startup(); + return 0; + + default: + return -ENODEV; + } +} + +static int fop_close(struct inode * inode, struct file * file) +{ + lock_kernel(); + if(MINOR(inode->i_rdev) == WATCHDOG_MINOR) + { + if(wdt_expect_close) + wdt_turnoff(); + else { + del_timer(&timer); + printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n"); + } + } + wdt_is_open = 0; + unlock_kernel(); + return 0; +} + +static long long fop_llseek(struct file *file, long long offset, int origin) +{ + return -ESPIPE; +} + +static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + static struct watchdog_info ident= + { + 0, + 1, + "W83877F" + }; + + switch(cmd) + { + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0; + case WDIOC_KEEPALIVE: + next_heartbeat = jiffies + WDT_HEARTBEAT; + return 0; + } +} + +static struct file_operations wdt_fops = { + owner: THIS_MODULE, + llseek: fop_llseek, + read: fop_read, + write: fop_write, + open: fop_open, + release: fop_close, + ioctl: fop_ioctl +}; + +static struct miscdevice wdt_miscdev = { + WATCHDOG_MINOR, + "watchdog", + &wdt_fops +}; + +/* + * Notifier for system down + */ + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if(code==SYS_DOWN || code==SYS_HALT) + wdt_turnoff(); + return NOTIFY_DONE; +} + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wdt_notifier= +{ + wdt_notify_sys, + 0, + 0 +}; + +static void __exit w83877f_wdt_unload(void) +{ + wdt_turnoff(); + + /* Deregister */ + misc_deregister(&wdt_miscdev); + + unregister_reboot_notifier(&wdt_notifier); + release_region(WDT_PING,1); + release_region(ENABLE_W83877F_PORT,2); +} + +static int __init w83877f_wdt_init(void) +{ + int rc = -EBUSY; + + if (!request_region(ENABLE_W83877F_PORT, 2, "W83877F WDT")) + goto err_out; + if (!request_region(WDT_PING, 1, "W8387FF WDT")) + goto err_out_region1; + + init_timer(&timer); + timer.function = wdt_timer_ping; + timer.data = 0; + + rc = misc_register(&wdt_miscdev); + if (rc) + goto err_out_region2; + + rc = register_reboot_notifier(&wdt_notifier); + if (rc) + goto err_out_miscdev; + + printk(KERN_INFO OUR_NAME ": WDT driver for W83877F initialised.\n"); + + return 0; + +err_out_miscdev: + misc_deregister(&wdt_miscdev); +err_out_region2: + release_region(WDT_PING,1); +err_out_region1: + release_region(ENABLE_W83877F_PORT,2); +err_out: + return rc; +} + +module_init(w83877f_wdt_init); +module_exit(w83877f_wdt_unload); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/Config.in linux.ac/drivers/i2o/Config.in --- linux.vanilla/drivers/i2o/Config.in Wed Apr 12 17:38:53 2000 +++ linux.ac/drivers/i2o/Config.in Thu Jan 1 01:00:00 1970 @@ -1,16 +0,0 @@ -mainmenu_option next_comment -comment 'I2O device support' - -tristate 'I2O support' CONFIG_I2O - -if [ "$CONFIG_PCI" = "y" ]; then - dep_tristate ' I2O PCI support' CONFIG_I2O_PCI $CONFIG_I2O -fi -dep_tristate ' I2O Block OSM' CONFIG_I2O_BLOCK $CONFIG_I2O -if [ "$CONFIG_NET" = "y" ]; then - dep_tristate ' I2O LAN OSM' CONFIG_I2O_LAN $CONFIG_I2O -fi -dep_tristate ' I2O SCSI OSM' CONFIG_I2O_SCSI $CONFIG_I2O $CONFIG_SCSI -dep_tristate ' I2O /proc support' CONFIG_I2O_PROC $CONFIG_I2O - -endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/Makefile linux.ac/drivers/i2o/Makefile --- linux.vanilla/drivers/i2o/Makefile Fri Dec 29 22:07:21 2000 +++ linux.ac/drivers/i2o/Makefile Thu Jan 1 01:00:00 1970 @@ -1,20 +0,0 @@ -# -# Makefile for the kernel I2O OSM. -# -# Note : at this point, these files are compiled on all systems. -# In the future, some of these should be built conditionally. -# - -O_TARGET := i2o.o - -export-objs := i2o_pci.o i2o_core.o i2o_config.o i2o_block.o i2o_lan.o i2o_scsi.o i2o_proc.o - -obj-$(CONFIG_I2O_PCI) += i2o_pci.o -obj-$(CONFIG_I2O) += i2o_core.o i2o_config.o -obj-$(CONFIG_I2O_BLOCK) += i2o_block.o -obj-$(CONFIG_I2O_LAN) += i2o_lan.o -obj-$(CONFIG_I2O_SCSI) += i2o_scsi.o -obj-$(CONFIG_I2O_PROC) += i2o_proc.o - -include $(TOPDIR)/Rules.make - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/README linux.ac/drivers/i2o/README --- linux.vanilla/drivers/i2o/README Mon Jun 19 21:30:55 2000 +++ linux.ac/drivers/i2o/README Thu Jan 1 01:00:00 1970 @@ -1,98 +0,0 @@ - - Linux I2O Support (c) Copyright 1999 Red Hat Software - and others. - - 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. - -AUTHORS (so far) - -Alan Cox, Building Number Three Ltd. - Core code, SCSI and Block OSMs - -Steve Ralston, LSI Logic Corp. - Debugging SCSI and Block OSM - -Deepak Saxena, Intel Corp. - Various core/block extensions - /proc interface, bug fixes - Ioctl interfaces for control - Debugging LAN OSM - -Philip Rumpf - Fixed assorted dumb SMP locking bugs - -Juha Sievanen, University of Helsinki Finland - LAN OSM code - /proc interface to LAN class - Bug fixes - Core code extensions - -Auvo Häkkinen, University of Helsinki Finland - LAN OSM code - /Proc interface to LAN class - Bug fixes - Core code extensions - -Taneli Vähäkangas, University of Helsinki Finland - Fixes to i2o_config - -CREDITS - - This work was made possible by - -Red Hat Software - Funding for the Building #3 part of the project - -Symbios Logic (Now LSI) - Host adapters, hints, known to work platforms when I hit - compatibility problems - -BoxHill Corporation - Loan of initial FibreChannel disk array used for development work. - -European Comission - Funding the work done by the University of Helsinki - -SysKonnect - Loan of FDDI and Gigabit Ethernet cards - -ASUSTeK - Loan of I2O motherboard - -STATUS: - -o The core setup works within limits. -o The scsi layer seems to almost work. - I'm still chasing down the hang bug. -o The block OSM is mostly functional -o LAN OSM works with FDDI and Ethernet cards. - -TO DO: - -General: -o Provide hidden address space if asked -o Long term message flow control -o PCI IOP's without interrupts are not supported yet -o Push FAIL handling into the core -o DDM control interfaces for module load etc -o Add I2O 2.0 support (Deffered to 2.5 kernel) - -Block: -o Multiple major numbers -o Read ahead and cache handling stuff. Talk to Ingo and people -o Power management -o Finish Media changers - -SCSI: -o Find the right way to associate drives/luns/busses - -Lan: -o Performance tuning -o Test Fibre Channel code - -Tape: -o Anyone seen anything implementing this ? - (D.S: Will attempt to do so if spare cycles permit) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/README.ioctl linux.ac/drivers/i2o/README.ioctl --- linux.vanilla/drivers/i2o/README.ioctl Mon Jun 19 21:30:55 2000 +++ linux.ac/drivers/i2o/README.ioctl Thu Jan 1 01:00:00 1970 @@ -1,394 +0,0 @@ - -Linux I2O User Space Interface -rev 0.3 - 04/20/99 - -============================================================================= -Originally written by Deepak Saxena(deepak@plexity.net) -Currently maintained by Deepak Saxena(deepak@plexity.net) -============================================================================= - -I. Introduction - -The Linux I2O subsystem provides a set of ioctl() commands that can be -utilized by user space applications to communicate with IOPs and devices -on individual IOPs. This document defines the specific ioctl() commands -that are available to the user and provides examples of their uses. - -This document assumes the reader is familiar with or has access to the -I2O specification as no I2O message parameters are outlined. For information -on the specification, see http://www.i2osig.org - -This document and the I2O user space interface are currently maintained -by Deepak Saxena. Please send all comments, errata, and bug fixes to -deepak@csociety.purdue.edu - -II. IOP Access - -Access to the I2O subsystem is provided through the device file named -/dev/i2o/ctl. This file is a character file with major number 10 and minor -number 166. It can be created through the following command: - - mknod /dev/i2o/ctl c 10 166 - -III. Determining the IOP Count - - SYNOPSIS - - ioctl(fd, I2OGETIOPS, int *count); - - u8 count[MAX_I2O_CONTROLLERS]; - - DESCRIPTION - - This function returns the system's active IOP table. count should - point to a buffer containing MAX_I2O_CONTROLLERS entries. Upon - returning, each entry will contain a non-zero value if the given - IOP unit is active, and NULL if it is inactive or non-existent. - - RETURN VALUE. - - Returns 0 if no errors occur, and -1 otherwise. If an error occurs, - errno is set appropriately: - - EFAULT Invalid user space pointer was passed - -IV. Getting Hardware Resource Table - - SYNOPSIS - - ioctl(fd, I2OHRTGET, struct i2o_cmd_hrt *hrt); - - struct i2o_cmd_hrtlct - { - u32 iop; /* IOP unit number */ - void *resbuf; /* Buffer for result */ - u32 *reslen; /* Buffer length in bytes */ - }; - - DESCRIPTION - - This function returns the Hardware Resource Table of the IOP specified - by hrt->iop in the buffer pointed to by hrt->resbuf. The actual size of - the data is written into *(hrt->reslen). - - RETURNS - - This function returns 0 if no errors occur. If an error occurs, -1 - is returned and errno is set appropriately: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ENOBUFS Buffer not large enough. If this occurs, the required - buffer length is written into *(hrt->reslen) - -V. Getting Logical Configuration Table - - SYNOPSIS - - ioctl(fd, I2OLCTGET, struct i2o_cmd_lct *lct); - - struct i2o_cmd_hrtlct - { - u32 iop; /* IOP unit number */ - void *resbuf; /* Buffer for result */ - u32 *reslen; /* Buffer length in bytes */ - }; - - DESCRIPTION - - This function returns the Logical Configuration Table of the IOP specified - by lct->iop in the buffer pointed to by lct->resbuf. The actual size of - the data is written into *(lct->reslen). - - RETURNS - - This function returns 0 if no errors occur. If an error occurs, -1 - is returned and errno is set appropriately: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ENOBUFS Buffer not large enough. If this occurs, the required - buffer length is written into *(lct->reslen) - -VI. Settting Parameters - - SYNOPSIS - - ioctl(fd, I2OPARMSET, struct i2o_parm_setget *ops); - - struct i2o_cmd_psetget - { - u32 iop; /* IOP unit number */ - u32 tid; /* Target device TID */ - void *opbuf; /* Operation List buffer */ - u32 oplen; /* Operation List buffer length in bytes */ - void *resbuf; /* Result List buffer */ - u32 *reslen; /* Result List buffer length in bytes */ - }; - - DESCRIPTION - - This function posts a UtilParamsSet message to the device identified - by ops->iop and ops->tid. The operation list for the message is - sent through the ops->opbuf buffer, and the result list is written - into the buffer pointed to by ops->resbuf. The number of bytes - written is placed into *(ops->reslen). - - RETURNS - - The return value is the size in bytes of the data written into - ops->resbuf if no errors occur. If an error occurs, -1 is returned - and errno is set appropriatly: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ENOBUFS Buffer not large enough. If this occurs, the required - buffer length is written into *(ops->reslen) - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - - A return value of 0 does not mean that the value was actually - changed properly on the IOP. The user should check the result - list to determine the specific status of the transaction. - -VII. Getting Parameters - - SYNOPSIS - - ioctl(fd, I2OPARMGET, struct i2o_parm_setget *ops); - - struct i2o_parm_setget - { - u32 iop; /* IOP unit number */ - u32 tid; /* Target device TID */ - void *opbuf; /* Operation List buffer */ - u32 oplen; /* Operation List buffer length in bytes */ - void *resbuf; /* Result List buffer */ - u32 *reslen; /* Result List buffer length in bytes */ - }; - - DESCRIPTION - - This function posts a UtilParamsGet message to the device identified - by ops->iop and ops->tid. The operation list for the message is - sent through the ops->opbuf buffer, and the result list is written - into the buffer pointed to by ops->resbuf. The actual size of data - written is placed into *(ops->reslen). - - RETURNS - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ENOBUFS Buffer not large enough. If this occurs, the required - buffer length is written into *(ops->reslen) - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - - A return value of 0 does not mean that the value was actually - properly retreived. The user should check the result list - to determine the specific status of the transaction. - -VIII. Downloading Software - - SYNOPSIS - - ioctl(fd, I2OSWDL, struct i2o_sw_xfer *sw); - - struct i2o_sw_xfer - { - u32 iop; /* IOP unit number */ - u8 flags; /* DownloadFlags field */ - u8 sw_type; /* Software type */ - u32 sw_id; /* Software ID */ - void *buf; /* Pointer to software buffer */ - u32 *swlen; /* Length of software buffer */ - u32 *maxfrag; /* Number of fragments */ - u32 *curfrag; /* Current fragment number */ - }; - - DESCRIPTION - - This function downloads a software fragment pointed by sw->buf - to the iop identified by sw->iop. The DownloadFlags, SwID, SwType - and SwSize fields of the ExecSwDownload message are filled in with - the values of sw->flags, sw->sw_id, sw->sw_type and *(sw->swlen). - - The fragments _must_ be sent in order and be 8K in size. The last - fragment _may_ be shorter, however. The kernel will compute its - size based on information in the sw->swlen field. - - Please note that SW transfers can take a long time. - - RETURNS - - This function returns 0 no errors occur. If an error occurs, -1 - is returned and errno is set appropriatly: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - -IX. Uploading Software - - SYNOPSIS - - ioctl(fd, I2OSWUL, struct i2o_sw_xfer *sw); - - struct i2o_sw_xfer - { - u32 iop; /* IOP unit number */ - u8 flags; /* UploadFlags */ - u8 sw_type; /* Software type */ - u32 sw_id; /* Software ID */ - void *buf; /* Pointer to software buffer */ - u32 *swlen; /* Length of software buffer */ - u32 *maxfrag; /* Number of fragments */ - u32 *curfrag; /* Current fragment number */ - }; - - DESCRIPTION - - This function uploads a software fragment from the IOP identified - by sw->iop, sw->sw_type, sw->sw_id and optionally sw->swlen fields. - The UploadFlags, SwID, SwType and SwSize fields of the ExecSwUpload - message are filled in with the values of sw->flags, sw->sw_id, - sw->sw_type and *(sw->swlen). - - The fragments _must_ be requested in order and be 8K in size. The - user is responsible for allocating memory pointed by sw->buf. The - last fragment _may_ be shorter. - - Please note that SW transfers can take a long time. - - RETURNS - - This function returns 0 if no errors occur. If an error occurs, -1 - is returned and errno is set appropriatly: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - -X. Removing Software - - SYNOPSIS - - ioctl(fd, I2OSWDEL, struct i2o_sw_xfer *sw); - - struct i2o_sw_xfer - { - u32 iop; /* IOP unit number */ - u8 flags; /* RemoveFlags */ - u8 sw_type; /* Software type */ - u32 sw_id; /* Software ID */ - void *buf; /* Unused */ - u32 *swlen; /* Length of the software data */ - u32 *maxfrag; /* Unused */ - u32 *curfrag; /* Unused */ - }; - - DESCRIPTION - - This function removes software from the IOP identified by sw->iop. - The RemoveFlags, SwID, SwType and SwSize fields of the ExecSwRemove message - are filled in with the values of sw->flags, sw->sw_id, sw->sw_type and - *(sw->swlen). Give zero in *(sw->len) if the value is unknown. IOP uses - *(sw->swlen) value to verify correct identication of the module to remove. - The actual size of the module is written into *(sw->swlen). - - RETURNS - - This function returns 0 if no errors occur. If an error occurs, -1 - is returned and errno is set appropriatly: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - -X. Validating Configuration - - SYNOPSIS - - ioctl(fd, I2OVALIDATE, int *iop); - u32 iop; - - DESCRIPTION - - This function posts an ExecConfigValidate message to the controller - identified by iop. This message indicates that the the current - configuration is accepted. The iop changes the status of suspect drivers - to valid and may delete old drivers from its store. - - RETURNS - - This function returns 0 if no erro occur. If an error occurs, -1 is - returned and errno is set appropriatly: - - ETIMEDOUT Timeout waiting for reply message - ENXIO Invalid IOP number - -XI. Configuration Dialog - - SYNOPSIS - - ioctl(fd, I2OHTML, struct i2o_html *htquery); - struct i2o_html - { - u32 iop; /* IOP unit number */ - u32 tid; /* Target device ID */ - u32 page; /* HTML page */ - void *resbuf; /* Buffer for reply HTML page */ - u32 *reslen; /* Length in bytes of reply buffer */ - void *qbuf; /* Pointer to HTTP query string */ - u32 qlen; /* Length in bytes of query string buffer */ - }; - - DESCRIPTION - - This function posts an UtilConfigDialog message to the device identified - by htquery->iop and htquery->tid. The requested HTML page number is - provided by the htquery->page field, and the resultant data is stored - in the buffer pointed to by htquery->resbuf. If there is an HTTP query - string that is to be sent to the device, it should be sent in the buffer - pointed to by htquery->qbuf. If there is no query string, this field - should be set to NULL. The actual size of the reply received is written - into *(htquery->reslen). - - RETURNS - - This function returns 0 if no error occur. If an error occurs, -1 - is returned and errno is set appropriatly: - - EFAULT Invalid user space pointer was passed - ENXIO Invalid IOP number - ENOBUFS Buffer not large enough. If this occurs, the required - buffer length is written into *(ops->reslen) - ETIMEDOUT Timeout waiting for reply message - ENOMEM Kernel memory allocation error - -XII. Events - - In the process of determining this. Current idea is to have use - the select() interface to allow user apps to periodically poll - the /dev/i2o/ctl device for events. When select() notifies the user - that an event is available, the user would call read() to retrieve - a list of all the events that are pending for the specific device. - -============================================================================= -Revision History -============================================================================= - -Rev 0.1 - 04/01/99 -- Initial revision - -Rev 0.2 - 04/06/99 -- Changed return values to match UNIX ioctl() standard. Only return values - are 0 and -1. All errors are reported through errno. -- Added summary of proposed possible event interfaces - -Rev 0.3 - 04/20/99 -- Changed all ioctls() to use pointers to user data instead of actual data -- Updated error values to match the code diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_block.c linux.ac/drivers/i2o/i2o_block.c --- linux.vanilla/drivers/i2o/i2o_block.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/i2o/i2o_block.c Thu Jan 1 01:00:00 1970 @@ -1,2068 +0,0 @@ -/* - * I2O Random Block Storage Class OSM - * - * (C) Copyright 1999 Red Hat Software - * - * Written by Alan Cox, Building Number Three Ltd - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * This is a beta test release. Most of the good code was taken - * from the nbd driver by Pavel Machek, who in turn took some of it - * from loop.c. Isn't free software great for reusability 8) - * - * Fixes/additions: - * Steve Ralston: - * Multiple device handling error fixes, - * Added a queue depth. - * Alan Cox: - * FC920 has an rmw bug. Dont or in the end marker. - * Removed queue walk, fixed for 64bitness. - * Deepak Saxena: - * Independent queues per IOP - * Support for dynamic device creation/deletion - * Code cleanup - * Support for larger I/Os through merge* functions - * (taken from DAC960 driver) - * Boji T Kannanthanam: - * Set the I2O Block devices to be detected in increasing - * order of TIDs during boot. - * Search and set the I2O block device that we boot off from as - * the first device to be claimed (as /dev/i2o/hda) - * Properly attach/detach I2O gendisk structure from the system - * gendisk list. The I2O block devices now appear in - * /proc/partitions. - * - * To do: - * Serial number scanning to find duplicates for FC multipathing - */ - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#define MAJOR_NR I2O_MAJOR - -#include - -#define MAX_I2OB 16 - -#define MAX_I2OB_DEPTH 128 -#define MAX_I2OB_RETRIES 4 - -//#define DRIVERDEBUG -#ifdef DRIVERDEBUG -#define DEBUG( s ) -#else -#define DEBUG( s ) printk( s ) -#endif - -/* - * Events that this OSM is interested in - */ -#define I2OB_EVENT_MASK (I2O_EVT_IND_BSA_VOLUME_LOAD | \ - I2O_EVT_IND_BSA_VOLUME_UNLOAD | \ - I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ | \ - I2O_EVT_IND_BSA_CAPACITY_CHANGE | \ - I2O_EVT_IND_BSA_SCSI_SMART ) - - -/* - * I2O Block Error Codes - should be in a header file really... - */ -#define I2O_BSA_DSC_SUCCESS 0x0000 -#define I2O_BSA_DSC_MEDIA_ERROR 0x0001 -#define I2O_BSA_DSC_ACCESS_ERROR 0x0002 -#define I2O_BSA_DSC_DEVICE_FAILURE 0x0003 -#define I2O_BSA_DSC_DEVICE_NOT_READY 0x0004 -#define I2O_BSA_DSC_MEDIA_NOT_PRESENT 0x0005 -#define I2O_BSA_DSC_MEDIA_LOCKED 0x0006 -#define I2O_BSA_DSC_MEDIA_FAILURE 0x0007 -#define I2O_BSA_DSC_PROTOCOL_FAILURE 0x0008 -#define I2O_BSA_DSC_BUS_FAILURE 0x0009 -#define I2O_BSA_DSC_ACCESS_VIOLATION 0x000A -#define I2O_BSA_DSC_WRITE_PROTECTED 0x000B -#define I2O_BSA_DSC_DEVICE_RESET 0x000C -#define I2O_BSA_DSC_VOLUME_CHANGED 0x000D -#define I2O_BSA_DSC_TIMEOUT 0x000E - -/* - * Some of these can be made smaller later - */ - -static int i2ob_blksizes[MAX_I2OB<<4]; -static int i2ob_hardsizes[MAX_I2OB<<4]; -static int i2ob_sizes[MAX_I2OB<<4]; -static int i2ob_media_change_flag[MAX_I2OB]; -static u32 i2ob_max_sectors[MAX_I2OB<<4]; - -static int i2ob_context; - -/* - * I2O Block device descriptor - */ -struct i2ob_device -{ - struct i2o_controller *controller; - struct i2o_device *i2odev; - int unit; - int tid; - int flags; - int refcnt; - struct request *head, *tail; - request_queue_t *req_queue; - int max_segments; - int done_flag; - int constipated; - int depth; -}; - -/* - * FIXME: - * We should cache align these to avoid ping-ponging lines on SMP - * boxes under heavy I/O load... - */ -struct i2ob_request -{ - struct i2ob_request *next; - struct request *req; - int num; -}; - -/* - * Per IOP requst queue information - * - * We have a separate requeust_queue_t per IOP so that a heavilly - * loaded I2O block device on an IOP does not starve block devices - * across all I2O controllers. - * - */ -struct i2ob_iop_queue -{ - atomic_t queue_depth; - struct i2ob_request request_queue[MAX_I2OB_DEPTH]; - struct i2ob_request *i2ob_qhead; - request_queue_t req_queue; -}; -static struct i2ob_iop_queue *i2ob_queues[MAX_I2O_CONTROLLERS]; -static struct i2ob_request *i2ob_backlog[MAX_I2O_CONTROLLERS]; -static struct i2ob_request *i2ob_backlog_tail[MAX_I2O_CONTROLLERS]; - -/* - * Each I2O disk is one of these. - */ - -static struct i2ob_device i2ob_dev[MAX_I2OB<<4]; -static int i2ob_dev_count = 0; -static struct hd_struct i2ob[MAX_I2OB<<4]; -static struct gendisk i2ob_gendisk; /* Declared later */ - -/* - * Mutex and spin lock for event handling synchronization - * evt_msg contains the last event. - */ -static DECLARE_MUTEX_LOCKED(i2ob_evt_sem); -static DECLARE_MUTEX_LOCKED(i2ob_thread_dead); -static spinlock_t i2ob_evt_lock = SPIN_LOCK_UNLOCKED; -static u32 evt_msg[MSG_FRAME_SIZE>>2]; - -static struct timer_list i2ob_timer; -static int i2ob_timer_started = 0; - -static void i2o_block_reply(struct i2o_handler *, struct i2o_controller *, - struct i2o_message *); -static void i2ob_new_device(struct i2o_controller *, struct i2o_device *); -static void i2ob_del_device(struct i2o_controller *, struct i2o_device *); -static void i2ob_reboot_event(void); -static int i2ob_install_device(struct i2o_controller *, struct i2o_device *, int); -static void i2ob_end_request(struct request *); -static void i2ob_request(request_queue_t *); -static int i2ob_backlog_request(struct i2o_controller *, struct i2ob_device *); -static int i2ob_init_iop(unsigned int); -static request_queue_t* i2ob_get_queue(kdev_t); -static int i2ob_query_device(struct i2ob_device *, int, int, void*, int); -static int do_i2ob_revalidate(kdev_t, int); -static int i2ob_evt(void *); - -static int evt_pid = 0; -static int evt_running = 0; -static int scan_unit = 0; - -/* - * I2O OSM registration structure...keeps getting bigger and bigger :) - */ -static struct i2o_handler i2o_block_handler = -{ - i2o_block_reply, - i2ob_new_device, - i2ob_del_device, - i2ob_reboot_event, - "I2O Block OSM", - 0, - I2O_CLASS_RANDOM_BLOCK_STORAGE -}; - -/* - * Get a message - */ - -static u32 i2ob_get(struct i2ob_device *dev) -{ - struct i2o_controller *c=dev->controller; - return I2O_POST_READ32(c); -} - -/* - * Turn a Linux block request into an I2O block read/write. - */ - -static int i2ob_send(u32 m, struct i2ob_device *dev, struct i2ob_request *ireq, u32 base, int unit) -{ - struct i2o_controller *c = dev->controller; - int tid = dev->tid; - unsigned long msg; - unsigned long mptr; - u64 offset; - struct request *req = ireq->req; - struct buffer_head *bh = req->bh; - int count = req->nr_sectors<<9; - char *last = NULL; - unsigned short size = 0; - - // printk(KERN_INFO "i2ob_send called\n"); - /* Map the message to a virtual address */ - msg = c->mem_offset + m; - - /* - * Build the message based on the request. - */ - __raw_writel(i2ob_context|(unit<<8), msg+8); - __raw_writel(ireq->num, msg+12); - __raw_writel(req->nr_sectors << 9, msg+20); - - /* - * Mask out partitions from now on - */ - unit &= 0xF0; - - /* This can be optimised later - just want to be sure its right for - starters */ - offset = ((u64)(req->sector+base)) << 9; - __raw_writel( offset & 0xFFFFFFFF, msg+24); - __raw_writel(offset>>32, msg+28); - mptr=msg+32; - - if(req->cmd == READ) - { - __raw_writel(I2O_CMD_BLOCK_READ<<24|HOST_TID<<12|tid, msg+4); - while(bh!=NULL) - { - if(bh->b_data == last) { - size += bh->b_size; - last += bh->b_size; - if(bh->b_reqnext) - __raw_writel(0x14000000|(size), mptr-8); - else - __raw_writel(0xD4000000|(size), mptr-8); - } - else - { - if(bh->b_reqnext) - __raw_writel(0x10000000|(bh->b_size), mptr); - else - __raw_writel(0xD0000000|(bh->b_size), mptr); - __raw_writel(virt_to_bus(bh->b_data), mptr+4); - mptr += 8; - size = bh->b_size; - last = bh->b_data + size; - } - - count -= bh->b_size; - bh = bh->b_reqnext; - } - /* - * Heuristic for now since the block layer doesnt give - * us enough info. If its a big write assume sequential - * readahead on controller. If its small then don't read - * ahead but do use the controller cache. - */ - if(size >= 8192) - __raw_writel((8<<24)|(1<<16)|8, msg+16); - else - __raw_writel((8<<24)|(1<<16)|4, msg+16); - } - else if(req->cmd == WRITE) - { - __raw_writel(I2O_CMD_BLOCK_WRITE<<24|HOST_TID<<12|tid, msg+4); - while(bh!=NULL) - { - if(bh->b_data == last) { - size += bh->b_size; - last += bh->b_size; - if(bh->b_reqnext) - __raw_writel(0x14000000|(size), mptr-8); - else - __raw_writel(0xD4000000|(size), mptr-8); - } - else - { - if(bh->b_reqnext) - __raw_writel(0x14000000|(bh->b_size), mptr); - else - __raw_writel(0xD4000000|(bh->b_size), mptr); - __raw_writel(virt_to_bus(bh->b_data), mptr+4); - mptr += 8; - size = bh->b_size; - last = bh->b_data + size; - } - - count -= bh->b_size; - bh = bh->b_reqnext; - } - - if(c->battery) - { - - if(size>16384) - __raw_writel(4, msg+16); - else - /* - * Allow replies to come back once data is cached in the controller - * This allows us to handle writes quickly thus giving more of the - * queue to reads. - */ - __raw_writel(16, msg+16); - } - else - { - /* Large write, don't cache */ - if(size>8192) - __raw_writel(4, msg+16); - else - /* write through */ - __raw_writel(8, msg+16); - } - } - __raw_writel(I2O_MESSAGE_SIZE(mptr-msg)>>2 | SGL_OFFSET_8, msg); - - if(count != 0) - { - printk(KERN_ERR "Request count botched by %d.\n", count); - } - - i2o_post_message(c,m); - atomic_inc(&i2ob_queues[c->unit]->queue_depth); - - return 0; -} - -/* - * Remove a request from the _locked_ request list. We update both the - * list chain and if this is the last item the tail pointer. Caller - * must hold the lock. - */ - -static inline void i2ob_unhook_request(struct i2ob_request *ireq, - unsigned int iop) -{ - ireq->next = i2ob_queues[iop]->i2ob_qhead; - i2ob_queues[iop]->i2ob_qhead = ireq; -} - -/* - * Request completion handler - */ - -static inline void i2ob_end_request(struct request *req) -{ - /* - * Loop until all of the buffers that are linked - * to this request have been marked updated and - * unlocked. - */ - - while (end_that_request_first( req, !req->errors, "i2o block" )); - - /* - * It is now ok to complete the request. - */ - end_that_request_last( req ); -} - -/* - * Request merging functions - */ -static inline int i2ob_new_segment(request_queue_t *q, struct request *req, - int __max_segments) -{ - int max_segments = i2ob_dev[MINOR(req->rq_dev)].max_segments; - - if (__max_segments < max_segments) - max_segments = __max_segments; - - if (req->nr_segments < max_segments) { - req->nr_segments++; - return 1; - } - return 0; -} - -static int i2ob_back_merge(request_queue_t *q, struct request *req, - struct buffer_head *bh, int __max_segments) -{ - if (req->bhtail->b_data + req->bhtail->b_size == bh->b_data) - return 1; - return i2ob_new_segment(q, req, __max_segments); -} - -static int i2ob_front_merge(request_queue_t *q, struct request *req, - struct buffer_head *bh, int __max_segments) -{ - if (bh->b_data + bh->b_size == req->bh->b_data) - return 1; - return i2ob_new_segment(q, req, __max_segments); -} - -static int i2ob_merge_requests(request_queue_t *q, - struct request *req, - struct request *next, - int __max_segments) -{ - int max_segments = i2ob_dev[MINOR(req->rq_dev)].max_segments; - int total_segments = req->nr_segments + next->nr_segments; - - if (__max_segments < max_segments) - max_segments = __max_segments; - - if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) - total_segments--; - - if (total_segments > max_segments) - return 0; - - req->nr_segments = total_segments; - return 1; -} - -static int i2ob_flush(struct i2o_controller *c, struct i2ob_device *d, int unit) -{ - unsigned long msg; - u32 m = i2ob_get(d); - - if(m == 0xFFFFFFFF) - return -1; - - msg = c->mem_offset + m; - - /* - * Ask the controller to write the cache back. This sorts out - * the supertrak firmware flaw and also does roughly the right - * thing for other cases too. - */ - - __raw_writel(FIVE_WORD_MSG_SIZE|SGL_OFFSET_0, msg); - __raw_writel(I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|d->tid, msg+4); - __raw_writel(i2ob_context|(unit<<8), msg+8); - __raw_writel(0, msg+12); - __raw_writel(60<<16, msg+16); - - i2o_post_message(c,m); - return 0; -} - -/* - * OSM reply handler. This gets all the message replies - */ - -static void i2o_block_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg) -{ - unsigned long flags; - struct i2ob_request *ireq = NULL; - u8 st; - u32 *m = (u32 *)msg; - u8 unit = (m[2]>>8)&0xF0; /* low 4 bits are partition */ - struct i2ob_device *dev = &i2ob_dev[(unit&0xF0)]; - - /* - * FAILed message - */ - if(m[0] & (1<<13)) - { - /* - * FAILed message from controller - * We increment the error count and abort it - * - * In theory this will never happen. The I2O block class - * speficiation states that block devices never return - * FAILs but instead use the REQ status field...but - * better be on the safe side since no one really follows - * the spec to the book :) - */ - ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; - ireq->req->errors++; - - spin_lock_irqsave(&io_request_lock, flags); - i2ob_unhook_request(ireq, c->unit); - i2ob_end_request(ireq->req); - spin_unlock_irqrestore(&io_request_lock, flags); - - /* Now flush the message by making it a NOP */ - m[0]&=0x00FFFFFF; - m[0]|=(I2O_CMD_UTIL_NOP)<<24; - i2o_post_message(c,virt_to_bus(m)); - - return; - } - - if(msg->function == I2O_CMD_UTIL_EVT_REGISTER) - { - spin_lock(&i2ob_evt_lock); - memcpy(evt_msg, msg, (m[0]>>16)<<2); - spin_unlock(&i2ob_evt_lock); - up(&i2ob_evt_sem); - return; - } - - if(msg->function == I2O_CMD_BLOCK_CFLUSH) - { - spin_lock_irqsave(&io_request_lock, flags); - dev->constipated=0; - DEBUG(("unconstipated\n")); - if(i2ob_backlog_request(c, dev)==0) - i2ob_request(dev->req_queue); - spin_unlock_irqrestore(&io_request_lock, flags); - return; - } - - if(!dev->i2odev) - { - /* - * This is HACK, but Intel Integrated RAID allows user - * to delete a volume that is claimed, locked, and in use - * by the OS. We have to check for a reply from a - * non-existent device and flag it as an error or the system - * goes kaput... - */ - ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; - ireq->req->errors++; - printk(KERN_WARNING "I2O Block: Data transfer to deleted device!\n"); - spin_lock_irqsave(&io_request_lock, flags); - i2ob_unhook_request(ireq, c->unit); - i2ob_end_request(ireq->req); - spin_unlock_irqrestore(&io_request_lock, flags); - return; - } - - /* - * Lets see what is cooking. We stuffed the - * request in the context. - */ - - ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; - st=m[4]>>24; - - if(st!=0) - { - int err; - char *bsa_errors[] = - { - "Success", - "Media Error", - "Failure communicating to device", - "Device Failure", - "Device is not ready", - "Media not present", - "Media is locked by another user", - "Media has failed", - "Failure communicating to device", - "Device bus failure", - "Device is locked by another user", - "Device is write protected", - "Device has reset", - "Volume has changed, waiting for acknowledgement" - }; - - err = m[4]&0xFFFF; - - /* - * Device not ready means two things. One is that the - * the thing went offline (but not a removal media) - * - * The second is that you have a SuperTrak 100 and the - * firmware got constipated. Unlike standard i2o card - * setups the supertrak returns an error rather than - * blocking for the timeout in these cases. - */ - - - spin_lock_irqsave(&io_request_lock, flags); - if(err==4) - { - /* - * Time to uncork stuff - */ - - if(!dev->constipated) - { - dev->constipated = 1; - DEBUG(("constipated\n")); - /* Now pull the chain */ - if(i2ob_flush(c, dev, unit)<0) - { - DEBUG(("i2ob: Unable to queue flush. Retrying I/O immediately.\n")); - dev->constipated=0; - } - DEBUG(("flushing\n")); - } - - /* - * Recycle the request - */ - -// i2ob_unhook_request(ireq, c->unit); - - /* - * Place it on the recycle queue - */ - - ireq->next = NULL; - if(i2ob_backlog_tail[c->unit]!=NULL) - i2ob_backlog_tail[c->unit]->next = ireq; - else - i2ob_backlog[c->unit] = ireq; - i2ob_backlog_tail[c->unit] = ireq; - - atomic_dec(&i2ob_queues[c->unit]->queue_depth); - - /* - * If the constipator flush failed we want to - * poke the queue again. - */ - - i2ob_request(dev->req_queue); - spin_unlock_irqrestore(&io_request_lock, flags); - - /* - * and out - */ - - return; - } - spin_unlock_irqrestore(&io_request_lock, flags); - printk(KERN_ERR "\n/dev/%s error: %s", dev->i2odev->dev_name, - bsa_errors[m[4]&0XFFFF]); - if(m[4]&0x00FF0000) - printk(" - DDM attempted %d retries", (m[4]>>16)&0x00FF ); - printk(".\n"); - ireq->req->errors++; - } - else - ireq->req->errors = 0; - - /* - * Dequeue the request. We use irqsave locks as one day we - * may be running polled controllers from a BH... - */ - - spin_lock_irqsave(&io_request_lock, flags); - i2ob_unhook_request(ireq, c->unit); - i2ob_end_request(ireq->req); - atomic_dec(&i2ob_queues[c->unit]->queue_depth); - - /* - * We may be able to do more I/O - */ - - if(i2ob_backlog_request(c, dev)==0) - i2ob_request(dev->req_queue); - - spin_unlock_irqrestore(&io_request_lock, flags); -} - -/* - * Event handler. Needs to be a separate thread b/c we may have - * to do things like scan a partition table, or query parameters - * which cannot be done from an interrupt or from a bottom half. - */ -static int i2ob_evt(void *dummy) -{ - unsigned int evt; - unsigned int flags; - int unit; - int i; - //The only event that has data is the SCSI_SMART event. - struct i2o_reply { - u32 header[4]; - u32 evt_indicator; - u8 ASC; - u8 ASCQ; - u8 data[16]; - } *evt_local; - - lock_kernel(); - daemonize(); - unlock_kernel(); - - strcpy(current->comm, "i2oblock"); - evt_running = 1; - - while(1) - { - if(down_interruptible(&i2ob_evt_sem)) - { - evt_running = 0; - printk("exiting..."); - break; - } - - /* - * Keep another CPU/interrupt from overwriting the - * message while we're reading it - * - * We stuffed the unit in the TxContext and grab the event mask - * None of the BSA we care about events have EventData - */ - spin_lock_irqsave(&i2ob_evt_lock, flags); - evt_local = (struct i2o_reply *)evt_msg; - spin_unlock_irqrestore(&i2ob_evt_lock, flags); - - unit = evt_local->header[3]; - evt = evt_local->evt_indicator; - - switch(evt) - { - /* - * New volume loaded on same TID, so we just re-install. - * The TID/controller don't change as it is the same - * I2O device. It's just new media that we have to - * rescan. - */ - case I2O_EVT_IND_BSA_VOLUME_LOAD: - { - i2ob_install_device(i2ob_dev[unit].i2odev->controller, - i2ob_dev[unit].i2odev, unit); - break; - } - - /* - * No media, so set all parameters to 0 and set the media - * change flag. The I2O device is still valid, just doesn't - * have media, so we don't want to clear the controller or - * device pointer. - */ - case I2O_EVT_IND_BSA_VOLUME_UNLOAD: - { - for(i = unit; i <= unit+15; i++) - { - i2ob_sizes[i] = 0; - i2ob_hardsizes[i] = 0; - i2ob_max_sectors[i] = 0; - i2ob[i].nr_sects = 0; - i2ob_gendisk.part[i].nr_sects = 0; - } - i2ob_media_change_flag[unit] = 1; - break; - } - - case I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ: - printk(KERN_WARNING "%s: Attempt to eject locked media\n", - i2ob_dev[unit].i2odev->dev_name); - break; - - /* - * The capacity has changed and we are going to be - * updating the max_sectors and other information - * about this disk. We try a revalidate first. If - * the block device is in use, we don't want to - * do that as there may be I/Os bound for the disk - * at the moment. In that case we read the size - * from the device and update the information ourselves - * and the user can later force a partition table - * update through an ioctl. - */ - case I2O_EVT_IND_BSA_CAPACITY_CHANGE: - { - u64 size; - - if(do_i2ob_revalidate(MKDEV(MAJOR_NR, unit),0) != -EBUSY) - continue; - - if(i2ob_query_device(&i2ob_dev[unit], 0x0004, 0, &size, 8) !=0 ) - i2ob_query_device(&i2ob_dev[unit], 0x0000, 4, &size, 8); - - spin_lock_irqsave(&io_request_lock, flags); - i2ob_sizes[unit] = (int)(size>>10); - i2ob_gendisk.part[unit].nr_sects = size>>9; - i2ob[unit].nr_sects = (int)(size>>9); - spin_unlock_irqrestore(&io_request_lock, flags); - break; - } - - /* - * We got a SCSI SMART event, we just log the relevant - * information and let the user decide what they want - * to do with the information. - */ - case I2O_EVT_IND_BSA_SCSI_SMART: - { - char buf[16]; - printk(KERN_INFO "I2O Block: %s received a SCSI SMART Event\n",i2ob_dev[unit].i2odev->dev_name); - evt_local->data[16]='\0'; - sprintf(buf,"%s",&evt_local->data[0]); - printk(KERN_INFO " Disk Serial#:%s\n",buf); - printk(KERN_INFO " ASC 0x%02x \n",evt_local->ASC); - printk(KERN_INFO " ASCQ 0x%02x \n",evt_local->ASCQ); - break; - } - - /* - * Non event - */ - - case 0: - break; - - /* - * An event we didn't ask for. Call the card manufacturer - * and tell them to fix their firmware :) - */ - default: - printk(KERN_INFO "%s: Received event %d we didn't register for\n" - KERN_INFO " Blame the I2O card manufacturer 8)\n", - i2ob_dev[unit].i2odev->dev_name, evt); - break; - } - }; - - up_and_exit(&i2ob_thread_dead,0); - return 0; -} - -/* - * The timer handler will attempt to restart requests - * that are queued to the driver. This handler - * currently only gets called if the controller - * had no more room in its inbound fifo. - */ - -static void i2ob_timer_handler(unsigned long q) -{ - unsigned long flags; - - /* - * We cannot touch the request queue or the timer - * flag without holding the io_request_lock. - */ - spin_lock_irqsave(&io_request_lock,flags); - - /* - * Clear the timer started flag so that - * the timer can be queued again. - */ - i2ob_timer_started = 0; - - /* - * Restart any requests. - */ - i2ob_request((request_queue_t*)q); - - /* - * Free the lock. - */ - spin_unlock_irqrestore(&io_request_lock,flags); -} - -static int i2ob_backlog_request(struct i2o_controller *c, struct i2ob_device *dev) -{ - u32 m; - struct i2ob_request *ireq; - - while((ireq=i2ob_backlog[c->unit])!=NULL) - { - int unit; - - if(atomic_read(&i2ob_queues[c->unit]->queue_depth) > dev->depth/4) - break; - - m = i2ob_get(dev); - if(m == 0xFFFFFFFF) - break; - - i2ob_backlog[c->unit] = ireq->next; - if(i2ob_backlog[c->unit] == NULL) - i2ob_backlog_tail[c->unit] = NULL; - - unit = MINOR(ireq->req->rq_dev); - i2ob_send(m, dev, ireq, i2ob[unit].start_sect, unit); - } - if(i2ob_backlog[c->unit]) - return 1; - return 0; -} - -/* - * The I2O block driver is listed as one of those that pulls the - * front entry off the queue before processing it. This is important - * to remember here. If we drop the io lock then CURRENT will change - * on us. We must unlink CURRENT in this routine before we return, if - * we use it. - */ - -static void i2ob_request(request_queue_t *q) -{ - struct request *req; - struct i2ob_request *ireq; - int unit; - struct i2ob_device *dev; - u32 m; - - - while (!list_empty(&q->queue_head)) { - /* - * On an IRQ completion if there is an inactive - * request on the queue head it means it isnt yet - * ready to dispatch. - */ - req = blkdev_entry_next_request(&q->queue_head); - - if(req->rq_status == RQ_INACTIVE) - return; - - unit = MINOR(req->rq_dev); - dev = &i2ob_dev[(unit&0xF0)]; - - /* - * Queue depths probably belong with some kind of - * generic IOP commit control. Certainly its not right - * its global! - */ - if(atomic_read(&i2ob_queues[dev->unit]->queue_depth) >= dev->depth) - break; - - /* - * Is the channel constipated ? - */ - - if(i2ob_backlog[dev->unit]!=NULL) - break; - - /* Get a message */ - m = i2ob_get(dev); - - if(m==0xFFFFFFFF) - { - /* - * See if the timer has already been queued. - */ - if (!i2ob_timer_started) - { - DEBUG((KERN_ERR "i2ob: starting timer\n")); - - /* - * Set the timer_started flag to insure - * that the timer is only queued once. - * Queing it more than once will corrupt - * the timer queue. - */ - i2ob_timer_started = 1; - - /* - * Set up the timer to expire in - * 500ms. - */ - i2ob_timer.expires = jiffies + (HZ >> 1); - i2ob_timer.data = (unsigned int)q; - - /* - * Start it. - */ - - add_timer(&i2ob_timer); - return; - } - } - - /* - * Everything ok, so pull from kernel queue onto our queue - */ - req->errors = 0; - blkdev_dequeue_request(req); - req->sem = NULL; - - ireq = i2ob_queues[dev->unit]->i2ob_qhead; - i2ob_queues[dev->unit]->i2ob_qhead = ireq->next; - ireq->req = req; - - i2ob_send(m, dev, ireq, i2ob[unit].start_sect, (unit&0xF0)); - } -} - - -/* - * SCSI-CAM for ioctl geometry mapping - * Duplicated with SCSI - this should be moved into somewhere common - * perhaps genhd ? - * - * LBA -> CHS mapping table taken from: - * - * "Incorporating the I2O Architecture into BIOS for Intel Architecture - * Platforms" - * - * This is an I2O document that is only available to I2O members, - * not developers. - * - * From my understanding, this is how all the I2O cards do this - * - * Disk Size | Sectors | Heads | Cylinders - * ---------------+---------+-------+------------------- - * 1 < X <= 528M | 63 | 16 | X/(63 * 16 * 512) - * 528M < X <= 1G | 63 | 32 | X/(63 * 32 * 512) - * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512) - * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512) - * - */ -#define BLOCK_SIZE_528M 1081344 -#define BLOCK_SIZE_1G 2097152 -#define BLOCK_SIZE_21G 4403200 -#define BLOCK_SIZE_42G 8806400 -#define BLOCK_SIZE_84G 17612800 - -static void i2o_block_biosparam( - unsigned long capacity, - unsigned short *cyls, - unsigned char *hds, - unsigned char *secs) -{ - unsigned long heads, sectors, cylinders; - - sectors = 63L; /* Maximize sectors per track */ - if(capacity <= BLOCK_SIZE_528M) - heads = 16; - else if(capacity <= BLOCK_SIZE_1G) - heads = 32; - else if(capacity <= BLOCK_SIZE_21G) - heads = 64; - else if(capacity <= BLOCK_SIZE_42G) - heads = 128; - else - heads = 255; - - cylinders = capacity / (heads * sectors); - - *cyls = (unsigned short) cylinders; /* Stuff return values */ - *secs = (unsigned char) sectors; - *hds = (unsigned char) heads; -} - - -/* - * Rescan the partition tables - */ - -static int do_i2ob_revalidate(kdev_t dev, int maxu) -{ - int minor=MINOR(dev); - int i; - - minor&=0xF0; - - i2ob_dev[minor].refcnt++; - if(i2ob_dev[minor].refcnt>maxu+1) - { - i2ob_dev[minor].refcnt--; - return -EBUSY; - } - - for( i = 15; i>=0 ; i--) - { - int m = minor+i; - invalidate_device(MKDEV(MAJOR_NR, m), 1); - i2ob_gendisk.part[m].start_sect = 0; - i2ob_gendisk.part[m].nr_sects = 0; - } - - /* - * Do a physical check and then reconfigure - */ - - i2ob_install_device(i2ob_dev[minor].controller, i2ob_dev[minor].i2odev, - minor); - i2ob_dev[minor].refcnt--; - return 0; -} - -/* - * Issue device specific ioctl calls. - */ - -static int i2ob_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct i2ob_device *dev; - int minor; - - /* Anyone capable of this syscall can do *real bad* things */ - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (!inode) - return -EINVAL; - minor = MINOR(inode->i_rdev); - if (minor >= (MAX_I2OB<<4)) - return -ENODEV; - - dev = &i2ob_dev[minor]; - switch (cmd) { - case BLKGETSIZE: - return put_user(i2ob[minor].nr_sects, (long *) arg); - - case HDIO_GETGEO: - { - struct hd_geometry g; - int u=minor&0xF0; - i2o_block_biosparam(i2ob_sizes[u]<<1, - &g.cylinders, &g.heads, &g.sectors); - g.start = i2ob[minor].start_sect; - return copy_to_user((void *)arg,&g, sizeof(g))?-EFAULT:0; - } - - case BLKRRPART: - if(!capable(CAP_SYS_ADMIN)) - return -EACCES; - return do_i2ob_revalidate(inode->i_rdev,1); - - case BLKFLSBUF: - case BLKROSET: - case BLKROGET: - case BLKRASET: - case BLKRAGET: - case BLKPG: - return blk_ioctl(inode->i_rdev, cmd, arg); - - default: - return -EINVAL; - } -} - -/* - * Close the block device down - */ - -static int i2ob_release(struct inode *inode, struct file *file) -{ - struct i2ob_device *dev; - int minor; - - minor = MINOR(inode->i_rdev); - if (minor >= (MAX_I2OB<<4)) - return -ENODEV; - dev = &i2ob_dev[(minor&0xF0)]; - - /* - * This is to deail with the case of an application - * opening a device and then the device dissapears while - * it's in use, and then the application tries to release - * it. ex: Unmounting a deleted RAID volume at reboot. - * If we send messages, it will just cause FAILs since - * the TID no longer exists. - */ - if(!dev->i2odev) - return 0; - - /* Sync the device so we don't get errors */ - fsync_dev(inode->i_rdev); - - if (dev->refcnt <= 0) - printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt); - dev->refcnt--; - if(dev->refcnt==0) - { - /* - * Flush the onboard cache on unmount - */ - u32 msg[5]; - int *query_done = &dev->done_flag; - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = 60<<16; - DEBUG("Flushing..."); - i2o_post_wait(dev->controller, msg, 20, 60); - - /* - * Unlock the media - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = -1; - DEBUG("Unlocking..."); - i2o_post_wait(dev->controller, msg, 20, 2); - DEBUG("Unlocked.\n"); - - /* - * Now unclaim the device. - */ - - if (i2o_release_device(dev->i2odev, &i2o_block_handler)) - printk(KERN_ERR "i2ob_release: controller rejected unclaim.\n"); - - DEBUG("Unclaim\n"); - } - MOD_DEC_USE_COUNT; - return 0; -} - -/* - * Open the block device. - */ - -static int i2ob_open(struct inode *inode, struct file *file) -{ - int minor; - struct i2ob_device *dev; - - if (!inode) - return -EINVAL; - minor = MINOR(inode->i_rdev); - if (minor >= MAX_I2OB<<4) - return -ENODEV; - dev=&i2ob_dev[(minor&0xF0)]; - - if(!dev->i2odev) - return -ENODEV; - - if(dev->refcnt++==0) - { - u32 msg[6]; - - DEBUG("Claim "); - if(i2o_claim_device(dev->i2odev, &i2o_block_handler)) - { - dev->refcnt--; - printk(KERN_INFO "I2O Block: Could not open device\n"); - return -EBUSY; - } - DEBUG("Claimed "); - - /* - * Mount the media if needed. Note that we don't use - * the lock bit. Since we have to issue a lock if it - * refuses a mount (quite possible) then we might as - * well just send two messages out. - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MMOUNT<<24|HOST_TID<<12|dev->tid; - msg[4] = -1; - msg[5] = 0; - DEBUG("Mount "); - i2o_post_wait(dev->controller, msg, 24, 2); - - /* - * Lock the media - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MLOCK<<24|HOST_TID<<12|dev->tid; - msg[4] = -1; - DEBUG("Lock "); - i2o_post_wait(dev->controller, msg, 20, 2); - DEBUG("Ready.\n"); - } - MOD_INC_USE_COUNT; - return 0; -} - -/* - * Issue a device query - */ - -static int i2ob_query_device(struct i2ob_device *dev, int table, - int field, void *buf, int buflen) -{ - return i2o_query_scalar(dev->controller, dev->tid, - table, field, buf, buflen); -} - - -/* - * Install the I2O block device we found. - */ - -static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, int unit) -{ - u64 size; - u32 blocksize; - u32 limit; - u8 type; - u32 flags, status; - struct i2ob_device *dev=&i2ob_dev[unit]; - int i; - - /* - * For logging purposes... - */ - printk(KERN_INFO "i2ob: Installing tid %d device at unit %d\n", - d->lct_data.tid, unit); - - /* - * Ask for the current media data. If that isn't supported - * then we ask for the device capacity data - */ - if(i2ob_query_device(dev, 0x0004, 1, &blocksize, 4) != 0 - || i2ob_query_device(dev, 0x0004, 0, &size, 8) !=0 ) - { - i2ob_query_device(dev, 0x0000, 3, &blocksize, 4); - i2ob_query_device(dev, 0x0000, 4, &size, 8); - } - - i2ob_query_device(dev, 0x0000, 5, &flags, 4); - i2ob_query_device(dev, 0x0000, 6, &status, 4); - i2ob_sizes[unit] = (int)(size>>10); - for(i=unit; i <= unit+15 ; i++) - i2ob_hardsizes[i] = blocksize; - i2ob_gendisk.part[unit].nr_sects = size>>9; - i2ob[unit].nr_sects = (int)(size>>9); - - /* Set limit based on inbound frame size */ - limit = (d->controller->status_block->inbound_frame_size - 8)/2; - limit = limit<<9; - - /* - * Max number of Scatter-Gather Elements - */ - - for(i=unit;i<=unit+15;i++) - { - if(d->controller->type == I2O_TYPE_PCI && d->controller->bus.pci.queue_buggy) - { - i2ob_max_sectors[i] = 32; - i2ob_dev[i].max_segments = 8; - i2ob_dev[i].depth = 4; - } - else if(d->controller->type == I2O_TYPE_PCI && d->controller->bus.pci.short_req) - { - i2ob_max_sectors[i] = 8; - i2ob_dev[i].max_segments = 8; - } - else - { - /* MAX_SECTORS was used but 255 is a dumb number for - striped RAID */ - i2ob_max_sectors[i]=256; - i2ob_dev[i].max_segments = (d->controller->status_block->inbound_frame_size - 8)/2; - } - } - - printk(KERN_INFO "Max segments set to %d\n", - i2ob_dev[unit].max_segments); - printk(KERN_INFO "Byte limit is %d.\n", limit); - - i2ob_query_device(dev, 0x0000, 0, &type, 1); - - sprintf(d->dev_name, "%s%c", i2ob_gendisk.major_name, 'a' + (unit>>4)); - - printk(KERN_INFO "%s: ", d->dev_name); - switch(type) - { - case 0: printk("Disk Storage");break; - case 4: printk("WORM");break; - case 5: printk("CD-ROM");break; - case 7: printk("Optical device");break; - default: - printk("Type %d", type); - } - if(status&(1<<10)) - printk("(RAID)"); - if(((flags & (1<<3)) && !(status & (1<<3))) || - ((flags & (1<<4)) && !(status & (1<<4)))) - { - printk(KERN_INFO " Not loaded.\n"); - return 1; - } - printk("- %dMb, %d byte sectors", - (int)(size>>20), blocksize); - if(status&(1<<0)) - { - u32 cachesize; - i2ob_query_device(dev, 0x0003, 0, &cachesize, 4); - cachesize>>=10; - if(cachesize>4095) - printk(", %dMb cache", cachesize>>10); - else - printk(", %dKb cache", cachesize); - - } - printk(".\n"); - printk(KERN_INFO "%s: Maximum sectors/read set to %d.\n", - d->dev_name, i2ob_max_sectors[unit]); - - /* - * If this is the first I2O block device found on this IOP, - * we need to initialize all the queue data structures - * before any I/O can be performed. If it fails, this - * device is useless. - */ - if(!i2ob_queues[c->unit]) { - if(i2ob_init_iop(c->unit)) - return 1; - } - - /* - * This will save one level of lookup/indirection in critical - * code so that we can directly get the queue ptr from the - * device instead of having to go the IOP data structure. - */ - dev->req_queue = &i2ob_queues[c->unit]->req_queue; - - grok_partitions(&i2ob_gendisk, unit>>4, 1<<4, (long)(size>>9)); - - /* - * Register for the events we're interested in and that the - * device actually supports. - */ - i2o_event_register(c, d->lct_data.tid, i2ob_context, unit, - (I2OB_EVENT_MASK & d->lct_data.event_capabilities)); - - return 0; -} - -/* - * Initialize IOP specific queue structures. This is called - * once for each IOP that has a block device sitting behind it. - */ -static int i2ob_init_iop(unsigned int unit) -{ - int i; - - i2ob_queues[unit] = (struct i2ob_iop_queue*) - kmalloc(sizeof(struct i2ob_iop_queue), GFP_ATOMIC); - if(!i2ob_queues[unit]) - { - printk(KERN_WARNING - "Could not allocate request queue for I2O block device!\n"); - return -1; - } - - for(i = 0; i< MAX_I2OB_DEPTH; i++) - { - i2ob_queues[unit]->request_queue[i].next = - &i2ob_queues[unit]->request_queue[i+1]; - i2ob_queues[unit]->request_queue[i].num = i; - } - - /* Queue is MAX_I2OB + 1... */ - i2ob_queues[unit]->request_queue[i].next = NULL; - i2ob_queues[unit]->i2ob_qhead = &i2ob_queues[unit]->request_queue[0]; - atomic_set(&i2ob_queues[unit]->queue_depth, 0); - - blk_init_queue(&i2ob_queues[unit]->req_queue, i2ob_request); - blk_queue_headactive(&i2ob_queues[unit]->req_queue, 0); - i2ob_queues[unit]->req_queue.back_merge_fn = i2ob_back_merge; - i2ob_queues[unit]->req_queue.front_merge_fn = i2ob_front_merge; - i2ob_queues[unit]->req_queue.merge_requests_fn = i2ob_merge_requests; - i2ob_queues[unit]->req_queue.queuedata = &i2ob_queues[unit]; - - return 0; -} - -/* - * Get the request queue for the given device. - */ -static request_queue_t* i2ob_get_queue(kdev_t dev) -{ - int unit = MINOR(dev)&0xF0; - - return i2ob_dev[unit].req_queue; -} - -/* - * Probe the I2O subsytem for block class devices - */ -static void i2ob_scan(int bios) -{ - int i; - int warned = 0; - - struct i2o_device *d, *b=NULL; - struct i2o_controller *c; - struct i2ob_device *dev; - - for(i=0; i< MAX_I2O_CONTROLLERS; i++) - { - c=i2o_find_controller(i); - - if(c==NULL) - continue; - - /* - * The device list connected to the I2O Controller is doubly linked - * Here we traverse the end of the list , and start claiming devices - * from that end. This assures that within an I2O controller atleast - * the newly created volumes get claimed after the older ones, thus - * mapping to same major/minor (and hence device file name) after - * every reboot. - * The exception being: - * 1. If there was a TID reuse. - * 2. There was more than one I2O controller. - */ - - if(!bios) - { - for (d=c->devices;d!=NULL;d=d->next) - if(d->next == NULL) - b = d; - } - else - b = c->devices; - - while(b != NULL) - { - d=b; - if(bios) - b = b->next; - else - b = b->prev; - - if(d->lct_data.class_id!=I2O_CLASS_RANDOM_BLOCK_STORAGE) - continue; - - if(d->lct_data.user_tid != 0xFFF) - continue; - - if(bios) - { - if(d->lct_data.bios_info != 0x80) - continue; - printk(KERN_INFO "Claiming as Boot device: Controller %d, TID %d\n", c->unit, d->lct_data.tid); - } - else - { - if(d->lct_data.bios_info == 0x80) - continue; /*Already claimed on pass 1 */ - } - - if(i2o_claim_device(d, &i2o_block_handler)) - { - printk(KERN_WARNING "i2o_block: Controller %d, TID %d\n", c->unit, - d->lct_data.tid); - printk(KERN_WARNING "\t%sevice refused claim! Skipping installation\n", bios?"Boot d":"D"); - continue; - } - - if(scan_uniti2odev = d; - dev->controller = c; - dev->unit = c->unit; - dev->tid = d->lct_data.tid; - - if(i2ob_install_device(c,d,scan_unit)) - printk(KERN_WARNING "Could not install I2O block device\n"); - else - { - scan_unit+=16; - i2ob_dev_count++; - - /* We want to know when device goes away */ - i2o_device_notify_on(d, &i2o_block_handler); - } - } - else - { - if(!warned++) - printk(KERN_WARNING "i2o_block: too many device, registering only %d.\n", scan_unit>>4); - } - i2o_release_device(d, &i2o_block_handler); - } - i2o_unlock_controller(c); - } -} - -static void i2ob_probe(void) -{ - /* - * Some overhead/redundancy involved here, while trying to - * claim the first boot volume encountered as /dev/i2o/hda - * everytime. All the i2o_controllers are searched and the - * first i2o block device marked as bootable is claimed - * If an I2O block device was booted off , the bios sets - * its bios_info field to 0x80, this what we search for. - * Assuming that the bootable volume is /dev/i2o/hda - * everytime will prevent any kernel panic while mounting - * root partition - */ - - printk(KERN_INFO "i2o_block: Checking for Boot device...\n"); - i2ob_scan(1); - - /* - * Now the remainder. - */ - printk(KERN_INFO "i2o_block: Checking for I2O Block devices...\n"); - i2ob_scan(0); -} - - -/* - * New device notification handler. Called whenever a new - * I2O block storage device is added to the system. - * - * Should we spin lock around this to keep multiple devs from - * getting updated at the same time? - * - */ -void i2ob_new_device(struct i2o_controller *c, struct i2o_device *d) -{ - struct i2ob_device *dev; - int unit = 0; - - printk(KERN_INFO "i2o_block: New device detected\n"); - printk(KERN_INFO " Controller %d Tid %d\n",c->unit, d->lct_data.tid); - - /* Check for available space */ - if(i2ob_dev_count>=MAX_I2OB<<4) - { - printk(KERN_ERR "i2o_block: No more devices allowed!\n"); - return; - } - for(unit = 0; unit < (MAX_I2OB<<4); unit += 16) - { - if(!i2ob_dev[unit].i2odev) - break; - } - - if(i2o_claim_device(d, &i2o_block_handler)) - { - printk(KERN_INFO - "i2o_block: Unable to claim device. Installation aborted\n"); - return; - } - - dev = &i2ob_dev[unit]; - dev->i2odev = d; - dev->controller = c; - dev->tid = d->lct_data.tid; - - if(i2ob_install_device(c,d,unit)) - printk(KERN_ERR "i2o_block: Could not install new device\n"); - else - { - i2ob_dev_count++; - i2o_device_notify_on(d, &i2o_block_handler); - } - - i2o_release_device(d, &i2o_block_handler); - - return; -} - -/* - * Deleted device notification handler. Called when a device we - * are talking to has been deleted by the user or some other - * mysterious fource outside the kernel. - */ -void i2ob_del_device(struct i2o_controller *c, struct i2o_device *d) -{ - int unit = 0; - int i = 0; - int flags; - - spin_lock_irqsave(&io_request_lock, flags); - - /* - * Need to do this...we somtimes get two events from the IRTOS - * in a row and that causes lots of problems. - */ - i2o_device_notify_off(d, &i2o_block_handler); - - printk(KERN_INFO "I2O Block Device Deleted\n"); - - for(unit = 0; unit < MAX_I2OB<<4; unit += 16) - { - if(i2ob_dev[unit].i2odev == d) - { - printk(KERN_INFO " /dev/%s: Controller %d Tid %d\n", - d->dev_name, c->unit, d->lct_data.tid); - break; - } - } - if(unit >= MAX_I2OB<<4) - { - printk(KERN_ERR "i2ob_del_device called, but not in dev table!\n"); - spin_unlock_irqrestore(&io_request_lock, flags); - return; - } - - /* - * This will force errors when i2ob_get_queue() is called - * by the kenrel. - */ - i2ob_dev[unit].req_queue = NULL; - for(i = unit; i <= unit+15; i++) - { - i2ob_dev[i].i2odev = NULL; - i2ob_sizes[i] = 0; - i2ob_hardsizes[i] = 0; - i2ob_max_sectors[i] = 0; - i2ob[i].nr_sects = 0; - i2ob_gendisk.part[i].nr_sects = 0; - } - spin_unlock_irqrestore(&io_request_lock, flags); - - /* - * Sync the device...this will force all outstanding I/Os - * to attempt to complete, thus causing error messages. - * We have to do this as the user could immediatelly create - * a new volume that gets assigned the same minor number. - * If there are still outstanding writes to the device, - * that could cause data corruption on the new volume! - * - * The truth is that deleting a volume that you are currently - * accessing will do _bad things_ to your system. This - * handler will keep it from crashing, but must probably - * you'll have to do a 'reboot' to get the system running - * properly. Deleting disks you are using is dumb. - * Umount them first and all will be good! - * - * It's not this driver's job to protect the system from - * dumb user mistakes :) - */ - if(i2ob_dev[unit].refcnt) - fsync_dev(MKDEV(MAJOR_NR,unit)); - - /* - * Decrease usage count for module - */ - while(i2ob_dev[unit].refcnt--) - MOD_DEC_USE_COUNT; - - i2ob_dev[unit].refcnt = 0; - - i2ob_dev[i].tid = 0; - - /* - * Do we need this? - * The media didn't really change...the device is just gone - */ - i2ob_media_change_flag[unit] = 1; - - i2ob_dev_count--; -} - -/* - * Have we seen a media change ? - */ -static int i2ob_media_change(kdev_t dev) -{ - int i=MINOR(dev); - i>>=4; - if(i2ob_media_change_flag[i]) - { - i2ob_media_change_flag[i]=0; - return 1; - } - return 0; -} - -static int i2ob_revalidate(kdev_t dev) -{ - return do_i2ob_revalidate(dev, 0); -} - -/* - * Reboot notifier. This is called by i2o_core when the system - * shuts down. - */ -static void i2ob_reboot_event(void) -{ - int i; - - for(i=0;irefcnt!=0) - { - /* - * Flush the onboard cache - */ - u32 msg[5]; - int *query_done = &dev->done_flag; - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = 60<<16; - - DEBUG("Flushing..."); - i2o_post_wait(dev->controller, msg, 20, 60); - - DEBUG("Unlocking..."); - /* - * Unlock the media - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = -1; - i2o_post_wait(dev->controller, msg, 20, 2); - - DEBUG("Unlocked.\n"); - } - } -} - -static struct block_device_operations i2ob_fops = -{ - open: i2ob_open, - release: i2ob_release, - ioctl: i2ob_ioctl, - check_media_change: i2ob_media_change, - revalidate: i2ob_revalidate, -}; - -static struct gendisk i2ob_gendisk = -{ - MAJOR_NR, - "i2o/hd", - 4, - 1<<4, - i2ob, - i2ob_sizes, - MAX_I2OB, - NULL, - NULL, - &i2ob_fops, -}; - - -/* - * And here should be modules and kernel interface - * (Just smiley confuses emacs :-) - */ - -#ifdef MODULE -#define i2o_block_init init_module -#endif - -int i2o_block_init(void) -{ - int i; - - printk(KERN_INFO "I2O Block Storage OSM v0.9\n"); - printk(KERN_INFO " (c) Copyright 1999-2001 Red Hat Software.\n"); - - /* - * Register the block device interfaces - */ - - if (register_blkdev(MAJOR_NR, "i2o_block", &i2ob_fops)) { - printk(KERN_ERR "Unable to get major number %d for i2o_block\n", - MAJOR_NR); - return -EIO; - } -#ifdef MODULE - printk(KERN_INFO "i2o_block: registered device at major %d\n", MAJOR_NR); -#endif - - /* - * Now fill in the boiler plate - */ - - blksize_size[MAJOR_NR] = i2ob_blksizes; - hardsect_size[MAJOR_NR] = i2ob_hardsizes; - blk_size[MAJOR_NR] = i2ob_sizes; - max_sectors[MAJOR_NR] = i2ob_max_sectors; - blk_dev[MAJOR_NR].queue = i2ob_get_queue; - - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), i2ob_request); - blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR), 0); - - for (i = 0; i < MAX_I2OB << 4; i++) { - i2ob_dev[i].refcnt = 0; - i2ob_dev[i].flags = 0; - i2ob_dev[i].controller = NULL; - i2ob_dev[i].i2odev = NULL; - i2ob_dev[i].tid = 0; - i2ob_dev[i].head = NULL; - i2ob_dev[i].tail = NULL; - i2ob_dev[i].depth = MAX_I2OB_DEPTH; - i2ob_blksizes[i] = 1024; - i2ob_max_sectors[i] = 2; - } - - /* - * Set up the queue - */ - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - i2ob_queues[i] = NULL; - } - - /* - * Timers - */ - - init_timer(&i2ob_timer); - i2ob_timer.function = i2ob_timer_handler; - i2ob_timer.data = 0; - - /* - * Register the OSM handler as we will need this to probe for - * drives, geometry and other goodies. - */ - - if(i2o_install_handler(&i2o_block_handler)<0) - { - unregister_blkdev(MAJOR_NR, "i2o_block"); - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - printk(KERN_ERR "i2o_block: unable to register OSM.\n"); - return -EINVAL; - } - i2ob_context = i2o_block_handler.context; - - /* - * Initialize event handling thread - */ - init_MUTEX_LOCKED(&i2ob_evt_sem); - evt_pid = kernel_thread(i2ob_evt, NULL, CLONE_SIGHAND); - if(evt_pid < 0) - { - printk(KERN_ERR - "i2o_block: Could not initialize event thread. Aborting\n"); - i2o_remove_handler(&i2o_block_handler); - return 0; - } - - /* - * Finally see what is actually plugged in to our controllers - */ - for (i = 0; i < MAX_I2OB; i++) - register_disk(&i2ob_gendisk, MKDEV(MAJOR_NR,i<<4), 1<<4, - &i2ob_fops, 0); - i2ob_probe(); - - /* - * Adding i2ob_gendisk into the gendisk list. - */ - i2ob_gendisk.next = gendisk_head; - gendisk_head = &i2ob_gendisk; - - return 0; -} - -#ifdef MODULE - -EXPORT_NO_SYMBOLS; -MODULE_AUTHOR("Red Hat Software"); -MODULE_DESCRIPTION("I2O Block Device OSM"); - -void cleanup_module(void) -{ - struct gendisk *gdp; - int i; - - if(evt_running) { - printk(KERN_INFO "Killing I2O block threads..."); - i = kill_proc(evt_pid, SIGTERM, 1); - if(!i) { - printk("waiting..."); - } - /* Be sure it died */ - down(&i2ob_thread_dead); - printk("done.\n"); - } - - /* - * Unregister for updates from any devices..otherwise we still - * get them and the core jumps to random memory :O - */ - if(i2ob_dev_count) { - struct i2o_device *d; - for(i = 0; i < MAX_I2OB; i++) - if((d=i2ob_dev[i<<4].i2odev)) { - i2o_device_notify_off(d, &i2o_block_handler); - i2o_event_register(d->controller, d->lct_data.tid, - i2ob_context, i<<4, 0); - } - } - - /* - * We may get further callbacks for ourself. The i2o_core - * code handles this case reasonably sanely. The problem here - * is we shouldn't get them .. but a couple of cards feel - * obliged to tell us stuff we dont care about. - * - * This isnt ideal at all but will do for now. - */ - - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ); - - /* - * Flush the OSM - */ - - i2o_remove_handler(&i2o_block_handler); - - /* - * Return the block device - */ - if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0) - printk("i2o_block: cleanup_module failed\n"); - - /* - * free request queue - */ - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - - /* - * Why isnt register/unregister gendisk in the kernel ??? - */ - - if (gendisk_head == &i2ob_gendisk) { - gendisk_head = i2ob_gendisk.next; - } - else { - for (gdp = gendisk_head; gdp; gdp = gdp->next) - if (gdp->next == &i2ob_gendisk) - { - gdp->next = i2ob_gendisk.next; - break; - } - } -} -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_config.c linux.ac/drivers/i2o/i2o_config.c --- linux.vanilla/drivers/i2o/i2o_config.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/i2o/i2o_config.c Thu Jan 1 01:00:00 1970 @@ -1,971 +0,0 @@ -/* - * I2O Configuration Interface Driver - * - * (C) Copyright 1999 Red Hat Software - * - * Written by Alan Cox, Building Number Three Ltd - * - * Modified 04/20/1999 by Deepak Saxena - * - Added basic ioctl() support - * Modified 06/07/1999 by Deepak Saxena - * - Added software download ioctl (still testing) - * Modified 09/10/1999 by Auvo Häkkinen - * - Changes to i2o_cfg_reply(), ioctl_parms() - * - Added ioct_validate() - * Modified 09/30/1999 by Taneli Vähäkangas - * - Fixed ioctl_swdl() - * Modified 10/04/1999 by Taneli Vähäkangas - * - Changed ioctl_swdl(), implemented ioctl_swul() and ioctl_swdel() - * Modified 11/18/199 by Deepak Saxena - * - Added event managmenet support - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static int i2o_cfg_context = -1; -static void *page_buf; -static spinlock_t i2o_config_lock = SPIN_LOCK_UNLOCKED; -struct wait_queue *i2o_wait_queue; - -#define MODINC(x,y) (x = x++ % y) - -struct i2o_cfg_info -{ - struct file* fp; - struct fasync_struct *fasync; - struct i2o_evt_info event_q[I2O_EVT_Q_LEN]; - u16 q_in; // Queue head index - u16 q_out; // Queue tail index - u16 q_len; // Queue length - u16 q_lost; // Number of lost events - u32 q_id; // Event queue ID...used as tx_context - struct i2o_cfg_info *next; -}; -static struct i2o_cfg_info *open_files = NULL; -static int i2o_cfg_info_id = 0; - -static int ioctl_getiops(unsigned long); -static int ioctl_gethrt(unsigned long); -static int ioctl_getlct(unsigned long); -static int ioctl_parms(unsigned long, unsigned int); -static int ioctl_html(unsigned long); -static int ioctl_swdl(unsigned long); -static int ioctl_swul(unsigned long); -static int ioctl_swdel(unsigned long); -static int ioctl_validate(unsigned long); -static int ioctl_evt_reg(unsigned long, struct file *); -static int ioctl_evt_get(unsigned long, struct file *); -static int cfg_fasync(int, struct file*, int); - -/* - * This is the callback for any message we have posted. The message itself - * will be returned to the message pool when we return from the IRQ - * - * This runs in irq context so be short and sweet. - */ -static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *m) -{ - u32 *msg = (u32 *)m; - - if (msg[0] & MSG_FAIL) { - u32 *preserved_msg = (u32*)(c->mem_offset + msg[7]); - - printk(KERN_ERR "i2o_config: IOP failed to process the msg.\n"); - - /* Release the preserved msg frame by resubmitting it as a NOP */ - - preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; - preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; - preserved_msg[2] = 0; - i2o_post_message(c, msg[7]); - } - - if (msg[4] >> 24) // ReqStatus != SUCCESS - i2o_report_status(KERN_INFO,"i2o_config", msg); - - if(m->function == I2O_CMD_UTIL_EVT_REGISTER) - { - struct i2o_cfg_info *inf; - - for(inf = open_files; inf; inf = inf->next) - if(inf->q_id == msg[3]) - break; - - // - // If this is the case, it means that we're getting - // events for a file descriptor that's been close()'d - // w/o the user unregistering for events first. - // The code currently assumes that the user will - // take care of unregistering for events before closing - // a file. - // - // TODO: - // Should we track event registartion and deregister - // for events when a file is close()'d so this doesn't - // happen? That would get rid of the search through - // the linked list since file->private_data could point - // directly to the i2o_config_info data structure...but - // it would mean having all sorts of tables to track - // what each file is registered for...I think the - // current method is simpler. - DS - // - if(!inf) - return; - - inf->event_q[inf->q_in].id.iop = c->unit; - inf->event_q[inf->q_in].id.tid = m->target_tid; - inf->event_q[inf->q_in].id.evt_mask = msg[4]; - - // - // Data size = msg size - reply header - // - inf->event_q[inf->q_in].data_size = (m->size - 5) * 4; - if(inf->event_q[inf->q_in].data_size) - memcpy(inf->event_q[inf->q_in].evt_data, - (unsigned char *)(msg + 5), - inf->event_q[inf->q_in].data_size); - - spin_lock(&i2o_config_lock); - MODINC(inf->q_in, I2O_EVT_Q_LEN); - if(inf->q_len == I2O_EVT_Q_LEN) - { - MODINC(inf->q_out, I2O_EVT_Q_LEN); - inf->q_lost++; - } - else - { - // Keep I2OEVTGET on another CPU from touching this - inf->q_len++; - } - spin_unlock(&i2o_config_lock); - - -// printk(KERN_INFO "File %p w/id %d has %d events\n", -// inf->fp, inf->q_id, inf->q_len); - - kill_fasync(&inf->fasync, SIGIO, POLL_IN); - } - - return; -} - -/* - * Each of these describes an i2o message handler. They are - * multiplexed by the i2o_core code - */ - -struct i2o_handler cfg_handler= -{ - i2o_cfg_reply, - NULL, - NULL, - NULL, - "Configuration", - 0, - 0xffffffff // All classes -}; - -static long long cfg_llseek(struct file *file, long long offset, int origin) -{ - return -ESPIPE; -} - - -static ssize_t cfg_write(struct file *file, const char *buf, size_t count, loff_t *ppos) -{ - printk(KERN_INFO "i2o_config write not yet supported\n"); - - return 0; -} - - -static ssize_t cfg_read(struct file *file, char *buf, size_t count, loff_t *ptr) -{ - return 0; -} - -/* - * IOCTL Handler - */ -static int cfg_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, - unsigned long arg) -{ - int ret; - - switch(cmd) - { - case I2OGETIOPS: - ret = ioctl_getiops(arg); - break; - - case I2OHRTGET: - ret = ioctl_gethrt(arg); - break; - - case I2OLCTGET: - ret = ioctl_getlct(arg); - break; - - case I2OPARMSET: - ret = ioctl_parms(arg, I2OPARMSET); - break; - - case I2OPARMGET: - ret = ioctl_parms(arg, I2OPARMGET); - break; - - case I2OSWDL: - ret = ioctl_swdl(arg); - break; - - case I2OSWUL: - ret = ioctl_swul(arg); - break; - - case I2OSWDEL: - ret = ioctl_swdel(arg); - break; - - case I2OVALIDATE: - ret = ioctl_validate(arg); - break; - - case I2OHTML: - ret = ioctl_html(arg); - break; - - case I2OEVTREG: - ret = ioctl_evt_reg(arg, fp); - break; - - case I2OEVTGET: - ret = ioctl_evt_get(arg, fp); - break; - - default: - ret = -EINVAL; - } - - return ret; -} - -int ioctl_getiops(unsigned long arg) -{ - u8 *user_iop_table = (u8*)arg; - struct i2o_controller *c = NULL; - int i; - u8 foo[MAX_I2O_CONTROLLERS]; - - if(!access_ok(VERIFY_WRITE, user_iop_table, MAX_I2O_CONTROLLERS)) - return -EFAULT; - - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - c = i2o_find_controller(i); - if(c) - { - foo[i] = 1; - i2o_unlock_controller(c); - } - else - { - foo[i] = 0; - } - } - - __copy_to_user(user_iop_table, foo, MAX_I2O_CONTROLLERS); - return 0; -} - -int ioctl_gethrt(unsigned long arg) -{ - struct i2o_controller *c; - struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg; - struct i2o_cmd_hrtlct kcmd; - i2o_hrt *hrt; - int len; - u32 reslen; - int ret = 0; - - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) - return -EFAULT; - - if(get_user(reslen, kcmd.reslen) < 0) - return -EFAULT; - - if(kcmd.resbuf == NULL) - return -EFAULT; - - c = i2o_find_controller(kcmd.iop); - if(!c) - return -ENXIO; - - hrt = (i2o_hrt *)c->hrt; - - i2o_unlock_controller(c); - - len = 8 + ((hrt->entry_len * hrt->num_entries) << 2); - - /* We did a get user...so assuming mem is ok...is this bad? */ - put_user(len, kcmd.reslen); - if(len > reslen) - ret = -ENOBUFS; - if(copy_to_user(kcmd.resbuf, (void*)hrt, len)) - ret = -EFAULT; - - return ret; -} - -int ioctl_getlct(unsigned long arg) -{ - struct i2o_controller *c; - struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg; - struct i2o_cmd_hrtlct kcmd; - i2o_lct *lct; - int len; - int ret = 0; - u32 reslen; - - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) - return -EFAULT; - - if(get_user(reslen, kcmd.reslen) < 0) - return -EFAULT; - - if(kcmd.resbuf == NULL) - return -EFAULT; - - c = i2o_find_controller(kcmd.iop); - if(!c) - return -ENXIO; - - lct = (i2o_lct *)c->lct; - i2o_unlock_controller(c); - - len = (unsigned int)lct->table_size << 2; - put_user(len, kcmd.reslen); - if(len > reslen) - ret = -ENOBUFS; - else if(copy_to_user(kcmd.resbuf, (void*)lct, len)) - ret = -EFAULT; - - return ret; -} - -static int ioctl_parms(unsigned long arg, unsigned int type) -{ - int ret = 0; - struct i2o_controller *c; - struct i2o_cmd_psetget *cmd = (struct i2o_cmd_psetget*)arg; - struct i2o_cmd_psetget kcmd; - u32 reslen; - u8 *ops; - u8 *res; - int len; - - u32 i2o_cmd = (type == I2OPARMGET ? - I2O_CMD_UTIL_PARAMS_GET : - I2O_CMD_UTIL_PARAMS_SET); - - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_psetget))) - return -EFAULT; - - if(get_user(reslen, kcmd.reslen)) - return -EFAULT; - - c = i2o_find_controller(kcmd.iop); - if(!c) - return -ENXIO; - - ops = (u8*)kmalloc(kcmd.oplen, GFP_KERNEL); - if(!ops) - { - i2o_unlock_controller(c); - return -ENOMEM; - } - - if(copy_from_user(ops, kcmd.opbuf, kcmd.oplen)) - { - i2o_unlock_controller(c); - kfree(ops); - return -EFAULT; - } - - /* - * It's possible to have a _very_ large table - * and that the user asks for all of it at once... - */ - res = (u8*)kmalloc(65536, GFP_KERNEL); - if(!res) - { - i2o_unlock_controller(c); - kfree(ops); - return -ENOMEM; - } - - len = i2o_issue_params(i2o_cmd, c, kcmd.tid, - ops, kcmd.oplen, res, 65536); - i2o_unlock_controller(c); - kfree(ops); - - if (len < 0) { - kfree(res); - return -EAGAIN; - } - - put_user(len, kcmd.reslen); - if(len > reslen) - ret = -ENOBUFS; - else if(copy_to_user(cmd->resbuf, res, len)) - ret = -EFAULT; - - kfree(res); - - return ret; -} - -int ioctl_html(unsigned long arg) -{ - struct i2o_html *cmd = (struct i2o_html*)arg; - struct i2o_html kcmd; - struct i2o_controller *c; - u8 *res = NULL; - void *query = NULL; - int ret = 0; - int token; - u32 len; - u32 reslen; - u32 msg[MSG_FRAME_SIZE/4]; - - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_html))) - { - printk(KERN_INFO "i2o_config: can't copy html cmd\n"); - return -EFAULT; - } - - if(get_user(reslen, kcmd.reslen) < 0) - { - printk(KERN_INFO "i2o_config: can't copy html reslen\n"); - return -EFAULT; - } - - if(!kcmd.resbuf) - { - printk(KERN_INFO "i2o_config: NULL html buffer\n"); - return -EFAULT; - } - - c = i2o_find_controller(kcmd.iop); - if(!c) - return -ENXIO; - - if(kcmd.qlen) /* Check for post data */ - { - query = kmalloc(kcmd.qlen, GFP_KERNEL); - if(!query) - { - i2o_unlock_controller(c); - return -ENOMEM; - } - if(copy_from_user(query, kcmd.qbuf, kcmd.qlen)) - { - i2o_unlock_controller(c); - printk(KERN_INFO "i2o_config: could not get query\n"); - kfree(query); - return -EFAULT; - } - } - - res = kmalloc(65536, GFP_KERNEL); - if(!res) - { - i2o_unlock_controller(c); - kfree(query); - return -ENOMEM; - } - - msg[1] = (I2O_CMD_UTIL_CONFIG_DIALOG << 24)|HOST_TID<<12|kcmd.tid; - msg[2] = i2o_cfg_context; - msg[3] = 0; - msg[4] = kcmd.page; - msg[5] = 0xD0000000|65536; - msg[6] = virt_to_bus(res); - if(!kcmd.qlen) /* Check for post data */ - msg[0] = SEVEN_WORD_MSG_SIZE|SGL_OFFSET_5; - else - { - msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_5; - msg[5] = 0x50000000|65536; - msg[7] = 0xD4000000|(kcmd.qlen); - msg[8] = virt_to_bus(query); - } - /* - Wait for a considerable time till the Controller - does its job before timing out. The controller might - take more time to process this request if there are - many devices connected to it. - */ - token = i2o_post_wait_mem(c, msg, 9*4, 400, query, res); - if(token < 0) - { - printk(KERN_DEBUG "token = %#10x\n", token); - i2o_unlock_controller(c); - - if(token != -ETIMEDOUT) - { - kfree(res); - if(kcmd.qlen) kfree(query); - } - - return token; - } - i2o_unlock_controller(c); - - len = strnlen(res, 65536); - put_user(len, kcmd.reslen); - if(len > reslen) - ret = -ENOMEM; - if(copy_to_user(kcmd.resbuf, res, len)) - ret = -EFAULT; - - kfree(res); - if(kcmd.qlen) - kfree(query); - - return ret; -} - -int ioctl_swdl(unsigned long arg) -{ - struct i2o_sw_xfer kxfer; - struct i2o_sw_xfer *pxfer = (struct i2o_sw_xfer *)arg; - unsigned char maxfrag = 0, curfrag = 1; - unsigned char *buffer; - u32 msg[9]; - unsigned int status = 0, swlen = 0, fragsize = 8192; - struct i2o_controller *c; - - if(copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) - return -EFAULT; - - if(get_user(swlen, kxfer.swlen) < 0) - return -EFAULT; - - if(get_user(maxfrag, kxfer.maxfrag) < 0) - return -EFAULT; - - if(get_user(curfrag, kxfer.curfrag) < 0) - return -EFAULT; - - if(curfrag==maxfrag) fragsize = swlen-(maxfrag-1)*8192; - - if(!kxfer.buf || !access_ok(VERIFY_READ, kxfer.buf, fragsize)) - return -EFAULT; - - c = i2o_find_controller(kxfer.iop); - if(!c) - return -ENXIO; - - buffer=kmalloc(fragsize, GFP_KERNEL); - if (buffer==NULL) - { - i2o_unlock_controller(c); - return -ENOMEM; - } - __copy_from_user(buffer, kxfer.buf, fragsize); - - msg[0]= NINE_WORD_MSG_SIZE | SGL_OFFSET_7; - msg[1]= I2O_CMD_SW_DOWNLOAD<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2]= (u32)cfg_handler.context; - msg[3]= 0; - msg[4]= (((u32)kxfer.flags)<<24) | (((u32)kxfer.sw_type)<<16) | - (((u32)maxfrag)<<8) | (((u32)curfrag)); - msg[5]= swlen; - msg[6]= kxfer.sw_id; - msg[7]= (0xD0000000 | fragsize); - msg[8]= virt_to_bus(buffer); - -// printk("i2o_config: swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); - status = i2o_post_wait_mem(c, msg, sizeof(msg), 60, buffer, NULL); - - i2o_unlock_controller(c); - if(status != -ETIMEDOUT) - kfree(buffer); - - if (status != I2O_POST_WAIT_OK) - { - // it fails if you try and send frags out of order - // and for some yet unknown reasons too - printk(KERN_INFO "i2o_config: swdl failed, DetailedStatus = %d\n", status); - return status; - } - - return 0; -} - -int ioctl_swul(unsigned long arg) -{ - struct i2o_sw_xfer kxfer; - struct i2o_sw_xfer *pxfer = (struct i2o_sw_xfer *)arg; - unsigned char maxfrag = 0, curfrag = 1; - unsigned char *buffer; - u32 msg[9]; - unsigned int status = 0, swlen = 0, fragsize = 8192; - struct i2o_controller *c; - - if(copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) - return -EFAULT; - - if(get_user(swlen, kxfer.swlen) < 0) - return -EFAULT; - - if(get_user(maxfrag, kxfer.maxfrag) < 0) - return -EFAULT; - - if(get_user(curfrag, kxfer.curfrag) < 0) - return -EFAULT; - - if(curfrag==maxfrag) fragsize = swlen-(maxfrag-1)*8192; - - if(!kxfer.buf || !access_ok(VERIFY_WRITE, kxfer.buf, fragsize)) - return -EFAULT; - - c = i2o_find_controller(kxfer.iop); - if(!c) - return -ENXIO; - - buffer=kmalloc(fragsize, GFP_KERNEL); - if (buffer==NULL) - { - i2o_unlock_controller(c); - return -ENOMEM; - } - - msg[0]= NINE_WORD_MSG_SIZE | SGL_OFFSET_7; - msg[1]= I2O_CMD_SW_UPLOAD<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2]= (u32)cfg_handler.context; - msg[3]= 0; - msg[4]= (u32)kxfer.flags<<24|(u32)kxfer.sw_type<<16|(u32)maxfrag<<8|(u32)curfrag; - msg[5]= swlen; - msg[6]= kxfer.sw_id; - msg[7]= (0xD0000000 | fragsize); - msg[8]= virt_to_bus(buffer); - -// printk("i2o_config: swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); - status = i2o_post_wait_mem(c, msg, sizeof(msg), 60, buffer, NULL); - i2o_unlock_controller(c); - - if (status != I2O_POST_WAIT_OK) - { - if(status != -ETIMEDOUT) - kfree(buffer); - printk(KERN_INFO "i2o_config: swul failed, DetailedStatus = %d\n", status); - return status; - } - - __copy_to_user(kxfer.buf, buffer, fragsize); - kfree(buffer); - - return 0; -} - -int ioctl_swdel(unsigned long arg) -{ - struct i2o_controller *c; - struct i2o_sw_xfer kxfer, *pxfer = (struct i2o_sw_xfer *)arg; - u32 msg[7]; - unsigned int swlen; - int token; - - if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) - return -EFAULT; - - if (get_user(swlen, kxfer.swlen) < 0) - return -EFAULT; - - c = i2o_find_controller(kxfer.iop); - if (!c) - return -ENXIO; - - msg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = I2O_CMD_SW_REMOVE<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2] = (u32)i2o_cfg_context; - msg[3] = 0; - msg[4] = (u32)kxfer.flags<<24 | (u32)kxfer.sw_type<<16; - msg[5] = swlen; - msg[6] = kxfer.sw_id; - - token = i2o_post_wait(c, msg, sizeof(msg), 10); - i2o_unlock_controller(c); - - if (token != I2O_POST_WAIT_OK) - { - printk(KERN_INFO "i2o_config: swdel failed, DetailedStatus = %d\n", token); - return -ETIMEDOUT; - } - - return 0; -} - -int ioctl_validate(unsigned long arg) -{ - int token; - int iop = (int)arg; - u32 msg[4]; - struct i2o_controller *c; - - c=i2o_find_controller(iop); - if (!c) - return -ENXIO; - - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_CONFIG_VALIDATE<<24 | HOST_TID<<12 | iop; - msg[2] = (u32)i2o_cfg_context; - msg[3] = 0; - - token = i2o_post_wait(c, msg, sizeof(msg), 10); - i2o_unlock_controller(c); - - if (token != I2O_POST_WAIT_OK) - { - printk(KERN_INFO "Can't validate configuration, ErrorStatus = %d\n", - token); - return -ETIMEDOUT; - } - - return 0; -} - -static int ioctl_evt_reg(unsigned long arg, struct file *fp) -{ - u32 msg[5]; - struct i2o_evt_id *pdesc = (struct i2o_evt_id *)arg; - struct i2o_evt_id kdesc; - struct i2o_controller *iop; - struct i2o_device *d; - - if (copy_from_user(&kdesc, pdesc, sizeof(struct i2o_evt_id))) - return -EFAULT; - - /* IOP exists? */ - iop = i2o_find_controller(kdesc.iop); - if(!iop) - return -ENXIO; - i2o_unlock_controller(iop); - - /* Device exists? */ - for(d = iop->devices; d; d = d->next) - if(d->lct_data.tid == kdesc.tid) - break; - - if(!d) - return -ENODEV; - - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_UTIL_EVT_REGISTER<<24 | HOST_TID<<12 | kdesc.tid; - msg[2] = (u32)i2o_cfg_context; - msg[3] = (u32)fp->private_data; - msg[4] = kdesc.evt_mask; - - i2o_post_this(iop, msg, 20); - - return 0; -} - -static int ioctl_evt_get(unsigned long arg, struct file *fp) -{ - u32 id = (u32)fp->private_data; - struct i2o_cfg_info *p = NULL; - struct i2o_evt_get *uget = (struct i2o_evt_get*)arg; - struct i2o_evt_get kget; - unsigned int flags; - - for(p = open_files; p; p = p->next) - if(p->q_id == id) - break; - - if(!p->q_len) - { - return -ENOENT; - return 0; - } - - memcpy(&kget.info, &p->event_q[p->q_out], sizeof(struct i2o_evt_info)); - MODINC(p->q_out, I2O_EVT_Q_LEN); - spin_lock_irqsave(&i2o_config_lock, flags); - p->q_len--; - kget.pending = p->q_len; - kget.lost = p->q_lost; - spin_unlock_irqrestore(&i2o_config_lock, flags); - - if(copy_to_user(uget, &kget, sizeof(struct i2o_evt_get))) - return -EFAULT; - return 0; -} - -static int cfg_open(struct inode *inode, struct file *file) -{ - struct i2o_cfg_info *tmp = - (struct i2o_cfg_info *)kmalloc(sizeof(struct i2o_cfg_info), GFP_KERNEL); - unsigned int flags; - - if(!tmp) - return -ENOMEM; - - file->private_data = (void*)(i2o_cfg_info_id++); - tmp->fp = file; - tmp->fasync = NULL; - tmp->q_id = (u32)file->private_data; - tmp->q_len = 0; - tmp->q_in = 0; - tmp->q_out = 0; - tmp->q_lost = 0; - tmp->next = open_files; - - spin_lock_irqsave(&i2o_config_lock, flags); - open_files = tmp; - spin_unlock_irqrestore(&i2o_config_lock, flags); - - return 0; -} - -static int cfg_release(struct inode *inode, struct file *file) -{ - u32 id = (u32)file->private_data; - struct i2o_cfg_info *p1, *p2; - unsigned int flags; - - lock_kernel(); - p1 = p2 = NULL; - - spin_lock_irqsave(&i2o_config_lock, flags); - for(p1 = open_files; p1; ) - { - if(p1->q_id == id) - { - - if(p1->fasync) - cfg_fasync(-1, file, 0); - if(p2) - p2->next = p1->next; - else - open_files = p1->next; - - kfree(p1); - break; - } - p2 = p1; - p1 = p1->next; - } - spin_unlock_irqrestore(&i2o_config_lock, flags); - unlock_kernel(); - - return 0; -} - -static int cfg_fasync(int fd, struct file *fp, int on) -{ - u32 id = (u32)fp->private_data; - struct i2o_cfg_info *p; - - for(p = open_files; p; p = p->next) - if(p->q_id == id) - break; - - if(!p) - return -EBADF; - - return fasync_helper(fd, fp, on, &p->fasync); -} - -static struct file_operations config_fops = -{ - owner: THIS_MODULE, - llseek: cfg_llseek, - read: cfg_read, - write: cfg_write, - ioctl: cfg_ioctl, - open: cfg_open, - release: cfg_release, - fasync: cfg_fasync, -}; - -static struct miscdevice i2o_miscdev = { - I2O_MINOR, - "i2octl", - &config_fops -}; - -#ifdef MODULE -int init_module(void) -#else -int __init i2o_config_init(void) -#endif -{ - printk(KERN_INFO "I2O configuration manager v 0.04.\n"); - printk(KERN_INFO " (C) Copyright 1999 Red Hat Software\n"); - - if((page_buf = kmalloc(4096, GFP_KERNEL))==NULL) - { - printk(KERN_ERR "i2o_config: no memory for page buffer.\n"); - return -ENOBUFS; - } - if(misc_register(&i2o_miscdev)==-1) - { - printk(KERN_ERR "i2o_config: can't register device.\n"); - kfree(page_buf); - return -EBUSY; - } - /* - * Install our handler - */ - if(i2o_install_handler(&cfg_handler)<0) - { - kfree(page_buf); - printk(KERN_ERR "i2o_config: handler register failed.\n"); - misc_deregister(&i2o_miscdev); - return -EBUSY; - } - /* - * The low 16bits of the transaction context must match this - * for everything we post. Otherwise someone else gets our mail - */ - i2o_cfg_context = cfg_handler.context; - return 0; -} - -#ifdef MODULE - -void cleanup_module(void) -{ - misc_deregister(&i2o_miscdev); - - if(page_buf) - kfree(page_buf); - if(i2o_cfg_context != -1) - i2o_remove_handler(&cfg_handler); -} - -EXPORT_NO_SYMBOLS; -MODULE_AUTHOR("Red Hat Software"); -MODULE_DESCRIPTION("I2O Configuration"); - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_core.c linux.ac/drivers/i2o/i2o_core.c --- linux.vanilla/drivers/i2o/i2o_core.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/i2o/i2o_core.c Thu Jan 1 01:00:00 1970 @@ -1,3503 +0,0 @@ -/* - * Core I2O structure management - * - * (C) Copyright 1999 Red Hat Software - * - * Written by Alan Cox, Building Number Three Ltd - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * A lot of the I2O message side code from this is taken from the - * Red Creek RCPCI45 adapter driver by Red Creek Communications - * - * Fixes by: - * Philipp Rumpf - * Juha Sievänen - * Auvo Häkkinen - * Deepak Saxena - * Boji T Kannanthanam - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "i2o_lan.h" - -//#define DRIVERDEBUG - -#ifdef DRIVERDEBUG -#define dprintk(s, args...) printk(s, ## args) -#else -#define dprintk(s, args...) -#endif - -/* OSM table */ -static struct i2o_handler *i2o_handlers[MAX_I2O_MODULES] = {NULL}; - -/* Controller list */ -static struct i2o_controller *i2o_controllers[MAX_I2O_CONTROLLERS] = {NULL}; -struct i2o_controller *i2o_controller_chain = NULL; -int i2o_num_controllers = 0; - -/* Initiator Context for Core message */ -static int core_context = 0; - -/* Initialization && shutdown functions */ -static void i2o_sys_init(void); -static void i2o_sys_shutdown(void); -static int i2o_reset_controller(struct i2o_controller *); -static int i2o_reboot_event(struct notifier_block *, unsigned long , void *); -static int i2o_online_controller(struct i2o_controller *); -static int i2o_init_outbound_q(struct i2o_controller *); -static int i2o_post_outbound_messages(struct i2o_controller *); - -/* Reply handler */ -static void i2o_core_reply(struct i2o_handler *, struct i2o_controller *, - struct i2o_message *); - -/* Various helper functions */ -static int i2o_lct_get(struct i2o_controller *); -static int i2o_lct_notify(struct i2o_controller *); -static int i2o_hrt_get(struct i2o_controller *); - -static int i2o_build_sys_table(void); -static int i2o_systab_send(struct i2o_controller *c); - -/* I2O core event handler */ -static int i2o_core_evt(void *); -static int evt_pid; -static int evt_running; - -/* Dynamic LCT update handler */ -static int i2o_dyn_lct(void *); - -void i2o_report_controller_unit(struct i2o_controller *, struct i2o_device *); - -/* - * I2O System Table. Contains information about - * all the IOPs in the system. Used to inform IOPs - * about each other's existence. - * - * sys_tbl_ver is the CurrentChangeIndicator that is - * used by IOPs to track changes. - */ -static struct i2o_sys_tbl *sys_tbl = NULL; -static int sys_tbl_ind = 0; -static int sys_tbl_len = 0; - -/* - * This spin lock is used to keep a device from being - * added and deleted concurrently across CPUs or interrupts. - * This can occur when a user creates a device and immediatelly - * deletes it before the new_dev_notify() handler is called. - */ -static spinlock_t i2o_dev_lock = SPIN_LOCK_UNLOCKED; - -#ifdef MODULE -/* - * Function table to send to bus specific layers - * See for explanation of this - */ -static struct i2o_core_func_table i2o_core_functions = -{ - i2o_install_controller, - i2o_activate_controller, - i2o_find_controller, - i2o_unlock_controller, - i2o_run_queue, - i2o_delete_controller -}; - -#ifdef CONFIG_I2O_PCI_MODULE -extern int i2o_pci_core_attach(struct i2o_core_func_table *); -extern void i2o_pci_core_detach(void); -#endif /* CONFIG_I2O_PCI_MODULE */ - -#endif /* MODULE */ - -/* - * Structures and definitions for synchronous message posting. - * See i2o_post_wait() for description. - */ -struct i2o_post_wait_data -{ - int *status; /* Pointer to status block on caller stack */ - int *complete; /* Pointer to completion flag on caller stack */ - u32 id; /* Unique identifier */ - wait_queue_head_t *wq; /* Wake up for caller (NULL for dead) */ - struct i2o_post_wait_data *next; /* Chain */ - void *mem[2]; /* Memory blocks to recover on failure path */ -}; -static struct i2o_post_wait_data *post_wait_queue = NULL; -static u32 post_wait_id = 0; // Unique ID for each post_wait -static spinlock_t post_wait_lock = SPIN_LOCK_UNLOCKED; -static void i2o_post_wait_complete(u32, int); - -/* OSM descriptor handler */ -static struct i2o_handler i2o_core_handler = -{ - (void *)i2o_core_reply, - NULL, - NULL, - NULL, - "I2O core layer", - 0, - I2O_CLASS_EXECUTIVE -}; - -/* - * Used when queueing a reply to be handled later - */ - -struct reply_info -{ - struct i2o_controller *iop; - u32 msg[MSG_FRAME_SIZE]; -}; -static struct reply_info evt_reply; -static struct reply_info events[I2O_EVT_Q_LEN]; -static int evt_in = 0; -static int evt_out = 0; -static int evt_q_len = 0; -#define MODINC(x,y) ((x) = ((x) + 1) % (y)) - -/* - * I2O configuration spinlock. This isnt a big deal for contention - * so we have one only - */ - -static DECLARE_MUTEX(i2o_configuration_lock); - -/* - * Event spinlock. Used to keep event queue sane and from - * handling multiple events simultaneously. - */ -static spinlock_t i2o_evt_lock = SPIN_LOCK_UNLOCKED; - -/* - * Semaphore used to synchronize event handling thread with - * interrupt handler. - */ - -static DECLARE_MUTEX(evt_sem); -static DECLARE_MUTEX_LOCKED(evt_dead); -DECLARE_WAIT_QUEUE_HEAD(evt_wait); - -static struct notifier_block i2o_reboot_notifier = -{ - i2o_reboot_event, - NULL, - 0 -}; - -/* - * Config options - */ - -static int verbose = 0; -MODULE_PARM(verbose, "i"); - -/* - * I2O Core reply handler - */ -static void i2o_core_reply(struct i2o_handler *h, struct i2o_controller *c, - struct i2o_message *m) -{ - u32 *msg=(u32 *)m; - u32 status; - u32 context = msg[2]; - - if (msg[0] & MSG_FAIL) // Fail bit is set - { - u32 *preserved_msg = (u32*)(c->mem_offset + msg[7]); - - i2o_report_status(KERN_INFO, "i2o_core", msg); - i2o_dump_message(preserved_msg); - - /* If the failed request needs special treatment, - * it should be done here. */ - - /* Release the preserved msg by resubmitting it as a NOP */ - - preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; - preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; - preserved_msg[2] = 0; - i2o_post_message(c, msg[7]); - - /* If reply to i2o_post_wait failed, return causes a timeout */ - - return; - } - -#ifdef DRIVERDEBUG - i2o_report_status(KERN_INFO, "i2o_core", msg); -#endif - - if(msg[2]&0x80000000) // Post wait message - { - if (msg[4] >> 24) - status = (msg[4] & 0xFFFF); - else - status = I2O_POST_WAIT_OK; - - i2o_post_wait_complete(context, status); - return; - } - - if(m->function == I2O_CMD_UTIL_EVT_REGISTER) - { - memcpy(events[evt_in].msg, msg, (msg[0]>>16)<<2); - events[evt_in].iop = c; - - spin_lock(&i2o_evt_lock); - MODINC(evt_in, I2O_EVT_Q_LEN); - if(evt_q_len == I2O_EVT_Q_LEN) - MODINC(evt_out, I2O_EVT_Q_LEN); - else - evt_q_len++; - spin_unlock(&i2o_evt_lock); - - up(&evt_sem); - wake_up_interruptible(&evt_wait); - return; - } - - if(m->function == I2O_CMD_LCT_NOTIFY) - { - up(&c->lct_sem); - return; - } - - /* - * If this happens, we want to dump the message to the syslog so - * it can be sent back to the card manufacturer by the end user - * to aid in debugging. - * - */ - printk(KERN_WARNING "%s: Unsolicited message reply sent to core!" - "Message dumped to syslog\n", - c->name); - i2o_dump_message(msg); - - return; -} - -/** - * i2o_install_handler - install a message handler - * @h: Handler structure - * - * Install an I2O handler - these handle the asynchronous messaging - * from the card once it has initialised. If the table of handlers is - * full then -ENOSPC is returned. On a success 0 is returned and the - * context field is set by the function. The structure is part of the - * system from this time onwards. It must not be freed until it has - * been uninstalled - */ - -int i2o_install_handler(struct i2o_handler *h) -{ - int i; - down(&i2o_configuration_lock); - for(i=0;icontext = i; - i2o_handlers[i]=h; - up(&i2o_configuration_lock); - return 0; - } - } - up(&i2o_configuration_lock); - return -ENOSPC; -} - -/** - * i2o_remove_handler - remove an i2o message handler - * @h: handler - * - * Remove a message handler previously installed with i2o_install_handler. - * After this function returns the handler object can be freed or re-used - */ - -int i2o_remove_handler(struct i2o_handler *h) -{ - i2o_handlers[h->context]=NULL; - return 0; -} - - -/* - * Each I2O controller has a chain of devices on it. - * Each device has a pointer to it's LCT entry to be used - * for fun purposes. - */ - -/** - * i2o_install_device - attach a device to a controller - * @c: controller - * @d: device - * - * Add a new device to an i2o controller. This can be called from - * non interrupt contexts only. It adds the device and marks it as - * unclaimed. The device memory becomes part of the kernel and must - * be uninstalled before being freed or reused. Zero is returned - * on success. - */ - -int i2o_install_device(struct i2o_controller *c, struct i2o_device *d) -{ - int i; - - down(&i2o_configuration_lock); - d->controller=c; - d->owner=NULL; - d->next=c->devices; - d->prev=NULL; - if (c->devices != NULL) - c->devices->prev=d; - c->devices=d; - *d->dev_name = 0; - - for(i = 0; i < I2O_MAX_MANAGERS; i++) - d->managers[i] = NULL; - - up(&i2o_configuration_lock); - return 0; -} - -/* we need this version to call out of i2o_delete_controller */ - -int __i2o_delete_device(struct i2o_device *d) -{ - struct i2o_device **p; - int i; - - p=&(d->controller->devices); - - /* - * Hey we have a driver! - * Check to see if the driver wants us to notify it of - * device deletion. If it doesn't we assume that it - * is unsafe to delete a device with an owner and - * fail. - */ - if(d->owner) - { - if(d->owner->dev_del_notify) - { - dprintk(KERN_INFO "Device has owner, notifying\n"); - d->owner->dev_del_notify(d->controller, d); - if(d->owner) - { - printk(KERN_WARNING - "Driver \"%s\" did not release device!\n", d->owner->name); - return -EBUSY; - } - } - else - return -EBUSY; - } - - /* - * Tell any other users who are talking to this device - * that it's going away. We assume that everything works. - */ - for(i=0; i < I2O_MAX_MANAGERS; i++) - { - if(d->managers[i] && d->managers[i]->dev_del_notify) - d->managers[i]->dev_del_notify(d->controller, d); - } - - while(*p!=NULL) - { - if(*p==d) - { - /* - * Destroy - */ - *p=d->next; - kfree(d); - return 0; - } - p=&((*p)->next); - } - printk(KERN_ERR "i2o_delete_device: passed invalid device.\n"); - return -EINVAL; -} - -/** - * i2o_delete_device - remove an i2o device - * @d: device to remove - * - * This function unhooks a device from a controller. The device - * will not be unhooked if it has an owner who does not wish to free - * it, or if the owner lacks a dev_del_notify function. In that case - * -EBUSY is returned. On success 0 is returned. Other errors cause - * negative errno values to be returned - */ - -int i2o_delete_device(struct i2o_device *d) -{ - int ret; - - down(&i2o_configuration_lock); - - /* - * Seek, locate - */ - - ret = __i2o_delete_device(d); - - up(&i2o_configuration_lock); - - return ret; -} - -/** - * i2o_install_controller - attach a controller - * @c: controller - * - * Add a new controller to the i2o layer. This can be called from - * non interrupt contexts only. It adds the controller and marks it as - * unused with no devices. If the tables are full or memory allocations - * fail then a negative errno code is returned. On success zero is - * returned and the controller is bound to the system. The structure - * must not be freed or reused until being uninstalled. - */ - -int i2o_install_controller(struct i2o_controller *c) -{ - int i; - down(&i2o_configuration_lock); - for(i=0;idlct = (i2o_lct*)kmalloc(8192, GFP_KERNEL); - if(c->dlct==NULL) - { - up(&i2o_configuration_lock); - return -ENOMEM; - } - i2o_controllers[i]=c; - c->devices = NULL; - c->next=i2o_controller_chain; - i2o_controller_chain=c; - c->unit = i; - c->page_frame = NULL; - c->hrt = NULL; - c->lct = NULL; - c->status_block = NULL; - sprintf(c->name, "i2o/iop%d", i); - i2o_num_controllers++; - init_MUTEX_LOCKED(&c->lct_sem); - up(&i2o_configuration_lock); - return 0; - } - } - printk(KERN_ERR "No free i2o controller slots.\n"); - up(&i2o_configuration_lock); - return -EBUSY; -} - -/** - * i2o_delete_controller - delete a controller - * @c: controller - * - * Remove an i2o controller from the system. If the controller or its - * devices are busy then -EBUSY is returned. On a failure a negative - * errno code is returned. On success zero is returned. - */ - -int i2o_delete_controller(struct i2o_controller *c) -{ - struct i2o_controller **p; - int users; - char name[16]; - int stat; - - dprintk(KERN_INFO "Deleting controller %s\n", c->name); - - /* - * Clear event registration as this can cause weird behavior - */ - if(c->status_block->iop_state == ADAPTER_STATE_OPERATIONAL) - i2o_event_register(c, core_context, 0, 0, 0); - - down(&i2o_configuration_lock); - if((users=atomic_read(&c->users))) - { - dprintk(KERN_INFO "I2O: %d users for controller %s\n", users, - c->name); - up(&i2o_configuration_lock); - return -EBUSY; - } - while(c->devices) - { - if(__i2o_delete_device(c->devices)<0) - { - /* Shouldnt happen */ - c->bus_disable(c); - up(&i2o_configuration_lock); - return -EBUSY; - } - } - - /* - * If this is shutdown time, the thread's already been killed - */ - if(c->lct_running) { - stat = kill_proc(c->lct_pid, SIGTERM, 1); - if(!stat) { - int count = 10 * 100; - while(c->lct_running && --count) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - } - - if(!count) - printk(KERN_ERR - "%s: LCT thread still running!\n", - c->name); - } - } - - p=&i2o_controller_chain; - - while(*p) - { - if(*p==c) - { - /* Ask the IOP to switch to RESET state */ - i2o_reset_controller(c); - - /* Release IRQ */ - c->destructor(c); - - *p=c->next; - up(&i2o_configuration_lock); - - if(c->page_frame) - kfree(c->page_frame); - if(c->hrt) - kfree(c->hrt); - if(c->lct) - kfree(c->lct); - if(c->status_block) - kfree(c->status_block); - if(c->dlct) - kfree(c->dlct); - - i2o_controllers[c->unit]=NULL; - memcpy(name, c->name, strlen(c->name)+1); - kfree(c); - dprintk(KERN_INFO "%s: Deleted from controller chain.\n", name); - - i2o_num_controllers--; - return 0; - } - p=&((*p)->next); - } - up(&i2o_configuration_lock); - printk(KERN_ERR "i2o_delete_controller: bad pointer!\n"); - return -ENOENT; -} - -/** - * i2o_unlock_controller - unlock a controller - * @c: controller to unlock - * - * Take a lock on an i2o controller. This prevents it being deleted. - * i2o controllers are not refcounted so a deletion of an in use device - * will fail, not take affect on the last dereference. - */ - -void i2o_unlock_controller(struct i2o_controller *c) -{ - atomic_dec(&c->users); -} - -/** - * i2o_find_controller - return a locked controller - * @n: controller number - * - * Returns a pointer to the controller object. The controller is locked - * on return. NULL is returned if the controller is not found. - */ - -struct i2o_controller *i2o_find_controller(int n) -{ - struct i2o_controller *c; - - if(n<0 || n>=MAX_I2O_CONTROLLERS) - return NULL; - - down(&i2o_configuration_lock); - c=i2o_controllers[n]; - if(c!=NULL) - atomic_inc(&c->users); - up(&i2o_configuration_lock); - return c; -} - -/** - * i2o_issue_claim - claim or release a device - * @cmd: command - * @c: controller to claim for - * @tid: i2o task id - * @type: type of claim - * - * Issue I2O UTIL_CLAIM and UTIL_RELEASE messages. The message to be sent - * is set by cmd. The tid is the task id of the object to claim and the - * type is the claim type (see the i2o standard) - * - * Zero is returned on success. - */ - -static int i2o_issue_claim(u32 cmd, struct i2o_controller *c, int tid, u32 type) -{ - u32 msg[5]; - - msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = cmd << 24 | HOST_TID<<12 | tid; - msg[3] = 0; - msg[4] = type; - - return i2o_post_wait(c, msg, sizeof(msg), 60); -} - -/* - * i2o_claim_device - claim a device for use by an OSM - * @d: device to claim - * @h: handler for this device - * - * Do the leg work to assign a device to a given OSM on Linux. The - * kernel updates the internal handler data for the device and then - * performs an I2O claim for the device, attempting to claim the - * device as primary. If the attempt fails a negative errno code - * is returned. On success zero is returned. - */ - -int i2o_claim_device(struct i2o_device *d, struct i2o_handler *h) -{ - down(&i2o_configuration_lock); - if (d->owner) { - printk(KERN_INFO "Device claim called, but dev already owned by %s!", - h->name); - up(&i2o_configuration_lock); - return -EBUSY; - } - d->owner=h; - - if(i2o_issue_claim(I2O_CMD_UTIL_CLAIM ,d->controller,d->lct_data.tid, - I2O_CLAIM_PRIMARY)) - { - d->owner = NULL; - return -EBUSY; - } - up(&i2o_configuration_lock); - return 0; -} - -/** - * i2o_release_device - release a device that the OSM is using - * @d: device to claim - * @h: handler for this device - * - * Drop a claim by an OSM on a given I2O device. The handler is cleared - * and 0 is returned on success. - * - * AC - some devices seem to want to refuse an unclaim until they have - * finished internal processing. It makes sense since you don't want a - * new device to go reconfiguring the entire system until you are done. - * Thus we are prepared to wait briefly. - */ - -int i2o_release_device(struct i2o_device *d, struct i2o_handler *h) -{ - int err = 0; - int tries; - - down(&i2o_configuration_lock); - if (d->owner != h) { - printk(KERN_INFO "Claim release called, but not owned by %s!\n", - h->name); - up(&i2o_configuration_lock); - return -ENOENT; - } - - for(tries=0;tries<10;tries++) - { - d->owner = NULL; - - /* - * If the controller takes a nonblocking approach to - * releases we have to sleep/poll for a few times. - */ - - if((err=i2o_issue_claim(I2O_CMD_UTIL_RELEASE, d->controller, d->lct_data.tid, I2O_CLAIM_PRIMARY)) ) - { - err = -ENXIO; - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ); - } - else - { - err=0; - break; - } - } - up(&i2o_configuration_lock); - return err; -} - -/** - * i2o_device_notify_on - Enable deletion notifiers - * @d: device for notification - * @h: handler to install - * - * Called by OSMs to let the core know that they want to be - * notified if the given device is deleted from the system. - */ - -int i2o_device_notify_on(struct i2o_device *d, struct i2o_handler *h) -{ - int i; - - if(d->num_managers == I2O_MAX_MANAGERS) - return -ENOSPC; - - for(i = 0; i < I2O_MAX_MANAGERS; i++) - { - if(!d->managers[i]) - { - d->managers[i] = h; - break; - } - } - - d->num_managers++; - - return 0; -} - -/** - * i2o_device_notify_off - Remove deletion notifiers - * @d: device for notification - * @h: handler to remove - * - * Called by OSMs to let the core know that they no longer - * are interested in the fate of the given device. - */ -int i2o_device_notify_off(struct i2o_device *d, struct i2o_handler *h) -{ - int i; - - for(i=0; i < I2O_MAX_MANAGERS; i++) - { - if(d->managers[i] == h) - { - d->managers[i] = NULL; - d->num_managers--; - return 0; - } - } - - return -ENOENT; -} - -/** - * i2o_event_register - register interest in an event - * @c: Controller to register interest with - * @tid: I2O task id - * @init_context: initiator context to use with this notifier - * @tr_context: transaction context to use with this notifier - * @evt_mask: mask of events - * - * Create and posts an event registration message to the task. No reply - * is waited for, or expected. Errors in posting will be reported. - */ - -int i2o_event_register(struct i2o_controller *c, u32 tid, - u32 init_context, u32 tr_context, u32 evt_mask) -{ - u32 msg[5]; // Not performance critical, so we just - // i2o_post_this it instead of building it - // in IOP memory - - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_UTIL_EVT_REGISTER<<24 | HOST_TID<<12 | tid; - msg[2] = init_context; - msg[3] = tr_context; - msg[4] = evt_mask; - - return i2o_post_this(c, msg, sizeof(msg)); -} - -/* - * i2o_event_ack - acknowledge an event - * @c: controller - * @msg: pointer to the UTIL_EVENT_REGISTER reply we received - * - * We just take a pointer to the original UTIL_EVENT_REGISTER reply - * message and change the function code since that's what spec - * describes an EventAck message looking like. - */ - -int i2o_event_ack(struct i2o_controller *c, u32 *msg) -{ - struct i2o_message *m = (struct i2o_message *)msg; - - m->function = I2O_CMD_UTIL_EVT_ACK; - - return i2o_post_wait(c, msg, m->size * 4, 2); -} - -/* - * Core event handler. Runs as a separate thread and is woken - * up whenever there is an Executive class event. - */ -static int i2o_core_evt(void *reply_data) -{ - struct reply_info *reply = (struct reply_info *) reply_data; - u32 *msg = reply->msg; - struct i2o_controller *c = NULL; - int flags; - - lock_kernel(); - daemonize(); - unlock_kernel(); - - strcpy(current->comm, "i2oevtd"); - evt_running = 1; - - while(1) - { - if(down_interruptible(&evt_sem)) - { - dprintk(KERN_INFO "I2O event thread dead\n"); - printk("exiting..."); - evt_running = 0; - up_and_exit(&evt_dead, 0); - } - - /* - * Copy the data out of the queue so that we don't have to lock - * around the whole function and just around the qlen update - */ - spin_lock_irqsave(&i2o_evt_lock, flags); - memcpy(reply, &events[evt_out], sizeof(struct reply_info)); - MODINC(evt_out, I2O_EVT_Q_LEN); - evt_q_len--; - spin_unlock_irqrestore(&i2o_evt_lock, flags); - - c = reply->iop; - dprintk(KERN_INFO "I2O IRTOS EVENT: iop%d, event %#10x\n", c->unit, msg[4]); - - /* - * We do not attempt to delete/quiesce/etc. the controller if - * some sort of error indidication occurs. We may want to do - * so in the future, but for now we just let the user deal with - * it. One reason for this is that what to do with an error - * or when to send what ćrror is not really agreed on, so - * we get errors that may not be fatal but just look like they - * are...so let the user deal with it. - */ - switch(msg[4]) - { - case I2O_EVT_IND_EXEC_RESOURCE_LIMITS: - printk(KERN_ERR "%s: Out of resources\n", c->name); - break; - - case I2O_EVT_IND_EXEC_POWER_FAIL: - printk(KERN_ERR "%s: Power failure\n", c->name); - break; - - case I2O_EVT_IND_EXEC_HW_FAIL: - { - char *fail[] = - { - "Unknown Error", - "Power Lost", - "Code Violation", - "Parity Error", - "Code Execution Exception", - "Watchdog Timer Expired" - }; - - if(msg[5] <= 6) - printk(KERN_ERR "%s: Hardware Failure: %s\n", - c->name, fail[msg[5]]); - else - printk(KERN_ERR "%s: Unknown Hardware Failure\n", c->name); - - break; - } - - /* - * New device created - * - Create a new i2o_device entry - * - Inform all interested drivers about this device's existence - */ - case I2O_EVT_IND_EXEC_NEW_LCT_ENTRY: - { - struct i2o_device *d = (struct i2o_device *) - kmalloc(sizeof(struct i2o_device), GFP_KERNEL); - int i; - - if (d == NULL) { - printk(KERN_EMERG "i2oevtd: out of memory\n"); - break; - } - memcpy(&d->lct_data, &msg[5], sizeof(i2o_lct_entry)); - - d->next = NULL; - d->controller = c; - d->flags = 0; - - i2o_report_controller_unit(c, d); - i2o_install_device(c,d); - - for(i = 0; i < MAX_I2O_MODULES; i++) - { - if(i2o_handlers[i] && - i2o_handlers[i]->new_dev_notify && - (i2o_handlers[i]->class&d->lct_data.class_id)) - { - spin_lock(&i2o_dev_lock); - i2o_handlers[i]->new_dev_notify(c,d); - spin_unlock(&i2o_dev_lock); - } - } - - break; - } - - /* - * LCT entry for a device has been modified, so update it - * internally. - */ - case I2O_EVT_IND_EXEC_MODIFIED_LCT: - { - struct i2o_device *d; - i2o_lct_entry *new_lct = (i2o_lct_entry *)&msg[5]; - - for(d = c->devices; d; d = d->next) - { - if(d->lct_data.tid == new_lct->tid) - { - memcpy(&d->lct_data, new_lct, sizeof(i2o_lct_entry)); - break; - } - } - break; - } - - case I2O_EVT_IND_CONFIGURATION_FLAG: - printk(KERN_WARNING "%s requires user configuration\n", c->name); - break; - - case I2O_EVT_IND_GENERAL_WARNING: - printk(KERN_WARNING "%s: Warning notification received!" - "Check configuration for errors!\n", c->name); - break; - - case I2O_EVT_IND_EVT_MASK_MODIFIED: - /* Well I guess that was us hey .. */ - break; - - default: - printk(KERN_WARNING "%s: No handler for event (0x%08x)\n", c->name, msg[4]); - break; - } - } - - return 0; -} - -/* - * Dynamic LCT update. This compares the LCT with the currently - * installed devices to check for device deletions..this needed b/c there - * is no DELETED_LCT_ENTRY EventIndicator for the Executive class so - * we can't just have the event handler do this...annoying - * - * This is a hole in the spec that will hopefully be fixed someday. - */ -static int i2o_dyn_lct(void *foo) -{ - struct i2o_controller *c = (struct i2o_controller *)foo; - struct i2o_device *d = NULL; - struct i2o_device *d1 = NULL; - int i = 0; - int found = 0; - int entries; - void *tmp; - char name[16]; - - lock_kernel(); - daemonize(); - unlock_kernel(); - - sprintf(name, "iop%d_lctd", c->unit); - strcpy(current->comm, name); - - c->lct_running = 1; - - while(1) - { - down_interruptible(&c->lct_sem); - if(signal_pending(current)) - { - dprintk(KERN_ERR "%s: LCT thread dead\n", c->name); - c->lct_running = 0; - return 0; - } - - entries = c->dlct->table_size; - entries -= 3; - entries /= 9; - - dprintk(KERN_INFO "%s: Dynamic LCT Update\n",c->name); - dprintk(KERN_INFO "%s: Dynamic LCT contains %d entries\n", c->name, entries); - - if(!entries) - { - printk(KERN_INFO "%s: Empty LCT???\n", c->name); - continue; - } - - /* - * Loop through all the devices on the IOP looking for their - * LCT data in the LCT. We assume that TIDs are not repeated. - * as that is the only way to really tell. It's been confirmed - * by the IRTOS vendor(s?) that TIDs are not reused until they - * wrap arround(4096), and I doubt a system will up long enough - * to create/delete that many devices. - */ - for(d = c->devices; d; ) - { - found = 0; - d1 = d->next; - - for(i = 0; i < entries; i++) - { - if(d->lct_data.tid == c->dlct->lct_entry[i].tid) - { - found = 1; - break; - } - } - if(!found) - { - dprintk(KERN_INFO "i2o_core: Deleted device!\n"); - spin_lock(&i2o_dev_lock); - i2o_delete_device(d); - spin_unlock(&i2o_dev_lock); - } - d = d1; - } - - /* - * Tell LCT to renotify us next time there is a change - */ - i2o_lct_notify(c); - - /* - * Copy new LCT into public LCT - * - * Possible race if someone is reading LCT while we are copying - * over it. If this happens, we'll fix it then. but I doubt that - * the LCT will get updated often enough or will get read by - * a user often enough to worry. - */ - if(c->lct->table_size < c->dlct->table_size) - { - tmp = c->lct; - c->lct = kmalloc(c->dlct->table_size<<2, GFP_KERNEL); - if(!c->lct) - { - printk(KERN_ERR "%s: No memory for LCT!\n", c->name); - c->lct = tmp; - continue; - } - kfree(tmp); - } - memcpy(c->lct, c->dlct, c->dlct->table_size<<2); - } - - return 0; -} - -/** - * i2o_run_queue - process pending events on a controller - * @c: controller to process - * - * This is called by the bus specific driver layer when an interrupt - * or poll of this card interface is desired. - */ - -void i2o_run_queue(struct i2o_controller *c) -{ - struct i2o_message *m; - u32 mv; - u32 *msg; - - /* - * Old 960 steppings had a bug in the I2O unit that caused - * the queue to appear empty when it wasn't. - */ - if((mv=I2O_REPLY_READ32(c))==0xFFFFFFFF) - mv=I2O_REPLY_READ32(c); - - while(mv!=0xFFFFFFFF) - { - struct i2o_handler *i; - m=(struct i2o_message *)bus_to_virt(mv); - msg=(u32*)m; - - i=i2o_handlers[m->initiator_context&(MAX_I2O_MODULES-1)]; - if(i && i->reply) - i->reply(i,c,m); - else - { - printk(KERN_WARNING "I2O: Spurious reply to handler %d\n", - m->initiator_context&(MAX_I2O_MODULES-1)); - } - i2o_flush_reply(c,mv); - mb(); - - /* That 960 bug again... */ - if((mv=I2O_REPLY_READ32(c))==0xFFFFFFFF) - mv=I2O_REPLY_READ32(c); - } -} - - -/** - * i2o_get_class_name - do i2o class name lookup - * @class: class number - * - * Return a descriptive string for an i2o class - */ - -const char *i2o_get_class_name(int class) -{ - int idx = 16; - static char *i2o_class_name[] = { - "Executive", - "Device Driver Module", - "Block Device", - "Tape Device", - "LAN Interface", - "WAN Interface", - "Fibre Channel Port", - "Fibre Channel Device", - "SCSI Device", - "ATE Port", - "ATE Device", - "Floppy Controller", - "Floppy Device", - "Secondary Bus Port", - "Peer Transport Agent", - "Peer Transport", - "Unknown" - }; - - switch(class&0xFFF) - { - case I2O_CLASS_EXECUTIVE: - idx = 0; break; - case I2O_CLASS_DDM: - idx = 1; break; - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - idx = 2; break; - case I2O_CLASS_SEQUENTIAL_STORAGE: - idx = 3; break; - case I2O_CLASS_LAN: - idx = 4; break; - case I2O_CLASS_WAN: - idx = 5; break; - case I2O_CLASS_FIBRE_CHANNEL_PORT: - idx = 6; break; - case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL: - idx = 7; break; - case I2O_CLASS_SCSI_PERIPHERAL: - idx = 8; break; - case I2O_CLASS_ATE_PORT: - idx = 9; break; - case I2O_CLASS_ATE_PERIPHERAL: - idx = 10; break; - case I2O_CLASS_FLOPPY_CONTROLLER: - idx = 11; break; - case I2O_CLASS_FLOPPY_DEVICE: - idx = 12; break; - case I2O_CLASS_BUS_ADAPTER_PORT: - idx = 13; break; - case I2O_CLASS_PEER_TRANSPORT_AGENT: - idx = 14; break; - case I2O_CLASS_PEER_TRANSPORT: - idx = 15; break; - } - - return i2o_class_name[idx]; -} - - -/** - * i2o_wait_message - obtain an i2o message from the IOP - * @c: controller - * @why: explanation - * - * This function waits up to 5 seconds for a message slot to be - * available. If no message is available it prints an error message - * that is expected to be what the message will be used for (eg - * "get_status"). 0xFFFFFFFF is returned on a failure. - * - * On a success the message is returned. This is the physical page - * frame offset address from the read port. (See the i2o spec) - */ - -u32 i2o_wait_message(struct i2o_controller *c, char *why) -{ - long time=jiffies; - u32 m; - while((m=I2O_POST_READ32(c))==0xFFFFFFFF) - { - if((jiffies-time)>=5*HZ) - { - dprintk(KERN_ERR "%s: Timeout waiting for message frame to send %s.\n", - c->name, why); - return 0xFFFFFFFF; - } - schedule(); - barrier(); - } - return m; -} - -/** - * i2o_report_controller_unit - print information about a tid - * @c: controller - * @d: device - * - * Dump an information block associated with a given unit (TID). The - * tables are read and a block of text is output to printk that is - * formatted intended for the user. - */ - -void i2o_report_controller_unit(struct i2o_controller *c, struct i2o_device *d) -{ - char buf[64]; - char str[22]; - int ret; - int unit = d->lct_data.tid; - - if(verbose==0) - return; - - printk(KERN_INFO "Target ID %d.\n", unit); - if((ret=i2o_query_scalar(c, unit, 0xF100, 3, buf, 16))>=0) - { - buf[16]=0; - printk(KERN_INFO " Vendor: %s\n", buf); - } - if((ret=i2o_query_scalar(c, unit, 0xF100, 4, buf, 16))>=0) - { - buf[16]=0; - printk(KERN_INFO " Device: %s\n", buf); - } - if(i2o_query_scalar(c, unit, 0xF100, 5, buf, 16)>=0) - { - buf[16]=0; - printk(KERN_INFO " Description: %s\n", buf); - } - if((ret=i2o_query_scalar(c, unit, 0xF100, 6, buf, 8))>=0) - { - buf[8]=0; - printk(KERN_INFO " Rev: %s\n", buf); - } - - printk(KERN_INFO " Class: "); - sprintf(str, "%-21s", i2o_get_class_name(d->lct_data.class_id)); - printk("%s\n", str); - - printk(KERN_INFO " Subclass: 0x%04X\n", d->lct_data.sub_class); - printk(KERN_INFO " Flags: "); - - if(d->lct_data.device_flags&(1<<0)) - printk("C"); // ConfigDialog requested - if(d->lct_data.device_flags&(1<<1)) - printk("U"); // Multi-user capable - if(!(d->lct_data.device_flags&(1<<4))) - printk("P"); // Peer service enabled! - if(!(d->lct_data.device_flags&(1<<5))) - printk("M"); // Mgmt service enabled! - printk("\n"); - -} - - -/* - * Parse the hardware resource table. Right now we print it out - * and don't do a lot with it. We should collate these and then - * interact with the Linux resource allocation block. - * - * Lets prove we can read it first eh ? - * - * This is full of endianisms! - */ - -static int i2o_parse_hrt(struct i2o_controller *c) -{ -#ifdef DRIVERDEBUG - u32 *rows=(u32*)c->hrt; - u8 *p=(u8 *)c->hrt; - u8 *d; - int count; - int length; - int i; - int state; - - if(p[3]!=0) - { - printk(KERN_ERR "%s: HRT table for controller is too new a version.\n", - c->name); - return -1; - } - - count=p[0]|(p[1]<<8); - length = p[2]; - - printk(KERN_INFO "%s: HRT has %d entries of %d bytes each.\n", - c->name, count, length<<2); - - rows+=2; - - for(i=0;i>=12; - if(state&(1<<0)) - printk("H"); /* Hidden */ - if(state&(1<<2)) - { - printk("P"); /* Present */ - if(state&(1<<1)) - printk("C"); /* Controlled */ - } - if(state>9) - printk("*"); /* Hard */ - - printk("]:"); - - switch(p[3]&0xFFFF) - { - case 0: - /* Adapter private bus - easy */ - printk("Local bus %d: I/O at 0x%04X Mem 0x%08X", - p[2], d[1]<<8|d[0], *(u32 *)(d+4)); - break; - case 1: - /* ISA bus */ - printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X", - p[2], d[2], d[1]<<8|d[0], *(u32 *)(d+4)); - break; - - case 2: /* EISA bus */ - printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X", - p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4)); - break; - - case 3: /* MCA bus */ - printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X", - p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4)); - break; - - case 4: /* PCI bus */ - printk("PCI %d: Bus %d Device %d Function %d", - p[2], d[2], d[1], d[0]); - break; - - case 0x80: /* Other */ - default: - printk("Unsupported bus type."); - break; - } - printk("\n"); - rows+=length; - } -#endif - return 0; -} - -/* - * The logical configuration table tells us what we can talk to - * on the board. Most of the stuff isn't interesting to us. - */ - -static int i2o_parse_lct(struct i2o_controller *c) -{ - int i; - int max; - int tid; - struct i2o_device *d; - i2o_lct *lct = c->lct; - - if (lct == NULL) { - printk(KERN_ERR "%s: LCT is empty???\n", c->name); - return -1; - } - - max = lct->table_size; - max -= 3; - max /= 9; - - printk(KERN_INFO "%s: LCT has %d entries.\n", c->name, max); - - if(lct->iop_flags&(1<<0)) - printk(KERN_WARNING "%s: Configuration dialog desired.\n", c->name); - - for(i=0;icontroller = c; - d->next = NULL; - - memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry)); - - d->flags = 0; - tid = d->lct_data.tid; - - i2o_report_controller_unit(c, d); - - i2o_install_device(c, d); - } - return 0; -} - - -/** - * i2o_quiesce_controller - quiesce controller - * @c: controller - * - * Quiesce an IOP. Causes IOP to make external operation quiescent - * (i2o 'READY' state). Internal operation of the IOP continues normally. - */ - -int i2o_quiesce_controller(struct i2o_controller *c) -{ - u32 msg[4]; - int ret; - - i2o_status_get(c); - - /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */ - - if ((c->status_block->iop_state != ADAPTER_STATE_READY) && - (c->status_block->iop_state != ADAPTER_STATE_OPERATIONAL)) - { - return 0; - } - - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID; - msg[3] = 0; - - /* Long timeout needed for quiesce if lots of devices */ - - if ((ret = i2o_post_wait(c, msg, sizeof(msg), 240))) - printk(KERN_INFO "%s: Unable to quiesce (status=%#x).\n", - c->name, -ret); - else - dprintk(KERN_INFO "%s: Quiesced.\n", c->name); - - i2o_status_get(c); // Entered READY state - return ret; -} - -/** - * i2o_enable_controller - move controller from ready to operational - * @c: controller - * - * Enable IOP. This allows the IOP to resume external operations and - * reverses the effect of a quiesce. In the event of an error a negative - * errno code is returned. - */ - -int i2o_enable_controller(struct i2o_controller *c) -{ - u32 msg[4]; - int ret; - - i2o_status_get(c); - - /* Enable only allowed on READY state */ - if(c->status_block->iop_state != ADAPTER_STATE_READY) - return -EINVAL; - - msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1]=I2O_CMD_SYS_ENABLE<<24|HOST_TID<<12|ADAPTER_TID; - - /* How long of a timeout do we need? */ - - if ((ret = i2o_post_wait(c, msg, sizeof(msg), 240))) - printk(KERN_ERR "%s: Could not enable (status=%#x).\n", - c->name, -ret); - else - dprintk(KERN_INFO "%s: Enabled.\n", c->name); - - i2o_status_get(c); // entered OPERATIONAL state - - return ret; -} - -/** - * i2o_clear_controller - clear a controller - * @c: controller - * - * Clear an IOP to HOLD state, ie. terminate external operations, clear all - * input queues and prepare for a system restart. IOP's internal operation - * continues normally and the outbound queue is alive. - * The IOP is not expected to rebuild its LCT. - */ - -int i2o_clear_controller(struct i2o_controller *c) -{ - struct i2o_controller *iop; - u32 msg[4]; - int ret; - - /* Quiesce all IOPs first */ - - for (iop = i2o_controller_chain; iop; iop = iop->next) - i2o_quiesce_controller(iop); - - msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1]=I2O_CMD_ADAPTER_CLEAR<<24|HOST_TID<<12|ADAPTER_TID; - msg[3]=0; - - if ((ret=i2o_post_wait(c, msg, sizeof(msg), 30))) - printk(KERN_INFO "%s: Unable to clear (status=%#x).\n", - c->name, -ret); - else - dprintk(KERN_INFO "%s: Cleared.\n",c->name); - - i2o_status_get(c); - - /* Enable other IOPs */ - - for (iop = i2o_controller_chain; iop; iop = iop->next) - if (iop != c) - i2o_enable_controller(iop); - - return ret; -} - - -/** - * i2o_reset_controller - reset an IOP - * @c: controller to reset - * - * Reset the IOP into INIT state and wait until IOP gets into RESET state. - * Terminate all external operations, clear IOP's inbound and outbound - * queues, terminate all DDMs, and reload the IOP's operating environment - * and all local DDMs. The IOP rebuilds its LCT. - */ - -static int i2o_reset_controller(struct i2o_controller *c) -{ - struct i2o_controller *iop; - u32 m; - u8 *status; - u32 *msg; - long time; - - /* Quiesce all IOPs first */ - - for (iop = i2o_controller_chain; iop; iop = iop->next) - { - if(iop->type != I2O_TYPE_PCI || !iop->bus.pci.dpt) - i2o_quiesce_controller(iop); - } - - m=i2o_wait_message(c, "AdapterReset"); - if(m==0xFFFFFFFF) - return -ETIMEDOUT; - msg=(u32 *)(c->mem_offset+m); - - status=(void *)kmalloc(4, GFP_KERNEL); - if(status==NULL) { - printk(KERN_ERR "IOP reset failed - no free memory.\n"); - return -ENOMEM; - } - memset(status, 0, 4); - - msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID; - msg[2]=core_context; - msg[3]=0; - msg[4]=0; - msg[5]=0; - msg[6]=virt_to_bus(status); - msg[7]=0; /* 64bit host FIXME */ - - i2o_post_message(c,m); - - /* Wait for a reply */ - time=jiffies; - while(*status==0) - { - if((jiffies-time)>=20*HZ) - { - printk(KERN_ERR "IOP reset timeout.\n"); - // Better to leak this for safety: kfree(status); - return -ETIMEDOUT; - } - schedule(); - barrier(); - } - - if (*status==I2O_CMD_IN_PROGRESS) - { - /* - * Once the reset is sent, the IOP goes into the INIT state - * which is indeterminate. We need to wait until the IOP - * has rebooted before we can let the system talk to - * it. We read the inbound Free_List until a message is - * available. If we can't read one in the given ammount of - * time, we assume the IOP could not reboot properly. - */ - - dprintk(KERN_INFO "%s: Reset in progress, waiting for reboot...\n", - c->name); - - time = jiffies; - m = I2O_POST_READ32(c); - while(m == 0XFFFFFFFF) - { - if((jiffies-time) >= 30*HZ) - { - printk(KERN_ERR "%s: Timeout waiting for IOP reset.\n", - c->name); - return -ETIMEDOUT; - } - schedule(); - barrier(); - m = I2O_POST_READ32(c); - } - i2o_flush_reply(c,m); - } - - /* If IopReset was rejected or didn't perform reset, try IopClear */ - - i2o_status_get(c); - if (status[0] == I2O_CMD_REJECTED || - c->status_block->iop_state != ADAPTER_STATE_RESET) - { - printk(KERN_WARNING "%s: Reset rejected, trying to clear\n",c->name); - i2o_clear_controller(c); - } - else - dprintk(KERN_INFO "%s: Reset completed.\n", c->name); - - /* Enable other IOPs */ - - for (iop = i2o_controller_chain; iop; iop = iop->next) - if (iop != c) - i2o_enable_controller(iop); - - kfree(status); - return 0; -} - - -/** - * i2o_status_get - get the status block for the IOP - * @c: controller - * - * Issue a status query on the controller. This updates the - * attached status_block. If the controller fails to reply or an - * error occurs then a negative errno code is returned. On success - * zero is returned and the status_blok is updated. - */ - -int i2o_status_get(struct i2o_controller *c) -{ - long time; - u32 m; - u32 *msg; - u8 *status_block; - - if (c->status_block == NULL) - { - c->status_block = (i2o_status_block *) - kmalloc(sizeof(i2o_status_block),GFP_KERNEL); - if (c->status_block == NULL) - { - printk(KERN_CRIT "%s: Get Status Block failed; Out of memory.\n", - c->name); - return -ENOMEM; - } - } - - status_block = (u8*)c->status_block; - memset(c->status_block,0,sizeof(i2o_status_block)); - - m=i2o_wait_message(c, "StatusGet"); - if(m==0xFFFFFFFF) - return -ETIMEDOUT; - msg=(u32 *)(c->mem_offset+m); - - msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1]=I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID; - msg[2]=core_context; - msg[3]=0; - msg[4]=0; - msg[5]=0; - msg[6]=virt_to_bus(c->status_block); - msg[7]=0; /* 64bit host FIXME */ - msg[8]=sizeof(i2o_status_block); /* always 88 bytes */ - - i2o_post_message(c,m); - - /* Wait for a reply */ - - time=jiffies; - while(status_block[87]!=0xFF) - { - if((jiffies-time)>=5*HZ) - { - printk(KERN_ERR "%s: Get status timeout.\n",c->name); - return -ETIMEDOUT; - } - schedule(); - barrier(); - } - -#ifdef DRIVERDEBUG - printk(KERN_INFO "%s: State = ", c->name); - switch (c->status_block->iop_state) { - case 0x01: - printk("INIT\n"); - break; - case 0x02: - printk("RESET\n"); - break; - case 0x04: - printk("HOLD\n"); - break; - case 0x05: - printk("READY\n"); - break; - case 0x08: - printk("OPERATIONAL\n"); - break; - case 0x10: - printk("FAILED\n"); - break; - case 0x11: - printk("FAULTED\n"); - break; - default: - printk("%x (unknown !!)\n",c->status_block->iop_state); -} -#endif - - return 0; -} - -/* - * Get the Hardware Resource Table for the device. - * The HRT contains information about possible hidden devices - * but is mostly useless to us - */ -int i2o_hrt_get(struct i2o_controller *c) -{ - u32 msg[6]; - int ret, size = sizeof(i2o_hrt); - - /* First read just the header to figure out the real size */ - - do { - if (c->hrt == NULL) { - c->hrt=kmalloc(size, GFP_KERNEL); - if (c->hrt == NULL) { - printk(KERN_CRIT "%s: Hrt Get failed; Out of memory.\n", c->name); - return -ENOMEM; - } - } - - msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4; - msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[3]= 0; - msg[4]= (0xD0000000 | size); /* Simple transaction */ - msg[5]= virt_to_bus(c->hrt); /* Dump it here */ - - ret = i2o_post_wait_mem(c, msg, sizeof(msg), 20, c->hrt, NULL); - - if(ret == -ETIMEDOUT) - { - /* The HRT block we used is in limbo somewhere. When the iop wakes up - we will recover it */ - c->hrt = NULL; - return ret; - } - - if(ret<0) - { - printk(KERN_ERR "%s: Unable to get HRT (status=%#x)\n", - c->name, -ret); - return ret; - } - - if (c->hrt->num_entries * c->hrt->entry_len << 2 > size) { - size = c->hrt->num_entries * c->hrt->entry_len << 2; - kfree(c->hrt); - c->hrt = NULL; - } - } while (c->hrt == NULL); - - i2o_parse_hrt(c); // just for debugging - - return 0; -} - -/* - * Send the I2O System Table to the specified IOP - * - * The system table contains information about all the IOPs in the - * system. It is build and then sent to each IOP so that IOPs can - * establish connections between each other. - * - */ -static int i2o_systab_send(struct i2o_controller *iop) -{ - u32 msg[12]; - int ret; - u32 *privbuf = kmalloc(16, GFP_KERNEL); - if(privbuf == NULL) - return -ENOMEM; - - privbuf[0] = iop->status_block->current_mem_base; - privbuf[1] = iop->status_block->current_mem_size; - privbuf[2] = iop->status_block->current_io_base; - privbuf[3] = iop->status_block->current_io_size; - - msg[0] = I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6; - msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[3] = 0; - msg[4] = (0<<16) | ((iop->unit+2) << 12); /* Host 0 IOP ID (unit + 2) */ - msg[5] = 0; /* Segment 0 */ - - /* - * Provide three SGL-elements: - * System table (SysTab), Private memory space declaration and - * Private i/o space declaration - * - * FIXME: provide these for controllers needing them - */ - msg[6] = 0x54000000 | sys_tbl_len; - msg[7] = virt_to_bus(sys_tbl); - msg[8] = 0x54000000 | 0; - msg[9] = virt_to_bus(privbuf); - msg[10] = 0xD4000000 | 0; - msg[11] = virt_to_bus(privbuf+8); - - ret=i2o_post_wait_mem(iop, msg, sizeof(msg), 120, privbuf, NULL); - - if(ret==-ETIMEDOUT) - { - printk(KERN_ERR "%s: SysTab setup timed out.\n", iop->name); - } - else if(ret<0) - { - printk(KERN_ERR "%s: Unable to set SysTab (status=%#x).\n", - iop->name, -ret); - kfree(privbuf); - } - else - { - dprintk(KERN_INFO "%s: SysTab set.\n", iop->name); - kfree(privbuf); - } - i2o_status_get(iop); // Entered READY state - - return ret; - - } - -/* - * Initialize I2O subsystem. - */ -static void __init i2o_sys_init(void) -{ - struct i2o_controller *iop, *niop = NULL; - - printk(KERN_INFO "Activating I2O controllers...\n"); - printk(KERN_INFO "This may take a few minutes if there are many devices\n"); - - /* In INIT state, Activate IOPs */ - for (iop = i2o_controller_chain; iop; iop = niop) { - dprintk(KERN_INFO "Calling i2o_activate_controller for %s...\n", - iop->name); - niop = iop->next; - if (i2o_activate_controller(iop) < 0) - i2o_delete_controller(iop); - } - - /* Active IOPs in HOLD state */ - -rebuild_sys_tab: - if (i2o_controller_chain == NULL) - return; - - /* - * If build_sys_table fails, we kill everything and bail - * as we can't init the IOPs w/o a system table - */ - dprintk(KERN_INFO "i2o_core: Calling i2o_build_sys_table...\n"); - if (i2o_build_sys_table() < 0) { - i2o_sys_shutdown(); - return; - } - - /* If IOP don't get online, we need to rebuild the System table */ - for (iop = i2o_controller_chain; iop; iop = niop) { - niop = iop->next; - dprintk(KERN_INFO "Calling i2o_online_controller for %s...\n", iop->name); - if (i2o_online_controller(iop) < 0) { - i2o_delete_controller(iop); - goto rebuild_sys_tab; - } - } - - /* Active IOPs now in OPERATIONAL state */ - - /* - * Register for status updates from all IOPs - */ - for(iop = i2o_controller_chain; iop; iop=iop->next) { - - /* Create a kernel thread to deal with dynamic LCT updates */ - iop->lct_pid = kernel_thread(i2o_dyn_lct, iop, CLONE_SIGHAND); - - /* Update change ind on DLCT */ - iop->dlct->change_ind = iop->lct->change_ind; - - /* Start dynamic LCT updates */ - i2o_lct_notify(iop); - - /* Register for all events from IRTOS */ - i2o_event_register(iop, core_context, 0, 0, 0xFFFFFFFF); - } -} - -/** - * i2o_sys_shutdown - shutdown I2O system - * - * Bring down each i2o controller and then return. Each controller - * is taken through an orderly shutdown - */ - -static void i2o_sys_shutdown(void) -{ - struct i2o_controller *iop, *niop; - - /* Delete all IOPs from the controller chain */ - /* that will reset all IOPs too */ - - for (iop = i2o_controller_chain; iop; iop = niop) { - niop = iop->next; - i2o_delete_controller(iop); - } -} - -/** - * i2o_activate_controller - bring controller up to HOLD - * @iop: controller - * - * This function brings an I2O controller into HOLD state. The adapter - * is reset if neccessary and then the queues and resource table - * are read. -1 is returned on a failure, 0 on success. - * - */ - -int i2o_activate_controller(struct i2o_controller *iop) -{ - /* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */ - /* In READY state, Get status */ - - if (i2o_status_get(iop) < 0) { - printk(KERN_INFO "Unable to obtain status of %s, " - "attempting a reset.\n", iop->name); - if (i2o_reset_controller(iop) < 0) - return -1; - } - - if(iop->status_block->iop_state == ADAPTER_STATE_FAULTED) { - printk(KERN_CRIT "%s: hardware fault\n", iop->name); - return -1; - } - - if (iop->status_block->i2o_version > I2OVER15) { - printk(KERN_ERR "%s: Not running vrs. 1.5. of the I2O Specification.\n", - iop->name); - return -1; - } - - if (iop->status_block->iop_state == ADAPTER_STATE_READY || - iop->status_block->iop_state == ADAPTER_STATE_OPERATIONAL || - iop->status_block->iop_state == ADAPTER_STATE_HOLD || - iop->status_block->iop_state == ADAPTER_STATE_FAILED) - { - dprintk(KERN_INFO "%s: Already running, trying to reset...\n", - iop->name); - if (i2o_reset_controller(iop) < 0) - return -1; - } - - if (i2o_init_outbound_q(iop) < 0) - return -1; - - if (i2o_post_outbound_messages(iop)) - return -1; - - /* In HOLD state */ - - if (i2o_hrt_get(iop) < 0) - return -1; - - return 0; -} - - -/** - * i2o_init_outbound_queue - setup the outbound queue - * @c: controller - * - * Clear and (re)initialize IOP's outbound queue. Returns 0 on - * success or a negative errno code on a failure. - */ - -int i2o_init_outbound_q(struct i2o_controller *c) -{ - u8 *status; - u32 m; - u32 *msg; - u32 time; - - dprintk(KERN_INFO "%s: Initializing Outbound Queue...\n", c->name); - m=i2o_wait_message(c, "OutboundInit"); - if(m==0xFFFFFFFF) - return -ETIMEDOUT; - msg=(u32 *)(c->mem_offset+m); - - status = kmalloc(4,GFP_KERNEL); - if (status==NULL) { - printk(KERN_ERR "%s: Outbound Queue initialization failed - no free memory.\n", - c->name); - return -ENOMEM; - } - memset(status, 0, 4); - - msg[0]= EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6; - msg[1]= I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2]= core_context; - msg[3]= 0x0106; /* Transaction context */ - msg[4]= 4096; /* Host page frame size */ - /* Frame size is in words. Pick 128, its what everyone elses uses and - other sizes break some adapters. */ - msg[5]= MSG_FRAME_SIZE<<16|0x80; /* Outbound msg frame size and Initcode */ - msg[6]= 0xD0000004; /* Simple SG LE, EOB */ - msg[7]= virt_to_bus(status); - - i2o_post_message(c,m); - - barrier(); - time=jiffies; - while(status[0] < I2O_CMD_REJECTED) - { - if((jiffies-time)>=30*HZ) - { - if(status[0]==0x00) - printk(KERN_ERR "%s: Ignored queue initialize request.\n", - c->name); - else - printk(KERN_ERR "%s: Outbound queue initialize timeout.\n", - c->name); - kfree(status); - return -ETIMEDOUT; - } - schedule(); - barrier(); - } - - if(status[0] != I2O_CMD_COMPLETED) - { - printk(KERN_ERR "%s: IOP outbound initialise failed.\n", c->name); - kfree(status); - return -ETIMEDOUT; - } - - return 0; -} - -/** - * i2o_post_outbound_messages - fill message queue - * @c: controller - * - * Allocate a message frame and load the messages into the IOP. The - * function returns zero on success or a negative errno code on - * failure. - */ - -int i2o_post_outbound_messages(struct i2o_controller *c) -{ - int i; - u32 m; - /* Alloc space for IOP's outbound queue message frames */ - - c->page_frame = kmalloc(MSG_POOL_SIZE, GFP_KERNEL); - if(c->page_frame==NULL) { - printk(KERN_CRIT "%s: Outbound Q initialize failed; out of memory.\n", - c->name); - return -ENOMEM; - } - m=virt_to_bus(c->page_frame); - - /* Post frames */ - - for(i=0; i< NMBR_MSG_FRAMES; i++) { - I2O_REPLY_WRITE32(c,m); - mb(); - m += MSG_FRAME_SIZE; - } - - return 0; -} - -/* - * Get the IOP's Logical Configuration Table - */ -int i2o_lct_get(struct i2o_controller *c) -{ - u32 msg[8]; - int ret, size = c->status_block->expected_lct_size; - - do { - if (c->lct == NULL) { - c->lct = kmalloc(size, GFP_KERNEL); - if(c->lct == NULL) { - printk(KERN_CRIT "%s: Lct Get failed. Out of memory.\n", - c->name); - return -ENOMEM; - } - } - memset(c->lct, 0, size); - - msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; - msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; - /* msg[2] filled in i2o_post_wait */ - msg[3] = 0; - msg[4] = 0xFFFFFFFF; /* All devices */ - msg[5] = 0x00000000; /* Report now */ - msg[6] = 0xD0000000|size; - msg[7] = virt_to_bus(c->lct); - - ret=i2o_post_wait_mem(c, msg, sizeof(msg), 120, c->lct, NULL); - - if(ret == -ETIMEDOUT) - { - c->lct = NULL; - return ret; - } - - if(ret<0) - { - printk(KERN_ERR "%s: LCT Get failed (status=%#x.\n", - c->name, -ret); - return ret; - } - - if (c->lct->table_size << 2 > size) { - size = c->lct->table_size << 2; - kfree(c->lct); - c->lct = NULL; - } - } while (c->lct == NULL); - - if ((ret=i2o_parse_lct(c)) < 0) - return ret; - - return 0; -} - -/* - * Like above, but used for async notification. The main - * difference is that we keep track of the CurrentChangeIndiicator - * so that we only get updates when it actually changes. - * - */ -int i2o_lct_notify(struct i2o_controller *c) -{ - u32 msg[8]; - - msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; - msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2] = core_context; - msg[3] = 0xDEADBEEF; - msg[4] = 0xFFFFFFFF; /* All devices */ - msg[5] = c->dlct->change_ind+1; /* Next change */ - msg[6] = 0xD0000000|8192; - msg[7] = virt_to_bus(c->dlct); - - return i2o_post_this(c, msg, sizeof(msg)); -} - -/* - * Bring a controller online into OPERATIONAL state. - */ - -int i2o_online_controller(struct i2o_controller *iop) -{ - u32 v; - - if (i2o_systab_send(iop) < 0) - return -1; - - /* In READY state */ - - dprintk(KERN_INFO "%s: Attempting to enable...\n", iop->name); - if (i2o_enable_controller(iop) < 0) - return -1; - - /* In OPERATIONAL state */ - - dprintk(KERN_INFO "%s: Attempting to get/parse lct...\n", iop->name); - if (i2o_lct_get(iop) < 0) - return -1; - - /* Check battery status */ - - iop->battery = 0; - if(i2o_query_scalar(iop, ADAPTER_TID, 0x0000, 4, &v, 4)>=0) - { - if(v&16) - iop->battery = 1; - } - - return 0; -} - -/* - * Build system table - * - * The system table contains information about all the IOPs in the - * system (duh) and is used by the Executives on the IOPs to establish - * peer2peer connections. We're not supporting peer2peer at the moment, - * but this will be needed down the road for things like lan2lan forwarding. - */ -static int i2o_build_sys_table(void) -{ - struct i2o_controller *iop = NULL; - struct i2o_controller *niop = NULL; - int count = 0; - - sys_tbl_len = sizeof(struct i2o_sys_tbl) + // Header + IOPs - (i2o_num_controllers) * - sizeof(struct i2o_sys_tbl_entry); - - if(sys_tbl) - kfree(sys_tbl); - - sys_tbl = kmalloc(sys_tbl_len, GFP_KERNEL); - if(!sys_tbl) { - printk(KERN_CRIT "SysTab Set failed. Out of memory.\n"); - return -ENOMEM; - } - memset((void*)sys_tbl, 0, sys_tbl_len); - - sys_tbl->num_entries = i2o_num_controllers; - sys_tbl->version = I2OVERSION; /* TODO: Version 2.0 */ - sys_tbl->change_ind = sys_tbl_ind++; - - for(iop = i2o_controller_chain; iop; iop = niop) - { - niop = iop->next; - - /* - * Get updated IOP state so we have the latest information - * - * We should delete the controller at this point if it - * doesn't respond since if it's not on the system table - * it is techninically not part of the I2O subsyßtem... - */ - if(i2o_status_get(iop)) { - printk(KERN_ERR "%s: Deleting b/c could not get status while" - "attempting to build system table\n", iop->name); - i2o_delete_controller(iop); - sys_tbl->num_entries--; - continue; // try the next one - } - - sys_tbl->iops[count].org_id = iop->status_block->org_id; - sys_tbl->iops[count].iop_id = iop->unit + 2; - sys_tbl->iops[count].seg_num = 0; - sys_tbl->iops[count].i2o_version = - iop->status_block->i2o_version; - sys_tbl->iops[count].iop_state = - iop->status_block->iop_state; - sys_tbl->iops[count].msg_type = - iop->status_block->msg_type; - sys_tbl->iops[count].frame_size = - iop->status_block->inbound_frame_size; - sys_tbl->iops[count].last_changed = sys_tbl_ind - 1; // ?? - sys_tbl->iops[count].iop_capabilities = - iop->status_block->iop_capabilities; - sys_tbl->iops[count].inbound_low = - (u32)virt_to_bus(iop->post_port); - sys_tbl->iops[count].inbound_high = 0; // TODO: 64-bit support - - count++; - } - -#ifdef DRIVERDEBUG -{ - u32 *table; - table = (u32*)sys_tbl; - for(count = 0; count < (sys_tbl_len >>2); count++) - printk(KERN_INFO "sys_tbl[%d] = %0#10x\n", count, table[count]); -} -#endif - - return 0; -} - - -/* - * Run time support routines - */ - -/* - * Generic "post and forget" helpers. This is less efficient - we do - * a memcpy for example that isnt strictly needed, but for most uses - * this is simply not worth optimising - */ - -int i2o_post_this(struct i2o_controller *c, u32 *data, int len) -{ - u32 m; - u32 *msg; - unsigned long t=jiffies; - - do - { - mb(); - m = I2O_POST_READ32(c); - } - while(m==0xFFFFFFFF && (jiffies-t)name); - return -ETIMEDOUT; - } - msg = (u32 *)(c->mem_offset + m); - memcpy_toio(msg, data, len); - i2o_post_message(c,m); - return 0; -} - -/** - * i2o_post_wait_mem - I2O query/reply with DMA buffers - * @c: controller - * @msg: message to send - * @len: length of message - * @timeout: time in seconds to wait - * @mem1: attached memory buffer 1 - * @mem2: attached memory buffer 2 - * - * This core API allows an OSM to post a message and then be told whether - * or not the system received a successful reply. - * - * If the message times out then the value '-ETIMEDOUT' is returned. This - * is a special case. In this situation the message may (should) complete - * at an indefinite time in the future. When it completes it will use the - * memory buffers attached to the request. If -ETIMEDOUT is returned then - * the memory buffers must not be freed. Instead the event completion will - * free them for you. In all other cases the buffers are your problem. - * - * Pass NULL for unneeded buffers. - */ - -int i2o_post_wait_mem(struct i2o_controller *c, u32 *msg, int len, int timeout, void *mem1, void *mem2) -{ - DECLARE_WAIT_QUEUE_HEAD(wq_i2o_post); - int complete = 0; - int status; - int flags = 0; - struct i2o_post_wait_data *wait_data = - kmalloc(sizeof(struct i2o_post_wait_data), GFP_KERNEL); - - if(!wait_data) - return -ENOMEM; - - /* - * Create a new notification object - */ - wait_data->status = &status; - wait_data->complete = &complete; - wait_data->mem[0] = mem1; - wait_data->mem[1] = mem2; - /* - * Queue the event with its unique id - */ - spin_lock_irqsave(&post_wait_lock, flags); - - wait_data->next = post_wait_queue; - post_wait_queue = wait_data; - wait_data->id = (++post_wait_id) & 0x7fff; - wait_data->wq = &wq_i2o_post; - - spin_unlock_irqrestore(&post_wait_lock, flags); - - /* - * Fill in the message id - */ - - msg[2] = 0x80000000|(u32)core_context|((u32)wait_data->id<<16); - - /* - * Post the message to the controller. At some point later it - * will return. If we time out before it returns then - * complete will be zero. From the point post_this returns - * the wait_data may have been deleted. - */ - if ((status = i2o_post_this(c, msg, len))==0) { - sleep_on_timeout(&wq_i2o_post, HZ * timeout); - } - else - return -EIO; - - if(signal_pending(current)) - status = -EINTR; - - spin_lock_irqsave(&post_wait_lock, flags); - barrier(); /* Be sure we see complete as it is locked */ - if(!complete) - { - /* - * Mark the entry dead. We cannot remove it. This is important. - * When it does terminate (which it must do if the controller hasnt - * died..) then it will otherwise scribble on stuff. - * !complete lets us safely check if the entry is still - * allocated and thus we can write into it - */ - wait_data->wq = NULL; - status = -ETIMEDOUT; - } - else - { - /* Debugging check - remove me soon */ - if(status == -ETIMEDOUT) - { - printk("TIMEDOUT BUG!\n"); - status = -EIO; - } - } - /* And the wait_data is not leaked either! */ - spin_unlock_irqrestore(&post_wait_lock, flags); - return status; -} - -/** - * i2o_post_wait - I2O query/reply - * @c: controller - * @msg: message to send - * @len: length of message - * @timeout: time in seconds to wait - * - * This core API allows an OSM to post a message and then be told whether - * or not the system received a successful reply. - */ - -int i2o_post_wait(struct i2o_controller *c, u32 *msg, int len, int timeout) -{ - return i2o_post_wait_mem(c, msg, len, timeout, NULL, NULL); -} - -/* - * i2o_post_wait is completed and we want to wake up the - * sleeping proccess. Called by core's reply handler. - */ - -static void i2o_post_wait_complete(u32 context, int status) -{ - struct i2o_post_wait_data **p1, *q; - unsigned long flags; - - /* - * We need to search through the post_wait - * queue to see if the given message is still - * outstanding. If not, it means that the IOP - * took longer to respond to the message than we - * had allowed and timer has already expired. - * Not much we can do about that except log - * it for debug purposes, increase timeout, and recompile - * - * Lock needed to keep anyone from moving queue pointers - * around while we're looking through them. - */ - - spin_lock_irqsave(&post_wait_lock, flags); - - for(p1 = &post_wait_queue; *p1!=NULL; p1 = &((*p1)->next)) - { - q = (*p1); - if(q->id == ((context >> 16) & 0x7fff)) { - /* - * Delete it - */ - - *p1 = q->next; - - /* - * Live or dead ? - */ - - if(q->wq) - { - /* Live entry - wakeup and set status */ - *q->status = status; - *q->complete = 1; - wake_up(q->wq); - } - else - { - /* - * Free resources. Caller is dead - */ - if(q->mem[0]) - kfree(q->mem[0]); - if(q->mem[1]) - kfree(q->mem[1]); - printk(KERN_WARNING "i2o_post_wait event completed after timeout.\n"); - } - kfree(q); - spin_unlock(&post_wait_lock); - return; - } - } - spin_unlock(&post_wait_lock); - - printk(KERN_DEBUG "i2o_post_wait: Bogus reply!\n"); -} - -/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET - * - * This function can be used for all UtilParamsGet/Set operations. - * The OperationList is given in oplist-buffer, - * and results are returned in reslist-buffer. - * Note that the minimum sized reslist is 8 bytes and contains - * ResultCount, ErrorInfoSize, BlockStatus and BlockSize. - */ -int i2o_issue_params(int cmd, struct i2o_controller *iop, int tid, - void *oplist, int oplen, void *reslist, int reslen) -{ - u32 msg[9]; - u32 *res32 = (u32*)reslist; - u32 *restmp = (u32*)reslist; - int len = 0; - int i = 0; - int wait_status; - u32 *opmem, *resmem; - - /* Get DMAable memory */ - opmem = kmalloc(oplen, GFP_KERNEL); - if(opmem == NULL) - return -ENOMEM; - memcpy(opmem, oplist, oplen); - - resmem = kmalloc(reslen, GFP_KERNEL); - if(resmem == NULL) - { - kfree(opmem); - return -ENOMEM; - } - - msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_5; - msg[1] = cmd << 24 | HOST_TID << 12 | tid; - msg[3] = 0; - msg[4] = 0; - msg[5] = 0x54000000 | oplen; /* OperationList */ - msg[6] = virt_to_bus(opmem); - msg[7] = 0xD0000000 | reslen; /* ResultList */ - msg[8] = virt_to_bus(resmem); - - wait_status = i2o_post_wait_mem(iop, msg, sizeof(msg), 10, opmem, resmem); - - /* - * This only looks like a memory leak - don't "fix" it. - */ - if(wait_status == -ETIMEDOUT) - return wait_status; - - /* Query failed */ - if(wait_status != 0) - { - kfree(resmem); - kfree(opmem); - return wait_status; - } - - memcpy(reslist, resmem, reslen); - /* - * Calculate number of bytes of Result LIST - * We need to loop through each Result BLOCK and grab the length - */ - restmp = res32 + 1; - len = 1; - for(i = 0; i < (res32[0]&0X0000FFFF); i++) - { - if(restmp[0]&0x00FF0000) /* BlockStatus != SUCCESS */ - { - printk(KERN_WARNING "%s - Error:\n ErrorInfoSize = 0x%02x, " - "BlockStatus = 0x%02x, BlockSize = 0x%04x\n", - (cmd == I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET" - : "PARAMS_GET", - res32[1]>>24, (res32[1]>>16)&0xFF, res32[1]&0xFFFF); - - /* - * If this is the only request,than we return an error - */ - if((res32[0]&0x0000FFFF) == 1) - { - return -((res32[1] >> 16) & 0xFF); /* -BlockStatus */ - } - } - len += restmp[0] & 0x0000FFFF; /* Length of res BLOCK */ - restmp += restmp[0] & 0x0000FFFF; /* Skip to next BLOCK */ - } - return (len << 2); /* bytes used by result list */ -} - -/* - * Query one scalar group value or a whole scalar group. - */ -int i2o_query_scalar(struct i2o_controller *iop, int tid, - int group, int field, void *buf, int buflen) -{ - u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field }; - u8 resblk[8+buflen]; /* 8 bytes for header */ - int size; - - if (field == -1) /* whole group */ - opblk[4] = -1; - - size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET, iop, tid, - opblk, sizeof(opblk), resblk, sizeof(resblk)); - - memcpy(buf, resblk+8, buflen); /* cut off header */ - - if(size>buflen) - return buflen; - return size; -} - -/* - * Set a scalar group value or a whole group. - */ -int i2o_set_scalar(struct i2o_controller *iop, int tid, - int group, int field, void *buf, int buflen) -{ - u16 *opblk; - u8 resblk[8+buflen]; /* 8 bytes for header */ - int size; - - opblk = kmalloc(buflen+64, GFP_KERNEL); - if (opblk == NULL) - { - printk(KERN_ERR "i2o: no memory for operation buffer.\n"); - return -ENOMEM; - } - - opblk[0] = 1; /* operation count */ - opblk[1] = 0; /* pad */ - opblk[2] = I2O_PARAMS_FIELD_SET; - opblk[3] = group; - - if(field == -1) { /* whole group */ - opblk[4] = -1; - memcpy(opblk+5, buf, buflen); - } - else /* single field */ - { - opblk[4] = 1; - opblk[5] = field; - memcpy(opblk+6, buf, buflen); - } - - size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, - opblk, 12+buflen, resblk, sizeof(resblk)); - - kfree(opblk); - if(size>buflen) - return buflen; - return size; -} - -/* - * if oper == I2O_PARAMS_TABLE_GET, get from all rows - * if fieldcount == -1 return all fields - * ibuf and ibuflen are unused (use NULL, 0) - * else return specific fields - * ibuf contains fieldindexes - * - * if oper == I2O_PARAMS_LIST_GET, get from specific rows - * if fieldcount == -1 return all fields - * ibuf contains rowcount, keyvalues - * else return specific fields - * fieldcount is # of fieldindexes - * ibuf contains fieldindexes, rowcount, keyvalues - * - * You could also use directly function i2o_issue_params(). - */ -int i2o_query_table(int oper, struct i2o_controller *iop, int tid, int group, - int fieldcount, void *ibuf, int ibuflen, - void *resblk, int reslen) -{ - u16 *opblk; - int size; - - opblk = kmalloc(10 + ibuflen, GFP_KERNEL); - if (opblk == NULL) - { - printk(KERN_ERR "i2o: no memory for query buffer.\n"); - return -ENOMEM; - } - - opblk[0] = 1; /* operation count */ - opblk[1] = 0; /* pad */ - opblk[2] = oper; - opblk[3] = group; - opblk[4] = fieldcount; - memcpy(opblk+5, ibuf, ibuflen); /* other params */ - - size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET,iop, tid, - opblk, 10+ibuflen, resblk, reslen); - - kfree(opblk); - if(size>reslen) - return reslen; - return size; -} - -/* - * Clear table group, i.e. delete all rows. - */ -int i2o_clear_table(struct i2o_controller *iop, int tid, int group) -{ - u16 opblk[] = { 1, 0, I2O_PARAMS_TABLE_CLEAR, group }; - u8 resblk[32]; /* min 8 bytes for result header */ - - return i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, - opblk, sizeof(opblk), resblk, sizeof(resblk)); -} - -/* - * Add a new row into a table group. - * - * if fieldcount==-1 then we add whole rows - * buf contains rowcount, keyvalues - * else just specific fields are given, rest use defaults - * buf contains fieldindexes, rowcount, keyvalues - */ -int i2o_row_add_table(struct i2o_controller *iop, int tid, - int group, int fieldcount, void *buf, int buflen) -{ - u16 *opblk; - u8 resblk[32]; /* min 8 bytes for header */ - int size; - - opblk = kmalloc(buflen+64, GFP_KERNEL); - if (opblk == NULL) - { - printk(KERN_ERR "i2o: no memory for operation buffer.\n"); - return -ENOMEM; - } - - opblk[0] = 1; /* operation count */ - opblk[1] = 0; /* pad */ - opblk[2] = I2O_PARAMS_ROW_ADD; - opblk[3] = group; - opblk[4] = fieldcount; - memcpy(opblk+5, buf, buflen); - - size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, - opblk, 10+buflen, resblk, sizeof(resblk)); - - kfree(opblk); - if(size>buflen) - return buflen; - return size; -} - - -/* - * Used for error reporting/debugging purposes. - * Following fail status are common to all classes. - * The preserved message must be handled in the reply handler. - */ -void i2o_report_fail_status(u8 req_status, u32* msg) -{ - static char *FAIL_STATUS[] = { - "0x80", /* not used */ - "SERVICE_SUSPENDED", /* 0x81 */ - "SERVICE_TERMINATED", /* 0x82 */ - "CONGESTION", - "FAILURE", - "STATE_ERROR", - "TIME_OUT", - "ROUTING_FAILURE", - "INVALID_VERSION", - "INVALID_OFFSET", - "INVALID_MSG_FLAGS", - "FRAME_TOO_SMALL", - "FRAME_TOO_LARGE", - "INVALID_TARGET_ID", - "INVALID_INITIATOR_ID", - "INVALID_INITIATOR_CONTEX", /* 0x8F */ - "UNKNOWN_FAILURE" /* 0xFF */ - }; - - if (req_status == I2O_FSC_TRANSPORT_UNKNOWN_FAILURE) - printk("TRANSPORT_UNKNOWN_FAILURE (%0#2x)\n.", req_status); - else - printk("TRANSPORT_%s.\n", FAIL_STATUS[req_status & 0x0F]); - - /* Dump some details */ - - printk(KERN_ERR " InitiatorId = %d, TargetId = %d\n", - (msg[1] >> 12) & 0xFFF, msg[1] & 0xFFF); - printk(KERN_ERR " LowestVersion = 0x%02X, HighestVersion = 0x%02X\n", - (msg[4] >> 8) & 0xFF, msg[4] & 0xFF); - printk(KERN_ERR " FailingHostUnit = 0x%04X, FailingIOP = 0x%03X\n", - msg[5] >> 16, msg[5] & 0xFFF); - - printk(KERN_ERR " Severity: 0x%02X ", (msg[4] >> 16) & 0xFF); - if (msg[4] & (1<<16)) - printk("(FormatError), " - "this msg can never be delivered/processed.\n"); - if (msg[4] & (1<<17)) - printk("(PathError), " - "this msg can no longer be delivered/processed.\n"); - if (msg[4] & (1<<18)) - printk("(PathState), " - "the system state does not allow delivery.\n"); - if (msg[4] & (1<<19)) - printk("(Congestion), resources temporarily not available;" - "do not retry immediately.\n"); -} - -/* - * Used for error reporting/debugging purposes. - * Following reply status are common to all classes. - */ -void i2o_report_common_status(u8 req_status) -{ - static char *REPLY_STATUS[] = { - "SUCCESS", - "ABORT_DIRTY", - "ABORT_NO_DATA_TRANSFER", - "ABORT_PARTIAL_TRANSFER", - "ERROR_DIRTY", - "ERROR_NO_DATA_TRANSFER", - "ERROR_PARTIAL_TRANSFER", - "PROCESS_ABORT_DIRTY", - "PROCESS_ABORT_NO_DATA_TRANSFER", - "PROCESS_ABORT_PARTIAL_TRANSFER", - "TRANSACTION_ERROR", - "PROGRESS_REPORT" - }; - - if (req_status > I2O_REPLY_STATUS_PROGRESS_REPORT) - printk("RequestStatus = %0#2x", req_status); - else - printk("%s", REPLY_STATUS[req_status]); -} - -/* - * Used for error reporting/debugging purposes. - * Following detailed status are valid for executive class, - * utility class, DDM class and for transaction error replies. - */ -static void i2o_report_common_dsc(u16 detailed_status) -{ - static char *COMMON_DSC[] = { - "SUCCESS", - "0x01", // not used - "BAD_KEY", - "TCL_ERROR", - "REPLY_BUFFER_FULL", - "NO_SUCH_PAGE", - "INSUFFICIENT_RESOURCE_SOFT", - "INSUFFICIENT_RESOURCE_HARD", - "0x08", // not used - "CHAIN_BUFFER_TOO_LARGE", - "UNSUPPORTED_FUNCTION", - "DEVICE_LOCKED", - "DEVICE_RESET", - "INAPPROPRIATE_FUNCTION", - "INVALID_INITIATOR_ADDRESS", - "INVALID_MESSAGE_FLAGS", - "INVALID_OFFSET", - "INVALID_PARAMETER", - "INVALID_REQUEST", - "INVALID_TARGET_ADDRESS", - "MESSAGE_TOO_LARGE", - "MESSAGE_TOO_SMALL", - "MISSING_PARAMETER", - "TIMEOUT", - "UNKNOWN_ERROR", - "UNKNOWN_FUNCTION", - "UNSUPPORTED_VERSION", - "DEVICE_BUSY", - "DEVICE_NOT_AVAILABLE" - }; - - if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE) - printk(" / DetailedStatus = %0#4x.\n", detailed_status); - else - printk(" / %s.\n", COMMON_DSC[detailed_status]); -} - -/* - * Used for error reporting/debugging purposes - */ -static void i2o_report_lan_dsc(u16 detailed_status) -{ - static char *LAN_DSC[] = { // Lan detailed status code strings - "SUCCESS", - "DEVICE_FAILURE", - "DESTINATION_NOT_FOUND", - "TRANSMIT_ERROR", - "TRANSMIT_ABORTED", - "RECEIVE_ERROR", - "RECEIVE_ABORTED", - "DMA_ERROR", - "BAD_PACKET_DETECTED", - "OUT_OF_MEMORY", - "BUCKET_OVERRUN", - "IOP_INTERNAL_ERROR", - "CANCELED", - "INVALID_TRANSACTION_CONTEXT", - "DEST_ADDRESS_DETECTED", - "DEST_ADDRESS_OMITTED", - "PARTIAL_PACKET_RETURNED", - "TEMP_SUSPENDED_STATE", // last Lan detailed status code - "INVALID_REQUEST" // general detailed status code - }; - - if (detailed_status > I2O_DSC_INVALID_REQUEST) - printk(" / %0#4x.\n", detailed_status); - else - printk(" / %s.\n", LAN_DSC[detailed_status]); -} - -/* - * Used for error reporting/debugging purposes - */ -static void i2o_report_util_cmd(u8 cmd) -{ - switch (cmd) { - case I2O_CMD_UTIL_NOP: - printk("UTIL_NOP, "); - break; - case I2O_CMD_UTIL_ABORT: - printk("UTIL_ABORT, "); - break; - case I2O_CMD_UTIL_CLAIM: - printk("UTIL_CLAIM, "); - break; - case I2O_CMD_UTIL_RELEASE: - printk("UTIL_CLAIM_RELEASE, "); - break; - case I2O_CMD_UTIL_CONFIG_DIALOG: - printk("UTIL_CONFIG_DIALOG, "); - break; - case I2O_CMD_UTIL_DEVICE_RESERVE: - printk("UTIL_DEVICE_RESERVE, "); - break; - case I2O_CMD_UTIL_DEVICE_RELEASE: - printk("UTIL_DEVICE_RELEASE, "); - break; - case I2O_CMD_UTIL_EVT_ACK: - printk("UTIL_EVENT_ACKNOWLEDGE, "); - break; - case I2O_CMD_UTIL_EVT_REGISTER: - printk("UTIL_EVENT_REGISTER, "); - break; - case I2O_CMD_UTIL_LOCK: - printk("UTIL_LOCK, "); - break; - case I2O_CMD_UTIL_LOCK_RELEASE: - printk("UTIL_LOCK_RELEASE, "); - break; - case I2O_CMD_UTIL_PARAMS_GET: - printk("UTIL_PARAMS_GET, "); - break; - case I2O_CMD_UTIL_PARAMS_SET: - printk("UTIL_PARAMS_SET, "); - break; - case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY: - printk("UTIL_REPLY_FAULT_NOTIFY, "); - break; - default: - printk("Cmd = %0#2x, ",cmd); - } -} - -/* - * Used for error reporting/debugging purposes - */ -static void i2o_report_exec_cmd(u8 cmd) -{ - switch (cmd) { - case I2O_CMD_ADAPTER_ASSIGN: - printk("EXEC_ADAPTER_ASSIGN, "); - break; - case I2O_CMD_ADAPTER_READ: - printk("EXEC_ADAPTER_READ, "); - break; - case I2O_CMD_ADAPTER_RELEASE: - printk("EXEC_ADAPTER_RELEASE, "); - break; - case I2O_CMD_BIOS_INFO_SET: - printk("EXEC_BIOS_INFO_SET, "); - break; - case I2O_CMD_BOOT_DEVICE_SET: - printk("EXEC_BOOT_DEVICE_SET, "); - break; - case I2O_CMD_CONFIG_VALIDATE: - printk("EXEC_CONFIG_VALIDATE, "); - break; - case I2O_CMD_CONN_SETUP: - printk("EXEC_CONN_SETUP, "); - break; - case I2O_CMD_DDM_DESTROY: - printk("EXEC_DDM_DESTROY, "); - break; - case I2O_CMD_DDM_ENABLE: - printk("EXEC_DDM_ENABLE, "); - break; - case I2O_CMD_DDM_QUIESCE: - printk("EXEC_DDM_QUIESCE, "); - break; - case I2O_CMD_DDM_RESET: - printk("EXEC_DDM_RESET, "); - break; - case I2O_CMD_DDM_SUSPEND: - printk("EXEC_DDM_SUSPEND, "); - break; - case I2O_CMD_DEVICE_ASSIGN: - printk("EXEC_DEVICE_ASSIGN, "); - break; - case I2O_CMD_DEVICE_RELEASE: - printk("EXEC_DEVICE_RELEASE, "); - break; - case I2O_CMD_HRT_GET: - printk("EXEC_HRT_GET, "); - break; - case I2O_CMD_ADAPTER_CLEAR: - printk("EXEC_IOP_CLEAR, "); - break; - case I2O_CMD_ADAPTER_CONNECT: - printk("EXEC_IOP_CONNECT, "); - break; - case I2O_CMD_ADAPTER_RESET: - printk("EXEC_IOP_RESET, "); - break; - case I2O_CMD_LCT_NOTIFY: - printk("EXEC_LCT_NOTIFY, "); - break; - case I2O_CMD_OUTBOUND_INIT: - printk("EXEC_OUTBOUND_INIT, "); - break; - case I2O_CMD_PATH_ENABLE: - printk("EXEC_PATH_ENABLE, "); - break; - case I2O_CMD_PATH_QUIESCE: - printk("EXEC_PATH_QUIESCE, "); - break; - case I2O_CMD_PATH_RESET: - printk("EXEC_PATH_RESET, "); - break; - case I2O_CMD_STATIC_MF_CREATE: - printk("EXEC_STATIC_MF_CREATE, "); - break; - case I2O_CMD_STATIC_MF_RELEASE: - printk("EXEC_STATIC_MF_RELEASE, "); - break; - case I2O_CMD_STATUS_GET: - printk("EXEC_STATUS_GET, "); - break; - case I2O_CMD_SW_DOWNLOAD: - printk("EXEC_SW_DOWNLOAD, "); - break; - case I2O_CMD_SW_UPLOAD: - printk("EXEC_SW_UPLOAD, "); - break; - case I2O_CMD_SW_REMOVE: - printk("EXEC_SW_REMOVE, "); - break; - case I2O_CMD_SYS_ENABLE: - printk("EXEC_SYS_ENABLE, "); - break; - case I2O_CMD_SYS_MODIFY: - printk("EXEC_SYS_MODIFY, "); - break; - case I2O_CMD_SYS_QUIESCE: - printk("EXEC_SYS_QUIESCE, "); - break; - case I2O_CMD_SYS_TAB_SET: - printk("EXEC_SYS_TAB_SET, "); - break; - default: - printk("Cmd = %#02x, ",cmd); - } -} - -/* - * Used for error reporting/debugging purposes - */ -static void i2o_report_lan_cmd(u8 cmd) -{ - switch (cmd) { - case LAN_PACKET_SEND: - printk("LAN_PACKET_SEND, "); - break; - case LAN_SDU_SEND: - printk("LAN_SDU_SEND, "); - break; - case LAN_RECEIVE_POST: - printk("LAN_RECEIVE_POST, "); - break; - case LAN_RESET: - printk("LAN_RESET, "); - break; - case LAN_SUSPEND: - printk("LAN_SUSPEND, "); - break; - default: - printk("Cmd = %0#2x, ",cmd); - } -} - -/* - * Used for error reporting/debugging purposes. - * Report Cmd name, Request status, Detailed Status. - */ -void i2o_report_status(const char *severity, const char *str, u32 *msg) -{ - u8 cmd = (msg[1]>>24)&0xFF; - u8 req_status = (msg[4]>>24)&0xFF; - u16 detailed_status = msg[4]&0xFFFF; - struct i2o_handler *h = i2o_handlers[msg[2] & (MAX_I2O_MODULES-1)]; - - printk("%s%s: ", severity, str); - - if (cmd < 0x1F) // Utility cmd - i2o_report_util_cmd(cmd); - - else if (cmd >= 0xA0 && cmd <= 0xEF) // Executive cmd - i2o_report_exec_cmd(cmd); - - else if (h->class == I2O_CLASS_LAN && cmd >= 0x30 && cmd <= 0x3F) - i2o_report_lan_cmd(cmd); // LAN cmd - else - printk("Cmd = %0#2x, ", cmd); // Other cmds - - if (msg[0] & MSG_FAIL) { - i2o_report_fail_status(req_status, msg); - return; - } - - i2o_report_common_status(req_status); - - if (cmd < 0x1F || (cmd >= 0xA0 && cmd <= 0xEF)) - i2o_report_common_dsc(detailed_status); - else if (h->class == I2O_CLASS_LAN && cmd >= 0x30 && cmd <= 0x3F) - i2o_report_lan_dsc(detailed_status); - else - printk(" / DetailedStatus = %0#4x.\n", detailed_status); -} - -/* Used to dump a message to syslog during debugging */ -void i2o_dump_message(u32 *msg) -{ -#ifdef DRIVERDEBUG - int i; - printk(KERN_INFO "Dumping I2O message size %d @ %p\n", - msg[0]>>16&0xffff, msg); - for(i = 0; i < ((msg[0]>>16)&0xffff); i++) - printk(KERN_INFO " msg[%d] = %0#10x\n", i, msg[i]); -#endif -} - -/* - * I2O reboot/shutdown notification. - * - * - Call each OSM's reboot notifier (if one exists) - * - Quiesce each IOP in the system - * - * Each IOP has to be quiesced before we can ensure that the system - * can be properly shutdown as a transaction that has already been - * acknowledged still needs to be placed in permanent store on the IOP. - * The SysQuiesce causes the IOP to force all HDMs to complete their - * transactions before returning, so only at that point is it safe - * - */ -static int i2o_reboot_event(struct notifier_block *n, unsigned long code, void -*p) -{ - int i = 0; - struct i2o_controller *c = NULL; - - if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF) - return NOTIFY_DONE; - - printk(KERN_INFO "Shutting down I2O system.\n"); - printk(KERN_INFO - " This could take a few minutes if there are many devices attached\n"); - - for(i = 0; i < MAX_I2O_MODULES; i++) - { - if(i2o_handlers[i] && i2o_handlers[i]->reboot_notify) - i2o_handlers[i]->reboot_notify(); - } - - for(c = i2o_controller_chain; c; c = c->next) - { - if(i2o_quiesce_controller(c)) - { - printk(KERN_WARNING "i2o: Could not quiesce %s." " - Verify setup on next system power up.\n", c->name); - } - } - - printk(KERN_INFO "I2O system down.\n"); - return NOTIFY_DONE; -} - - -EXPORT_SYMBOL(i2o_controller_chain); -EXPORT_SYMBOL(i2o_num_controllers); -EXPORT_SYMBOL(i2o_find_controller); -EXPORT_SYMBOL(i2o_unlock_controller); -EXPORT_SYMBOL(i2o_status_get); - -EXPORT_SYMBOL(i2o_install_handler); -EXPORT_SYMBOL(i2o_remove_handler); - -EXPORT_SYMBOL(i2o_claim_device); -EXPORT_SYMBOL(i2o_release_device); -EXPORT_SYMBOL(i2o_device_notify_on); -EXPORT_SYMBOL(i2o_device_notify_off); - -EXPORT_SYMBOL(i2o_post_this); -EXPORT_SYMBOL(i2o_post_wait); -EXPORT_SYMBOL(i2o_post_wait_mem); - -EXPORT_SYMBOL(i2o_query_scalar); -EXPORT_SYMBOL(i2o_set_scalar); -EXPORT_SYMBOL(i2o_query_table); -EXPORT_SYMBOL(i2o_clear_table); -EXPORT_SYMBOL(i2o_row_add_table); -EXPORT_SYMBOL(i2o_issue_params); - -EXPORT_SYMBOL(i2o_event_register); -EXPORT_SYMBOL(i2o_event_ack); - -EXPORT_SYMBOL(i2o_report_status); -EXPORT_SYMBOL(i2o_dump_message); - -EXPORT_SYMBOL(i2o_get_class_name); - -#ifdef MODULE - -MODULE_AUTHOR("Red Hat Software"); -MODULE_DESCRIPTION("I2O Core"); - - -int init_module(void) -{ - printk(KERN_INFO "I2O Core - (C) Copyright 1999 Red Hat Software\n"); - if (i2o_install_handler(&i2o_core_handler) < 0) - { - printk(KERN_ERR - "i2o_core: Unable to install core handler.\nI2O stack not loaded!"); - return 0; - } - - core_context = i2o_core_handler.context; - - /* - * Attach core to I2O PCI transport (and others as they are developed) - */ -#ifdef CONFIG_I2O_PCI_MODULE - if(i2o_pci_core_attach(&i2o_core_functions) < 0) - printk(KERN_INFO "i2o: No PCI I2O controllers found\n"); -#endif - - /* - * Initialize event handling thread - */ - init_MUTEX_LOCKED(&evt_sem); - evt_pid = kernel_thread(i2o_core_evt, &evt_reply, CLONE_SIGHAND); - if(evt_pid < 0) - { - printk(KERN_ERR "I2O: Could not create event handler kernel thread\n"); - i2o_remove_handler(&i2o_core_handler); - return 0; - } - else - printk(KERN_INFO "I2O: Event thread created as pid %d\n", evt_pid); - - if(i2o_num_controllers) - i2o_sys_init(); - - register_reboot_notifier(&i2o_reboot_notifier); - - return 0; -} - -void cleanup_module(void) -{ - int stat; - - unregister_reboot_notifier(&i2o_reboot_notifier); - - if(i2o_num_controllers) - i2o_sys_shutdown(); - - /* - * If this is shutdown time, the thread has already been killed - */ - if(evt_running) { - printk("Terminating i2o threads..."); - stat = kill_proc(evt_pid, SIGTERM, 1); - if(!stat) { - printk("waiting..."); - down(&evt_dead); - } - printk("done.\n"); - } - -#ifdef CONFIG_I2O_PCI_MODULE - i2o_pci_core_detach(); -#endif - - i2o_remove_handler(&i2o_core_handler); - - unregister_reboot_notifier(&i2o_reboot_notifier); -} - -#else - -extern int i2o_block_init(void); -extern int i2o_config_init(void); -extern int i2o_lan_init(void); -extern int i2o_pci_init(void); -extern int i2o_proc_init(void); -extern int i2o_scsi_init(void); - -int __init i2o_init(void) -{ - printk(KERN_INFO "Loading I2O Core - (c) Copyright 1999 Red Hat Software\n"); - - if (i2o_install_handler(&i2o_core_handler) < 0) - { - printk(KERN_ERR - "i2o_core: Unable to install core handler.\nI2O stack not loaded!"); - return 0; - } - - core_context = i2o_core_handler.context; - - /* - * Initialize event handling thread - * We may not find any controllers, but still want this as - * down the road we may have hot pluggable controllers that - * need to be dealt with. - */ - init_MUTEX_LOCKED(&evt_sem); - if((evt_pid = kernel_thread(i2o_core_evt, &evt_reply, CLONE_SIGHAND)) < 0) - { - printk(KERN_ERR "I2O: Could not create event handler kernel thread\n"); - i2o_remove_handler(&i2o_core_handler); - return 0; - } - - -#ifdef CONFIG_I2O_PCI - i2o_pci_init(); -#endif - - if(i2o_num_controllers) - i2o_sys_init(); - - register_reboot_notifier(&i2o_reboot_notifier); - - i2o_config_init(); -#ifdef CONFIG_I2O_BLOCK - i2o_block_init(); -#endif -#ifdef CONFIG_I2O_LAN - i2o_lan_init(); -#endif -#ifdef CONFIG_I2O_PROC - i2o_proc_init(); -#endif - return 0; -} - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_lan.c linux.ac/drivers/i2o/i2o_lan.c --- linux.vanilla/drivers/i2o/i2o_lan.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/i2o/i2o_lan.c Thu Jan 1 01:00:00 1970 @@ -1,1575 +0,0 @@ -/* - * drivers/i2o/i2o_lan.c - * - * I2O LAN CLASS OSM May 26th 2000 - * - * (C) Copyright 1999, 2000 University of Helsinki, - * Department of Computer Science - * - * This code is still under development / test. - * - * 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. - * - * Authors: Auvo Häkkinen - * Fixes: Juha Sievänen - * Taneli Vähäkangas - * Deepak Saxena - * - * Tested: in FDDI environment (using SysKonnect's DDM) - * in Gigabit Eth environment (using SysKonnect's DDM) - * in Fast Ethernet environment (using Intel 82558 DDM) - * - * TODO: tests for other LAN classes (Token Ring, Fibre Channel) - */ - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include "i2o_lan.h" - -//#define DRIVERDEBUG -#ifdef DRIVERDEBUG -#define dprintk(s, args...) printk(s, ## args) -#else -#define dprintk(s, args...) -#endif - -/* The following module parameters are used as default values - * for per interface values located in the net_device private area. - * Private values are changed via /proc filesystem. - */ -static u32 max_buckets_out = I2O_LAN_MAX_BUCKETS_OUT; -static u32 bucket_thresh = I2O_LAN_BUCKET_THRESH; -static u32 rx_copybreak = I2O_LAN_RX_COPYBREAK; -static u8 tx_batch_mode = I2O_LAN_TX_BATCH_MODE; -static u32 i2o_event_mask = I2O_LAN_EVENT_MASK; - -#define MAX_LAN_CARDS 16 -static struct net_device *i2o_landevs[MAX_LAN_CARDS+1]; -static int unit = -1; /* device unit number */ - -static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); -static void i2o_lan_send_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); -static int i2o_lan_receive_post(struct net_device *dev); -static void i2o_lan_receive_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); -static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg); - -static int i2o_lan_reset(struct net_device *dev); -static void i2o_lan_handle_event(struct net_device *dev, u32 *msg); - -/* Structures to register handlers for the incoming replies. */ - -static struct i2o_handler i2o_lan_send_handler = { - i2o_lan_send_post_reply, // For send replies - NULL, - NULL, - NULL, - "I2O LAN OSM send", - -1, - I2O_CLASS_LAN -}; -static int lan_send_context; - -static struct i2o_handler i2o_lan_receive_handler = { - i2o_lan_receive_post_reply, // For receive replies - NULL, - NULL, - NULL, - "I2O LAN OSM receive", - -1, - I2O_CLASS_LAN -}; -static int lan_receive_context; - -static struct i2o_handler i2o_lan_handler = { - i2o_lan_reply, // For other replies - NULL, - NULL, - NULL, - "I2O LAN OSM", - -1, - I2O_CLASS_LAN -}; -static int lan_context; - -DECLARE_TASK_QUEUE(i2o_post_buckets_task); -struct tq_struct run_i2o_post_buckets_task = { - routine: (void (*)(void *)) run_task_queue, - data: (void *) 0 -}; - -/* Functions to handle message failures and transaction errors: -==============================================================*/ - -/* - * i2o_lan_handle_failure(): Fail bit has been set since IOP's message - * layer cannot deliver the request to the target, or the target cannot - * process the request. - */ -static void i2o_lan_handle_failure(struct net_device *dev, u32 *msg) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - - u32 *preserved_msg = (u32*)(iop->mem_offset + msg[7]); - u32 *sgl_elem = &preserved_msg[4]; - struct sk_buff *skb = NULL; - u8 le_flag; - - i2o_report_status(KERN_INFO, dev->name, msg); - - /* If PacketSend failed, free sk_buffs reserved by upper layers */ - - if (msg[1] >> 24 == LAN_PACKET_SEND) { - do { - skb = (struct sk_buff *)(sgl_elem[1]); - dev_kfree_skb_irq(skb); - - atomic_dec(&priv->tx_out); - - le_flag = *sgl_elem >> 31; - sgl_elem +=3; - } while (le_flag == 0); /* Last element flag not set */ - - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); - } - - /* If ReceivePost failed, free sk_buffs we have reserved */ - - if (msg[1] >> 24 == LAN_RECEIVE_POST) { - do { - skb = (struct sk_buff *)(sgl_elem[1]); - dev_kfree_skb_irq(skb); - - atomic_dec(&priv->buckets_out); - - le_flag = *sgl_elem >> 31; - sgl_elem +=3; - } while (le_flag == 0); /* Last element flag not set */ - } - - /* Release the preserved msg frame by resubmitting it as a NOP */ - - preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; - preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; - preserved_msg[2] = 0; - i2o_post_message(iop, msg[7]); -} -/* - * i2o_lan_handle_transaction_error(): IOP or DDM has rejected the request - * for general cause (format error, bad function code, insufficient resources, - * etc.). We get one transaction_error for each failed transaction. - */ -static void i2o_lan_handle_transaction_error(struct net_device *dev, u32 *msg) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct sk_buff *skb; - - i2o_report_status(KERN_INFO, dev->name, msg); - - /* If PacketSend was rejected, free sk_buff reserved by upper layers */ - - if (msg[1] >> 24 == LAN_PACKET_SEND) { - skb = (struct sk_buff *)(msg[3]); // TransactionContext - dev_kfree_skb_irq(skb); - atomic_dec(&priv->tx_out); - - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); - } - - /* If ReceivePost was rejected, free sk_buff we have reserved */ - - if (msg[1] >> 24 == LAN_RECEIVE_POST) { - skb = (struct sk_buff *)(msg[3]); - dev_kfree_skb_irq(skb); - atomic_dec(&priv->buckets_out); - } -} - -/* - * i2o_lan_handle_status(): Common parts of handling a not succeeded request - * (status != SUCCESS). - */ -static int i2o_lan_handle_status(struct net_device *dev, u32 *msg) -{ - /* Fail bit set? */ - - if (msg[0] & MSG_FAIL) { - i2o_lan_handle_failure(dev, msg); - return -1; - } - - /* Message rejected for general cause? */ - - if ((msg[4]>>24) == I2O_REPLY_STATUS_TRANSACTION_ERROR) { - i2o_lan_handle_transaction_error(dev, msg); - return -1; - } - - /* Else have to handle it in the callback function */ - - return 0; -} - -/* Callback functions called from the interrupt routine: -=======================================================*/ - -/* - * i2o_lan_send_post_reply(): Callback function to handle PostSend replies. - */ -static void i2o_lan_send_post_reply(struct i2o_handler *h, - struct i2o_controller *iop, struct i2o_message *m) -{ - u32 *msg = (u32 *)m; - u8 unit = (u8)(msg[2]>>16); // InitiatorContext - struct net_device *dev = i2o_landevs[unit]; - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - u8 trl_count = msg[3] & 0x000000FF; - - if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { - if (i2o_lan_handle_status(dev, msg)) - return; - } - -#ifdef DRIVERDEBUG - i2o_report_status(KERN_INFO, dev->name, msg); -#endif - - /* DDM has handled transmit request(s), free sk_buffs. - * We get similar single transaction reply also in error cases - * (except if msg failure or transaction error). - */ - while (trl_count) { - dev_kfree_skb_irq((struct sk_buff *)msg[4 + trl_count]); - dprintk(KERN_INFO "%s: tx skb freed (trl_count=%d).\n", - dev->name, trl_count); - atomic_dec(&priv->tx_out); - trl_count--; - } - - /* If priv->tx_out had reached tx_max_out, the queue was stopped */ - - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); -} - -/* - * i2o_lan_receive_post_reply(): Callback function to process incoming packets. - */ -static void i2o_lan_receive_post_reply(struct i2o_handler *h, - struct i2o_controller *iop, struct i2o_message *m) -{ - u32 *msg = (u32 *)m; - u8 unit = (u8)(msg[2]>>16); // InitiatorContext - struct net_device *dev = i2o_landevs[unit]; - - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_bucket_descriptor *bucket = (struct i2o_bucket_descriptor *)&msg[6]; - struct i2o_packet_info *packet; - u8 trl_count = msg[3] & 0x000000FF; - struct sk_buff *skb, *old_skb; - unsigned long flags = 0; - - if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { - if (i2o_lan_handle_status(dev, msg)) - return; - - i2o_lan_release_buckets(dev, msg); - return; - } - -#ifdef DRIVERDEBUG - i2o_report_status(KERN_INFO, dev->name, msg); -#endif - - /* Else we are receiving incoming post. */ - - while (trl_count--) { - skb = (struct sk_buff *)bucket->context; - packet = (struct i2o_packet_info *)bucket->packet_info; - atomic_dec(&priv->buckets_out); - - /* Sanity checks: Any weird characteristics in bucket? */ - - if (packet->flags & 0x0f || ! packet->flags & 0x40) { - if (packet->flags & 0x01) - printk(KERN_WARNING "%s: packet with errors, error code=0x%02x.\n", - dev->name, packet->status & 0xff); - - /* The following shouldn't happen, unless parameters in - * LAN_OPERATION group are changed during the run time. - */ - if (packet->flags & 0x0c) - printk(KERN_DEBUG "%s: multi-bucket packets not supported!\n", - dev->name); - - if (! packet->flags & 0x40) - printk(KERN_DEBUG "%s: multiple packets in a bucket not supported!\n", - dev->name); - - dev_kfree_skb_irq(skb); - - bucket++; - continue; - } - - /* Copy short packet to a new skb */ - - if (packet->len < priv->rx_copybreak) { - old_skb = skb; - skb = (struct sk_buff *)dev_alloc_skb(packet->len+2); - if (skb == NULL) { - printk(KERN_ERR "%s: Can't allocate skb.\n", dev->name); - return; - } - skb_reserve(skb, 2); - memcpy(skb_put(skb, packet->len), old_skb->data, packet->len); - - spin_lock_irqsave(&priv->fbl_lock, flags); - if (priv->i2o_fbl_tail < I2O_LAN_MAX_BUCKETS_OUT) - priv->i2o_fbl[++priv->i2o_fbl_tail] = old_skb; - else - dev_kfree_skb_irq(old_skb); - - spin_unlock_irqrestore(&priv->fbl_lock, flags); - } else - skb_put(skb, packet->len); - - /* Deliver to upper layers */ - - skb->dev = dev; - skb->protocol = priv->type_trans(skb, dev); - netif_rx(skb); - - dev->last_rx = jiffies; - - dprintk(KERN_INFO "%s: Incoming packet (%d bytes) delivered " - "to upper level.\n", dev->name, packet->len); - - bucket++; // to next Packet Descriptor Block - } - -#ifdef DRIVERDEBUG - if (msg[5] == 0) - printk(KERN_INFO "%s: DDM out of buckets (priv->count = %d)!\n", - dev->name, atomic_read(&priv->buckets_out)); -#endif - - /* If DDM has already consumed bucket_thresh buckets, post new ones */ - - if (atomic_read(&priv->buckets_out) <= priv->max_buckets_out - priv->bucket_thresh) { - run_i2o_post_buckets_task.data = (void *)dev; - queue_task(&run_i2o_post_buckets_task, &tq_immediate); - mark_bh(IMMEDIATE_BH); - } - - return; -} - -/* - * i2o_lan_reply(): Callback function to handle other incoming messages - * except SendPost and ReceivePost. - */ -static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, - struct i2o_message *m) -{ - u32 *msg = (u32 *)m; - u8 unit = (u8)(msg[2]>>16); // InitiatorContext - struct net_device *dev = i2o_landevs[unit]; - - if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { - if (i2o_lan_handle_status(dev, msg)) - return; - - /* In other error cases just report and continue */ - - i2o_report_status(KERN_INFO, dev->name, msg); - } - -#ifdef DRIVERDEBUG - i2o_report_status(KERN_INFO, dev->name, msg); -#endif - switch (msg[1] >> 24) { - case LAN_RESET: - case LAN_SUSPEND: - /* default reply without payload */ - break; - - case I2O_CMD_UTIL_EVT_REGISTER: - case I2O_CMD_UTIL_EVT_ACK: - i2o_lan_handle_event(dev, msg); - break; - - case I2O_CMD_UTIL_PARAMS_SET: - /* default reply, results in ReplyPayload (not examined) */ - switch (msg[3] >> 16) { - case 1: dprintk(KERN_INFO "%s: Reply to set MAC filter mask.\n", - dev->name); - break; - case 2: dprintk(KERN_INFO "%s: Reply to set MAC table.\n", - dev->name); - break; - default: printk(KERN_WARNING "%s: Bad group 0x%04X\n", - dev->name,msg[3] >> 16); - } - break; - - default: - printk(KERN_ERR "%s: No handler for the reply.\n", - dev->name); - i2o_report_status(KERN_INFO, dev->name, msg); - } -} - -/* Functions used by the above callback functions: -=================================================*/ -/* - * i2o_lan_release_buckets(): Free unused buckets (sk_buffs). - */ -static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - u8 trl_elem_size = (u8)(msg[3]>>8 & 0x000000FF); - u8 trl_count = (u8)(msg[3] & 0x000000FF); - u32 *pskb = &msg[6]; - - while (trl_count--) { - dprintk(KERN_DEBUG "%s: Releasing unused rx skb %p (trl_count=%d).\n", - dev->name, (struct sk_buff*)(*pskb),trl_count+1); - dev_kfree_skb_irq((struct sk_buff *)(*pskb)); - pskb += 1 + trl_elem_size; - atomic_dec(&priv->buckets_out); - } -} - -/* - * i2o_lan_event_reply(): Handle events. - */ -static void i2o_lan_handle_event(struct net_device *dev, u32 *msg) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 max_evt_data_size =iop->status_block->inbound_frame_size-5; - struct i2o_reply { - u32 header[4]; - u32 evt_indicator; - u32 data[max_evt_data_size]; - } *evt = (struct i2o_reply *)msg; - int evt_data_len = ((msg[0]>>16) - 5) * 4; /* real size*/ - - printk(KERN_INFO "%s: I2O event - ", dev->name); - - if (msg[1]>>24 == I2O_CMD_UTIL_EVT_ACK) { - printk("Event acknowledgement reply.\n"); - return; - } - - /* Else evt->function == I2O_CMD_UTIL_EVT_REGISTER) */ - - switch (evt->evt_indicator) { - case I2O_EVT_IND_STATE_CHANGE: { - struct state_data { - u16 status; - u8 state; - u8 data; - } *evt_data = (struct state_data *)(evt->data[0]); - - printk("State chance 0x%08x.\n", evt->data[0]); - - /* If the DDM is in error state, recovery may be - * possible if status = Transmit or Receive Control - * Unit Inoperable. - */ - if (evt_data->state==0x05 && evt_data->status==0x0003) - i2o_lan_reset(dev); - break; - } - - case I2O_EVT_IND_FIELD_MODIFIED: { - u16 *work16 = (u16 *)evt->data; - printk("Group 0x%04x, field %d changed.\n", work16[0], work16[1]); - break; - } - - case I2O_EVT_IND_VENDOR_EVT: { - int i; - printk("Vendor event:\n"); - for (i = 0; i < evt_data_len / 4; i++) - printk(" 0x%08x\n", evt->data[i]); - break; - } - - case I2O_EVT_IND_DEVICE_RESET: - /* Spec 2.0 p. 6-121: - * The event of _DEVICE_RESET should also be responded - */ - printk("Device reset.\n"); - if (i2o_event_ack(iop, msg) < 0) - printk("%s: Event Acknowledge timeout.\n", dev->name); - break; - -#if 0 - case I2O_EVT_IND_EVT_MASK_MODIFIED: - printk("Event mask modified, 0x%08x.\n", evt->data[0]); - break; - - case I2O_EVT_IND_GENERAL_WARNING: - printk("General warning 0x%04x.\n", evt->data[0]); - break; - - case I2O_EVT_IND_CONFIGURATION_FLAG: - printk("Configuration requested.\n"); - break; - - case I2O_EVT_IND_CAPABILITY_CHANGE: - printk("Capability change 0x%04x.\n", evt->data[0]); - break; - - case I2O_EVT_IND_DEVICE_STATE: - printk("Device state changed 0x%08x.\n", evt->data[0]); - break; -#endif - case I2O_LAN_EVT_LINK_DOWN: - netif_carrier_off(dev); - printk("Link to the physical device is lost.\n"); - break; - - case I2O_LAN_EVT_LINK_UP: - netif_carrier_on(dev); - printk("Link to the physical device is (re)established.\n"); - break; - - case I2O_LAN_EVT_MEDIA_CHANGE: - printk("Media change.\n"); - break; - default: - printk("0x%08x. No handler.\n", evt->evt_indicator); - } -} - -/* - * i2o_lan_receive_post(): Post buckets to receive packets. - */ -static int i2o_lan_receive_post(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - struct sk_buff *skb; - u32 m, *msg; - u32 bucket_len = (dev->mtu + dev->hard_header_len); - u32 total = priv->max_buckets_out - atomic_read(&priv->buckets_out); - u32 bucket_count; - u32 *sgl_elem; - unsigned long flags; - - /* Send (total/bucket_count) separate I2O requests */ - - while (total) { - m = I2O_POST_READ32(iop); - if (m == 0xFFFFFFFF) - return -ETIMEDOUT; - msg = (u32 *)(iop->mem_offset + m); - - bucket_count = (total >= priv->sgl_max) ? priv->sgl_max : total; - total -= bucket_count; - atomic_add(bucket_count, &priv->buckets_out); - - dprintk(KERN_INFO "%s: Sending %d buckets (size %d) to LAN DDM.\n", - dev->name, bucket_count, bucket_len); - - /* Fill in the header */ - - __raw_writel(I2O_MESSAGE_SIZE(4 + 3 * bucket_count) | SGL_OFFSET_4, msg); - __raw_writel(LAN_RECEIVE_POST<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); - __raw_writel(priv->unit << 16 | lan_receive_context, msg+2); - __raw_writel(bucket_count, msg+3); - sgl_elem = &msg[4]; - - /* Fill in the payload - contains bucket_count SGL elements */ - - while (bucket_count--) { - spin_lock_irqsave(&priv->fbl_lock, flags); - if (priv->i2o_fbl_tail >= 0) - skb = priv->i2o_fbl[priv->i2o_fbl_tail--]; - else { - skb = dev_alloc_skb(bucket_len + 2); - if (skb == NULL) { - spin_unlock_irqrestore(&priv->fbl_lock, flags); - return -ENOMEM; - } - skb_reserve(skb, 2); - } - spin_unlock_irqrestore(&priv->fbl_lock, flags); - - __raw_writel(0x51000000 | bucket_len, sgl_elem); - __raw_writel((u32)skb, sgl_elem+1); - __raw_writel(virt_to_bus(skb->data), sgl_elem+2); - sgl_elem += 3; - } - - /* set LE flag and post */ - __raw_writel(__raw_readl(sgl_elem-3) | 0x80000000, (sgl_elem-3)); - i2o_post_message(iop, m); - } - - return 0; -} - -/* Functions called from the network stack, and functions called by them: -========================================================================*/ - -/* - * i2o_lan_reset(): Reset the LAN adapter into the operational state and - * restore it to full operation. - */ -static int i2o_lan_reset(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 msg[5]; - - dprintk(KERN_INFO "%s: LAN RESET MESSAGE.\n", dev->name); - msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = LAN_RESET<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid; - msg[2] = priv->unit << 16 | lan_context; // InitiatorContext - msg[3] = 0; // TransactionContext - msg[4] = 0; // Keep posted buckets - - if (i2o_post_this(iop, msg, sizeof(msg)) < 0) - return -ETIMEDOUT; - - return 0; -} - -/* - * i2o_lan_suspend(): Put LAN adapter into a safe, non-active state. - * IOP replies to any LAN class message with status error_no_data_transfer - * / suspended. - */ -static int i2o_lan_suspend(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 msg[5]; - - dprintk(KERN_INFO "%s: LAN SUSPEND MESSAGE.\n", dev->name); - msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = LAN_SUSPEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid; - msg[2] = priv->unit << 16 | lan_context; // InitiatorContext - msg[3] = 0; // TransactionContext - msg[4] = 1 << 16; // return posted buckets - - if (i2o_post_this(iop, msg, sizeof(msg)) < 0) - return -ETIMEDOUT; - - return 0; -} - -/* - * i2o_set_ddm_parameters: - * These settings are done to ensure proper initial values for DDM. - * They can be changed via proc file system or vai configuration utility. - */ -static void i2o_set_ddm_parameters(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 val; - - /* - * When PacketOrphanlimit is set to the maximum packet length, - * the packets will never be split into two separate buckets - */ - val = dev->mtu + dev->hard_header_len; - if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0004, 2, &val, sizeof(val)) < 0) - printk(KERN_WARNING "%s: Unable to set PacketOrphanLimit.\n", - dev->name); - else - dprintk(KERN_INFO "%s: PacketOrphanLimit set to %d.\n", - dev->name, val); - - /* When RxMaxPacketsBucket = 1, DDM puts only one packet into bucket */ - - val = 1; - if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0008, 4, &val, sizeof(val)) <0) - printk(KERN_WARNING "%s: Unable to set RxMaxPacketsBucket.\n", - dev->name); - else - dprintk(KERN_INFO "%s: RxMaxPacketsBucket set to %d.\n", - dev->name, val); - return; -} - -/* Functions called from the network stack: -==========================================*/ - -/* - * i2o_lan_open(): Open the device to send/receive packets via - * the network device. - */ -static int i2o_lan_open(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 mc_addr_group[64]; - - MOD_INC_USE_COUNT; - - if (i2o_claim_device(i2o_dev, &i2o_lan_handler)) { - printk(KERN_WARNING "%s: Unable to claim the I2O LAN device.\n", dev->name); - MOD_DEC_USE_COUNT; - return -EAGAIN; - } - dprintk(KERN_INFO "%s: I2O LAN device (tid=%d) claimed by LAN OSM.\n", - dev->name, i2o_dev->lct_data.tid); - - if (i2o_event_register(iop, i2o_dev->lct_data.tid, - priv->unit << 16 | lan_context, 0, priv->i2o_event_mask) < 0) - printk(KERN_WARNING "%s: Unable to set the event mask.\n", dev->name); - - i2o_lan_reset(dev); - - /* Get the max number of multicast addresses */ - - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0001, -1, - &mc_addr_group, sizeof(mc_addr_group)) < 0 ) { - printk(KERN_WARNING "%s: Unable to query LAN_MAC_ADDRESS group.\n", dev->name); - MOD_DEC_USE_COUNT; - return -EAGAIN; - } - priv->max_size_mc_table = mc_addr_group[8]; - - /* Malloc space for free bucket list to resuse reveive post buckets */ - - priv->i2o_fbl = kmalloc(priv->max_buckets_out * sizeof(struct sk_buff *), - GFP_KERNEL); - if (priv->i2o_fbl == NULL) { - MOD_DEC_USE_COUNT; - return -ENOMEM; - } - priv->i2o_fbl_tail = -1; - priv->send_active = 0; - - i2o_set_ddm_parameters(dev); - i2o_lan_receive_post(dev); - - netif_start_queue(dev); - - return 0; -} - -/* - * i2o_lan_close(): End the transfering. - */ -static int i2o_lan_close(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - int ret = 0; - - netif_stop_queue(dev); - i2o_lan_suspend(dev); - - if (i2o_event_register(iop, i2o_dev->lct_data.tid, - priv->unit << 16 | lan_context, 0, 0) < 0) - printk(KERN_WARNING "%s: Unable to clear the event mask.\n", - dev->name); - - while (priv->i2o_fbl_tail >= 0) - dev_kfree_skb(priv->i2o_fbl[priv->i2o_fbl_tail--]); - - kfree(priv->i2o_fbl); - - if (i2o_release_device(i2o_dev, &i2o_lan_handler)) { - printk(KERN_WARNING "%s: Unable to unclaim I2O LAN device " - "(tid=%d).\n", dev->name, i2o_dev->lct_data.tid); - ret = -EBUSY; - } - - MOD_DEC_USE_COUNT; - - return ret; -} - -/* - * i2o_lan_tx_timeout(): Tx timeout handler. - */ -static void i2o_lan_tx_timeout(struct net_device *dev) -{ - if (!netif_queue_stopped(dev)) - netif_start_queue(dev); -} - -/* - * i2o_lan_batch_send(): Send packets in batch. - * Both i2o_lan_sdu_send and i2o_lan_packet_send use this. - */ -static void i2o_lan_batch_send(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_controller *iop = priv->i2o_dev->controller; - - spin_lock_irq(&priv->tx_lock); - if (priv->tx_count != 0) { - dev->trans_start = jiffies; - i2o_post_message(iop, priv->m); - dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); - priv->tx_count = 0; - } - priv->send_active = 0; - spin_unlock_irq(&priv->tx_lock); - MOD_DEC_USE_COUNT; -} - -#ifdef CONFIG_NET_FC -/* - * i2o_lan_sdu_send(): Send a packet, MAC header added by the DDM. - * Must be supported by Fibre Channel, optional for Ethernet/802.3, - * Token Ring, FDDI - */ -static int i2o_lan_sdu_send(struct sk_buff *skb, struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - int tickssofar = jiffies - dev->trans_start; - u32 m, *msg; - u32 *sgl_elem; - - spin_lock_irq(&priv->tx_lock); - - priv->tx_count++; - atomic_inc(&priv->tx_out); - - /* - * If tx_batch_mode = 0x00 forced to immediate mode - * If tx_batch_mode = 0x01 forced to batch mode - * If tx_batch_mode = 0x10 switch automatically, current mode immediate - * If tx_batch_mode = 0x11 switch automatically, current mode batch - * If gap between two packets is > 0 ticks, switch to immediate - */ - if (priv->tx_batch_mode >> 1) // switch automatically - priv->tx_batch_mode = tickssofar ? 0x02 : 0x03; - - if (priv->tx_count == 1) { - m = I2O_POST_READ32(iop); - if (m == 0xFFFFFFFF) { - spin_unlock_irq(&priv->tx_lock); - return 1; - } - msg = (u32 *)(iop->mem_offset + m); - priv->m = m; - - __raw_writel(NINE_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4, msg); - __raw_writel(LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); - __raw_writel(priv->unit << 16 | lan_send_context, msg+2); // InitiatorContext - __raw_writel(1 << 30 | 1 << 3, msg+3); // TransmitControlWord - - __raw_writel(0xD7000000 | skb->len, msg+4); // MAC hdr included - __raw_writel((u32)skb, msg+5); // TransactionContext - __raw_writel(virt_to_bus(skb->data), msg+6); - __raw_writel((u32)skb->mac.raw, msg+7); - __raw_writel((u32)skb->mac.raw+4, msg+8); - - if ((priv->tx_batch_mode & 0x01) && !priv->send_active) { - priv->send_active = 1; - MOD_INC_USE_COUNT; - if (schedule_task(&priv->i2o_batch_send_task) == 0) - MOD_DEC_USE_COUNT; - } - } else { /* Add new SGL element to the previous message frame */ - - msg = (u32 *)(iop->mem_offset + priv->m); - sgl_elem = &msg[priv->tx_count * 5 + 1]; - - __raw_writel(I2O_MESSAGE_SIZE((__raw_readl(msg)>>16) + 5) | 1<<12 | SGL_OFFSET_4, msg); - __raw_writel(__raw_readl(sgl_elem-5) & 0x7FFFFFFF, sgl_elem-5); /* clear LE flag */ - __raw_writel(0xD5000000 | skb->len, sgl_elem); - __raw_writel((u32)skb, sgl_elem+1); - __raw_writel(virt_to_bus(skb->data), sgl_elem+2); - __raw_writel((u32)(skb->mac.raw), sgl_elem+3); - __raw_writel((u32)(skb->mac.raw)+1, sgl_elem+4); - } - - /* If tx not in batch mode or frame is full, send immediatelly */ - - if (!(priv->tx_batch_mode & 0x01) || priv->tx_count == priv->sgl_max) { - dev->trans_start = jiffies; - i2o_post_message(iop, priv->m); - dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); - priv->tx_count = 0; - } - - /* If DDMs TxMaxPktOut reached, stop queueing layer to send more */ - - if (atomic_read(&priv->tx_out) >= priv->tx_max_out) - netif_stop_queue(dev); - - spin_unlock_irq(&priv->tx_lock); - return 0; -} -#endif /* CONFIG_NET_FC */ - -/* - * i2o_lan_packet_send(): Send a packet as is, including the MAC header. - * - * Must be supported by Ethernet/802.3, Token Ring, FDDI, optional for - * Fibre Channel - */ -static int i2o_lan_packet_send(struct sk_buff *skb, struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - int tickssofar = jiffies - dev->trans_start; - u32 m, *msg; - u32 *sgl_elem; - - spin_lock_irq(&priv->tx_lock); - - priv->tx_count++; - atomic_inc(&priv->tx_out); - - /* - * If tx_batch_mode = 0x00 forced to immediate mode - * If tx_batch_mode = 0x01 forced to batch mode - * If tx_batch_mode = 0x10 switch automatically, current mode immediate - * If tx_batch_mode = 0x11 switch automatically, current mode batch - * If gap between two packets is > 0 ticks, switch to immediate - */ - if (priv->tx_batch_mode >> 1) // switch automatically - priv->tx_batch_mode = tickssofar ? 0x02 : 0x03; - - if (priv->tx_count == 1) { - m = I2O_POST_READ32(iop); - if (m == 0xFFFFFFFF) { - spin_unlock_irq(&priv->tx_lock); - return 1; - } - msg = (u32 *)(iop->mem_offset + m); - priv->m = m; - - __raw_writel(SEVEN_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4, msg); - __raw_writel(LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); - __raw_writel(priv->unit << 16 | lan_send_context, msg+2); // InitiatorContext - __raw_writel(1 << 30 | 1 << 3, msg+3); // TransmitControlWord - // bit 30: reply as soon as transmission attempt is complete - // bit 3: Supress CRC generation - __raw_writel(0xD5000000 | skb->len, msg+4); // MAC hdr included - __raw_writel((u32)skb, msg+5); // TransactionContext - __raw_writel(virt_to_bus(skb->data), msg+6); - - if ((priv->tx_batch_mode & 0x01) && !priv->send_active) { - priv->send_active = 1; - MOD_INC_USE_COUNT; - if (schedule_task(&priv->i2o_batch_send_task) == 0) - MOD_DEC_USE_COUNT; - } - } else { /* Add new SGL element to the previous message frame */ - - msg = (u32 *)(iop->mem_offset + priv->m); - sgl_elem = &msg[priv->tx_count * 3 + 1]; - - __raw_writel(I2O_MESSAGE_SIZE((__raw_readl(msg)>>16) + 3) | 1<<12 | SGL_OFFSET_4, msg); - __raw_writel(__raw_readl(sgl_elem-3) & 0x7FFFFFFF, sgl_elem-3); /* clear LE flag */ - __raw_writel(0xD5000000 | skb->len, sgl_elem); - __raw_writel((u32)skb, sgl_elem+1); - __raw_writel(virt_to_bus(skb->data), sgl_elem+2); - } - - /* If tx is in immediate mode or frame is full, send now */ - - if (!(priv->tx_batch_mode & 0x01) || priv->tx_count == priv->sgl_max) { - dev->trans_start = jiffies; - i2o_post_message(iop, priv->m); - dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); - priv->tx_count = 0; - } - - /* If DDMs TxMaxPktOut reached, stop queueing layer to send more */ - - if (atomic_read(&priv->tx_out) >= priv->tx_max_out) - netif_stop_queue(dev); - - spin_unlock_irq(&priv->tx_lock); - return 0; -} - -/* - * i2o_lan_get_stats(): Fill in the statistics. - */ -static struct net_device_stats *i2o_lan_get_stats(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u64 val64[16]; - u64 supported_group[4] = { 0, 0, 0, 0 }; - - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0100, -1, val64, - sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_HISTORICAL_STATS.\n", dev->name); - else { - dprintk(KERN_DEBUG "%s: LAN_HISTORICAL_STATS queried.\n", dev->name); - priv->stats.tx_packets = val64[0]; - priv->stats.tx_bytes = val64[1]; - priv->stats.rx_packets = val64[2]; - priv->stats.rx_bytes = val64[3]; - priv->stats.tx_errors = val64[4]; - priv->stats.rx_errors = val64[5]; - priv->stats.rx_dropped = val64[6]; - } - - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0180, -1, - &supported_group, sizeof(supported_group)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_SUPPORTED_OPTIONAL_HISTORICAL_STATS.\n", dev->name); - - if (supported_group[2]) { - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0183, -1, - val64, sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_OPTIONAL_RX_HISTORICAL_STATS.\n", dev->name); - else { - dprintk(KERN_DEBUG "%s: LAN_OPTIONAL_RX_HISTORICAL_STATS queried.\n", dev->name); - priv->stats.multicast = val64[4]; - priv->stats.rx_length_errors = val64[10]; - priv->stats.rx_crc_errors = val64[0]; - } - } - - if (i2o_dev->lct_data.sub_class == I2O_LAN_ETHERNET) { - u64 supported_stats = 0; - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0200, -1, - val64, sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_802_3_HISTORICAL_STATS.\n", dev->name); - else { - dprintk(KERN_DEBUG "%s: LAN_802_3_HISTORICAL_STATS queried.\n", dev->name); - priv->stats.transmit_collision = val64[1] + val64[2]; - priv->stats.rx_frame_errors = val64[0]; - priv->stats.tx_carrier_errors = val64[6]; - } - - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0280, -1, - &supported_stats, sizeof(supported_stats)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_SUPPORTED_802_3_HISTORICAL_STATS.\n", dev->name); - - if (supported_stats != 0) { - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0281, -1, - val64, sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_OPTIONAL_802_3_HISTORICAL_STATS.\n", dev->name); - else { - dprintk(KERN_DEBUG "%s: LAN_OPTIONAL_802_3_HISTORICAL_STATS queried.\n", dev->name); - if (supported_stats & 0x1) - priv->stats.rx_over_errors = val64[0]; - if (supported_stats & 0x4) - priv->stats.tx_heartbeat_errors = val64[2]; - } - } - } - -#ifdef CONFIG_TR - if (i2o_dev->lct_data.sub_class == I2O_LAN_TR) { - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0300, -1, - val64, sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_802_5_HISTORICAL_STATS.\n", dev->name); - else { - struct tr_statistics *stats = - (struct tr_statistics *)&priv->stats; - dprintk(KERN_DEBUG "%s: LAN_802_5_HISTORICAL_STATS queried.\n", dev->name); - - stats->line_errors = val64[0]; - stats->internal_errors = val64[7]; - stats->burst_errors = val64[4]; - stats->A_C_errors = val64[2]; - stats->abort_delimiters = val64[3]; - stats->lost_frames = val64[1]; - /* stats->recv_congest_count = ?; FIXME ??*/ - stats->frame_copied_errors = val64[5]; - stats->frequency_errors = val64[6]; - stats->token_errors = val64[9]; - } - /* Token Ring optional stats not yet defined */ - } -#endif - -#ifdef CONFIG_FDDI - if (i2o_dev->lct_data.sub_class == I2O_LAN_FDDI) { - if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0400, -1, - val64, sizeof(val64)) < 0) - printk(KERN_INFO "%s: Unable to query LAN_FDDI_HISTORICAL_STATS.\n", dev->name); - else { - dprintk(KERN_DEBUG "%s: LAN_FDDI_HISTORICAL_STATS queried.\n", dev->name); - priv->stats.smt_cf_state = val64[0]; - memcpy(priv->stats.mac_upstream_nbr, &val64[1], FDDI_K_ALEN); - memcpy(priv->stats.mac_downstream_nbr, &val64[2], FDDI_K_ALEN); - priv->stats.mac_error_cts = val64[3]; - priv->stats.mac_lost_cts = val64[4]; - priv->stats.mac_rmt_state = val64[5]; - memcpy(priv->stats.port_lct_fail_cts, &val64[6], 8); - memcpy(priv->stats.port_lem_reject_cts, &val64[7], 8); - memcpy(priv->stats.port_lem_cts, &val64[8], 8); - memcpy(priv->stats.port_pcm_state, &val64[9], 8); - } - /* FDDI optional stats not yet defined */ - } -#endif - -#ifdef CONFIG_NET_FC - /* Fibre Channel Statistics not yet defined in 1.53 nor 2.0 */ -#endif - - return (struct net_device_stats *)&priv->stats; -} - -/* - * i2o_lan_set_mc_filter(): Post a request to set multicast filter. - */ -int i2o_lan_set_mc_filter(struct net_device *dev, u32 filter_mask) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 msg[10]; - - msg[0] = TEN_WORD_MSG_SIZE | SGL_OFFSET_5; - msg[1] = I2O_CMD_UTIL_PARAMS_SET << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid; - msg[2] = priv->unit << 16 | lan_context; - msg[3] = 0x0001 << 16 | 3 ; // TransactionContext: group&field - msg[4] = 0; - msg[5] = 0xCC000000 | 16; // Immediate data SGL - msg[6] = 1; // OperationCount - msg[7] = 0x0001<<16 | I2O_PARAMS_FIELD_SET; // Group, Operation - msg[8] = 3 << 16 | 1; // FieldIndex, FieldCount - msg[9] = filter_mask; // Value - - return i2o_post_this(iop, msg, sizeof(msg)); -} - -/* - * i2o_lan_set_mc_table(): Post a request to set LAN_MULTICAST_MAC_ADDRESS table. - */ -int i2o_lan_set_mc_table(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - struct dev_mc_list *mc; - u32 msg[10 + 2 * dev->mc_count]; - u8 *work8 = (u8 *)(msg + 10); - - msg[0] = I2O_MESSAGE_SIZE(10 + 2 * dev->mc_count) | SGL_OFFSET_5; - msg[1] = I2O_CMD_UTIL_PARAMS_SET << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid; - msg[2] = priv->unit << 16 | lan_context; // InitiatorContext - msg[3] = 0x0002 << 16 | (u16)-1; // TransactionContext - msg[4] = 0; // OperationFlags - msg[5] = 0xCC000000 | (16 + 8 * dev->mc_count); // Immediate data SGL - msg[6] = 2; // OperationCount - msg[7] = 0x0002 << 16 | I2O_PARAMS_TABLE_CLEAR; // Group, Operation - msg[8] = 0x0002 << 16 | I2O_PARAMS_ROW_ADD; // Group, Operation - msg[9] = dev->mc_count << 16 | (u16)-1; // RowCount, FieldCount - - for (mc = dev->mc_list; mc ; mc = mc->next, work8 += 8) { - memset(work8, 0, 8); - memcpy(work8, mc->dmi_addr, mc->dmi_addrlen); // Values - } - - return i2o_post_this(iop, msg, sizeof(msg)); -} - -/* - * i2o_lan_set_multicast_list(): Enable a network device to receive packets - * not send to the protocol address. - */ -static void i2o_lan_set_multicast_list(struct net_device *dev) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - u32 filter_mask; - - if (dev->flags & IFF_PROMISC) { - filter_mask = 0x00000002; - dprintk(KERN_INFO "%s: Enabling promiscuous mode...\n", dev->name); - } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > priv->max_size_mc_table) { - filter_mask = 0x00000004; - dprintk(KERN_INFO "%s: Enabling all multicast mode...\n", dev->name); - } else if (dev->mc_count) { - filter_mask = 0x00000000; - dprintk(KERN_INFO "%s: Enabling multicast mode...\n", dev->name); - if (i2o_lan_set_mc_table(dev) < 0) - printk(KERN_WARNING "%s: Unable to send MAC table.\n", dev->name); - } else { - filter_mask = 0x00000300; // Broadcast, Multicast disabled - dprintk(KERN_INFO "%s: Enabling unicast mode...\n", dev->name); - } - - /* Finally copy new FilterMask to DDM */ - - if (i2o_lan_set_mc_filter(dev, filter_mask) < 0) - printk(KERN_WARNING "%s: Unable to send MAC FilterMask.\n", dev->name); -} - -/* - * i2o_lan_change_mtu(): Change maximum transfer unit size. - */ -static int i2o_lan_change_mtu(struct net_device *dev, int new_mtu) -{ - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - u32 max_pkt_size; - - if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, - 0x0000, 6, &max_pkt_size, 4) < 0) - return -EFAULT; - - if (new_mtu < 68 || new_mtu > 9000 || new_mtu > max_pkt_size) - return -EINVAL; - - dev->mtu = new_mtu; - - i2o_lan_suspend(dev); // to SUSPENDED state, return buckets - - while (priv->i2o_fbl_tail >= 0) // free buffered buckets - dev_kfree_skb(priv->i2o_fbl[priv->i2o_fbl_tail--]); - - i2o_lan_reset(dev); // to OPERATIONAL state - i2o_set_ddm_parameters(dev); // reset some parameters - i2o_lan_receive_post(dev); // post new buckets (new size) - - return 0; -} - -/* Functions to initialize I2O LAN OSM: -======================================*/ - -/* - * i2o_lan_register_device(): Register LAN class device to kernel. - */ -struct net_device *i2o_lan_register_device(struct i2o_device *i2o_dev) -{ - struct net_device *dev = NULL; - struct i2o_lan_local *priv = NULL; - u8 hw_addr[8]; - u32 tx_max_out = 0; - unsigned short (*type_trans)(struct sk_buff *, struct net_device *); - void (*unregister_dev)(struct net_device *dev); - - switch (i2o_dev->lct_data.sub_class) { - case I2O_LAN_ETHERNET: - dev = init_etherdev(NULL, sizeof(struct i2o_lan_local)); - if (dev == NULL) - return NULL; - type_trans = eth_type_trans; - unregister_dev = unregister_netdev; - break; - -#ifdef CONFIG_ANYLAN - case I2O_LAN_100VG: - printk(KERN_ERR "i2o_lan: 100base VG not yet supported.\n"); - return NULL; - break; -#endif - -#ifdef CONFIG_TR - case I2O_LAN_TR: - dev = init_trdev(NULL, sizeof(struct i2o_lan_local)); - if (dev==NULL) - return NULL; - type_trans = tr_type_trans; - unregister_dev = unregister_trdev; - break; -#endif - -#ifdef CONFIG_FDDI - case I2O_LAN_FDDI: - { - int size = sizeof(struct net_device) + sizeof(struct i2o_lan_local); - - dev = (struct net_device *) kmalloc(size, GFP_KERNEL); - if (dev == NULL) - return NULL; - memset((char *)dev, 0, size); - dev->priv = (void *)(dev + 1); - - if (dev_alloc_name(dev, "fddi%d") < 0) { - printk(KERN_WARNING "i2o_lan: Too many FDDI devices.\n"); - kfree(dev); - return NULL; - } - type_trans = fddi_type_trans; - unregister_dev = (void *)unregister_netdevice; - - fddi_setup(dev); - register_netdev(dev); - } - break; -#endif - -#ifdef CONFIG_NET_FC - case I2O_LAN_FIBRE_CHANNEL: - dev = init_fcdev(NULL, sizeof(struct i2o_lan_local)); - if (dev == NULL) - return NULL; - type_trans = NULL; -/* FIXME: Move fc_type_trans() from drivers/net/fc/iph5526.c to net/802/fc.c - * and export it in include/linux/fcdevice.h - * type_trans = fc_type_trans; - */ - unregister_dev = (void *)unregister_fcdev; - break; -#endif - - case I2O_LAN_UNKNOWN: - default: - printk(KERN_ERR "i2o_lan: LAN type 0x%04x not supported.\n", - i2o_dev->lct_data.sub_class); - return NULL; - } - - priv = (struct i2o_lan_local *)dev->priv; - priv->i2o_dev = i2o_dev; - priv->type_trans = type_trans; - priv->sgl_max = (i2o_dev->controller->status_block->inbound_frame_size - 4) / 3; - atomic_set(&priv->buckets_out, 0); - - /* Set default values for user configurable parameters */ - /* Private values are changed via /proc file system */ - - priv->max_buckets_out = max_buckets_out; - priv->bucket_thresh = bucket_thresh; - priv->rx_copybreak = rx_copybreak; - priv->tx_batch_mode = tx_batch_mode & 0x03; - priv->i2o_event_mask = i2o_event_mask; - - priv->tx_lock = SPIN_LOCK_UNLOCKED; - priv->fbl_lock = SPIN_LOCK_UNLOCKED; - - unit++; - i2o_landevs[unit] = dev; - priv->unit = unit; - - if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, - 0x0001, 0, &hw_addr, sizeof(hw_addr)) < 0) { - printk(KERN_ERR "%s: Unable to query hardware address.\n", dev->name); - unit--; - unregister_dev(dev); - kfree(dev); - return NULL; - } - dprintk(KERN_DEBUG "%s: hwaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", - dev->name, hw_addr[0], hw_addr[1], hw_addr[2], hw_addr[3], - hw_addr[4], hw_addr[5]); - - dev->addr_len = 6; - memcpy(dev->dev_addr, hw_addr, 6); - - if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, - 0x0007, 2, &tx_max_out, sizeof(tx_max_out)) < 0) { - printk(KERN_ERR "%s: Unable to query max TX queue.\n", dev->name); - unit--; - unregister_dev(dev); - kfree(dev); - return NULL; - } - dprintk(KERN_INFO "%s: Max TX Outstanding = %d.\n", dev->name, tx_max_out); - priv->tx_max_out = tx_max_out; - atomic_set(&priv->tx_out, 0); - priv->tx_count = 0; - - INIT_LIST_HEAD(&priv->i2o_batch_send_task.list); - priv->i2o_batch_send_task.sync = 0; - priv->i2o_batch_send_task.routine = (void *)i2o_lan_batch_send; - priv->i2o_batch_send_task.data = (void *)dev; - - dev->open = i2o_lan_open; - dev->stop = i2o_lan_close; - dev->get_stats = i2o_lan_get_stats; - dev->set_multicast_list = i2o_lan_set_multicast_list; - dev->tx_timeout = i2o_lan_tx_timeout; - dev->watchdog_timeo = I2O_LAN_TX_TIMEOUT; - -#ifdef CONFIG_NET_FC - if (i2o_dev->lct_data.sub_class == I2O_LAN_FIBRE_CHANNEL) - dev->hard_start_xmit = i2o_lan_sdu_send; - else -#endif - dev->hard_start_xmit = i2o_lan_packet_send; - - if (i2o_dev->lct_data.sub_class == I2O_LAN_ETHERNET) - dev->change_mtu = i2o_lan_change_mtu; - - return dev; -} - -#ifdef MODULE -#define i2o_lan_init init_module -#endif - -int __init i2o_lan_init(void) -{ - struct net_device *dev; - int i; - - printk(KERN_INFO "I2O LAN OSM (C) 1999 University of Helsinki.\n"); - - /* Module params are used as global defaults for private values */ - - if (max_buckets_out > I2O_LAN_MAX_BUCKETS_OUT) - max_buckets_out = I2O_LAN_MAX_BUCKETS_OUT; - if (bucket_thresh > max_buckets_out) - bucket_thresh = max_buckets_out; - - /* Install handlers for incoming replies */ - - if (i2o_install_handler(&i2o_lan_send_handler) < 0) { - printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); - return -EINVAL; - } - lan_send_context = i2o_lan_send_handler.context; - - if (i2o_install_handler(&i2o_lan_receive_handler) < 0) { - printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); - return -EINVAL; - } - lan_receive_context = i2o_lan_receive_handler.context; - - if (i2o_install_handler(&i2o_lan_handler) < 0) { - printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); - return -EINVAL; - } - lan_context = i2o_lan_handler.context; - - for(i=0; i <= MAX_LAN_CARDS; i++) - i2o_landevs[i] = NULL; - - for (i=0; i < MAX_I2O_CONTROLLERS; i++) { - struct i2o_controller *iop = i2o_find_controller(i); - struct i2o_device *i2o_dev; - - if (iop==NULL) - continue; - - for (i2o_dev=iop->devices;i2o_dev != NULL;i2o_dev=i2o_dev->next) { - - if (i2o_dev->lct_data.class_id != I2O_CLASS_LAN) - continue; - - /* Make sure device not already claimed by an ISM */ - if (i2o_dev->lct_data.user_tid != 0xFFF) - continue; - - if (unit == MAX_LAN_CARDS) { - i2o_unlock_controller(iop); - printk(KERN_WARNING "i2o_lan: Too many I2O LAN devices.\n"); - return -EINVAL; - } - - dev = i2o_lan_register_device(i2o_dev); - if (dev == NULL) { - printk(KERN_ERR "i2o_lan: Unable to register I2O LAN device 0x%04x.\n", - i2o_dev->lct_data.sub_class); - continue; - } - - printk(KERN_INFO "%s: I2O LAN device registered, " - "subclass = 0x%04x, unit = %d, tid = %d.\n", - dev->name, i2o_dev->lct_data.sub_class, - ((struct i2o_lan_local *)dev->priv)->unit, - i2o_dev->lct_data.tid); - } - - i2o_unlock_controller(iop); - } - - dprintk(KERN_INFO "%d I2O LAN devices found and registered.\n", unit+1); - - return 0; -} - -#ifdef MODULE - -void cleanup_module(void) -{ - int i; - - for (i = 0; i <= unit; i++) { - struct net_device *dev = i2o_landevs[i]; - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - - switch (i2o_dev->lct_data.sub_class) { - case I2O_LAN_ETHERNET: - unregister_netdev(dev); - break; -#ifdef CONFIG_FDDI - case I2O_LAN_FDDI: - unregister_netdevice(dev); - break; -#endif -#ifdef CONFIG_TR - case I2O_LAN_TR: - unregister_trdev(dev); - break; -#endif -#ifdef CONFIG_NET_FC - case I2O_LAN_FIBRE_CHANNEL: - unregister_fcdev(dev); - break; -#endif - default: - printk(KERN_WARNING "%s: Spurious I2O LAN subclass 0x%08x.\n", - dev->name, i2o_dev->lct_data.sub_class); - } - - dprintk(KERN_INFO "%s: I2O LAN device unregistered.\n", - dev->name); - kfree(dev); - } - - i2o_remove_handler(&i2o_lan_handler); - i2o_remove_handler(&i2o_lan_send_handler); - i2o_remove_handler(&i2o_lan_receive_handler); -} - -EXPORT_NO_SYMBOLS; - -MODULE_AUTHOR("University of Helsinki, Department of Computer Science"); -MODULE_DESCRIPTION("I2O Lan OSM"); - -MODULE_PARM(max_buckets_out, "1-" __MODULE_STRING(I2O_LAN_MAX_BUCKETS_OUT) "i"); -MODULE_PARM_DESC(max_buckets_out, "Total number of buckets to post (1-)"); -MODULE_PARM(bucket_thresh, "1-" __MODULE_STRING(I2O_LAN_MAX_BUCKETS_OUT) "i"); -MODULE_PARM_DESC(bucket_thresh, "Bucket post threshold (1-)"); -MODULE_PARM(rx_copybreak, "1-" "i"); -MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy only small frames (1-)"); -MODULE_PARM(tx_batch_mode, "0-2" "i"); -MODULE_PARM_DESC(tx_batch_mode, "0=Send immediatelly, 1=Send in batches, 2=Switch automatically"); - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_lan.h linux.ac/drivers/i2o/i2o_lan.h --- linux.vanilla/drivers/i2o/i2o_lan.h Mon Dec 11 21:20:00 2000 +++ linux.ac/drivers/i2o/i2o_lan.h Thu Jan 1 01:00:00 1970 @@ -1,159 +0,0 @@ -/* - * i2o_lan.h I2O LAN Class definitions - * - * I2O LAN CLASS OSM May 26th 2000 - * - * (C) Copyright 1999, 2000 University of Helsinki, - * Department of Computer Science - * - * This code is still under development / test. - * - * Author: Auvo Häkkinen - * Juha Sievänen - * Taneli Vähäkangas - */ - -#ifndef _I2O_LAN_H -#define _I2O_LAN_H - -/* Default values for tunable parameters first */ - -#define I2O_LAN_MAX_BUCKETS_OUT 96 -#define I2O_LAN_BUCKET_THRESH 18 /* 9 buckets in one message */ -#define I2O_LAN_RX_COPYBREAK 200 -#define I2O_LAN_TX_TIMEOUT (1*HZ) -#define I2O_LAN_TX_BATCH_MODE 2 /* 2=automatic, 1=on, 0=off */ -#define I2O_LAN_EVENT_MASK 0 /* 0=None, 0xFFC00002=All */ - -/* LAN types */ -#define I2O_LAN_ETHERNET 0x0030 -#define I2O_LAN_100VG 0x0040 -#define I2O_LAN_TR 0x0050 -#define I2O_LAN_FDDI 0x0060 -#define I2O_LAN_FIBRE_CHANNEL 0x0070 -#define I2O_LAN_UNKNOWN 0x00000000 - -/* Connector types */ - -/* Ethernet */ -#define I2O_LAN_AUI (I2O_LAN_ETHERNET << 4) + 0x00000001 -#define I2O_LAN_10BASE5 (I2O_LAN_ETHERNET << 4) + 0x00000002 -#define I2O_LAN_FIORL (I2O_LAN_ETHERNET << 4) + 0x00000003 -#define I2O_LAN_10BASE2 (I2O_LAN_ETHERNET << 4) + 0x00000004 -#define I2O_LAN_10BROAD36 (I2O_LAN_ETHERNET << 4) + 0x00000005 -#define I2O_LAN_10BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000006 -#define I2O_LAN_10BASE_FP (I2O_LAN_ETHERNET << 4) + 0x00000007 -#define I2O_LAN_10BASE_FB (I2O_LAN_ETHERNET << 4) + 0x00000008 -#define I2O_LAN_10BASE_FL (I2O_LAN_ETHERNET << 4) + 0x00000009 -#define I2O_LAN_100BASE_TX (I2O_LAN_ETHERNET << 4) + 0x0000000A -#define I2O_LAN_100BASE_FX (I2O_LAN_ETHERNET << 4) + 0x0000000B -#define I2O_LAN_100BASE_T4 (I2O_LAN_ETHERNET << 4) + 0x0000000C -#define I2O_LAN_1000BASE_SX (I2O_LAN_ETHERNET << 4) + 0x0000000D -#define I2O_LAN_1000BASE_LX (I2O_LAN_ETHERNET << 4) + 0x0000000E -#define I2O_LAN_1000BASE_CX (I2O_LAN_ETHERNET << 4) + 0x0000000F -#define I2O_LAN_1000BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000010 - -/* AnyLAN */ -#define I2O_LAN_100VG_ETHERNET (I2O_LAN_100VG << 4) + 0x00000001 -#define I2O_LAN_100VG_TR (I2O_LAN_100VG << 4) + 0x00000002 - -/* Token Ring */ -#define I2O_LAN_4MBIT (I2O_LAN_TR << 4) + 0x00000001 -#define I2O_LAN_16MBIT (I2O_LAN_TR << 4) + 0x00000002 - -/* FDDI */ -#define I2O_LAN_125MBAUD (I2O_LAN_FDDI << 4) + 0x00000001 - -/* Fibre Channel */ -#define I2O_LAN_POINT_POINT (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000001 -#define I2O_LAN_ARB_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000002 -#define I2O_LAN_PUBLIC_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000003 -#define I2O_LAN_FABRIC (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000004 - -#define I2O_LAN_EMULATION 0x00000F00 -#define I2O_LAN_OTHER 0x00000F01 -#define I2O_LAN_DEFAULT 0xFFFFFFFF - -/* LAN class functions */ - -#define LAN_PACKET_SEND 0x3B -#define LAN_SDU_SEND 0x3D -#define LAN_RECEIVE_POST 0x3E -#define LAN_RESET 0x35 -#define LAN_SUSPEND 0x37 - -/* LAN DetailedStatusCode defines */ -#define I2O_LAN_DSC_SUCCESS 0x00 -#define I2O_LAN_DSC_DEVICE_FAILURE 0x01 -#define I2O_LAN_DSC_DESTINATION_NOT_FOUND 0x02 -#define I2O_LAN_DSC_TRANSMIT_ERROR 0x03 -#define I2O_LAN_DSC_TRANSMIT_ABORTED 0x04 -#define I2O_LAN_DSC_RECEIVE_ERROR 0x05 -#define I2O_LAN_DSC_RECEIVE_ABORTED 0x06 -#define I2O_LAN_DSC_DMA_ERROR 0x07 -#define I2O_LAN_DSC_BAD_PACKET_DETECTED 0x08 -#define I2O_LAN_DSC_OUT_OF_MEMORY 0x09 -#define I2O_LAN_DSC_BUCKET_OVERRUN 0x0A -#define I2O_LAN_DSC_IOP_INTERNAL_ERROR 0x0B -#define I2O_LAN_DSC_CANCELED 0x0C -#define I2O_LAN_DSC_INVALID_TRANSACTION_CONTEXT 0x0D -#define I2O_LAN_DSC_DEST_ADDRESS_DETECTED 0x0E -#define I2O_LAN_DSC_DEST_ADDRESS_OMITTED 0x0F -#define I2O_LAN_DSC_PARTIAL_PACKET_RETURNED 0x10 -#define I2O_LAN_DSC_SUSPENDED 0x11 - -struct i2o_packet_info { - u32 offset : 24; - u32 flags : 8; - u32 len : 24; - u32 status : 8; -}; - -struct i2o_bucket_descriptor { - u32 context; /* FIXME: 64bit support */ - struct i2o_packet_info packet_info[1]; -}; - -/* Event Indicator Mask Flags for LAN OSM */ - -#define I2O_LAN_EVT_LINK_DOWN 0x01 -#define I2O_LAN_EVT_LINK_UP 0x02 -#define I2O_LAN_EVT_MEDIA_CHANGE 0x04 - -#include -#include - -struct i2o_lan_local { - u8 unit; - struct i2o_device *i2o_dev; - - struct fddi_statistics stats; /* see also struct net_device_stats */ - unsigned short (*type_trans)(struct sk_buff *, struct net_device *); - atomic_t buckets_out; /* nbr of unused buckets on DDM */ - atomic_t tx_out; /* outstanding TXes */ - u8 tx_count; /* packets in one TX message frame */ - u16 tx_max_out; /* DDM's Tx queue len */ - u8 sgl_max; /* max SGLs in one message frame */ - u32 m; /* IOP address of the batch msg frame */ - - struct tq_struct i2o_batch_send_task; - int send_active; - struct sk_buff **i2o_fbl; /* Free bucket list (to reuse skbs) */ - int i2o_fbl_tail; - spinlock_t fbl_lock; - - spinlock_t tx_lock; - - u32 max_size_mc_table; /* max number of multicast addresses */ - - /* LAN OSM configurable parameters are here: */ - - u16 max_buckets_out; /* max nbr of buckets to send to DDM */ - u16 bucket_thresh; /* send more when this many used */ - u16 rx_copybreak; - - u8 tx_batch_mode; /* Set when using batch mode sends */ - u32 i2o_event_mask; /* To turn on interesting event flags */ -}; - -#endif /* _I2O_LAN_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_pci.c linux.ac/drivers/i2o/i2o_pci.c --- linux.vanilla/drivers/i2o/i2o_pci.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/i2o/i2o_pci.c Thu Jan 1 01:00:00 1970 @@ -1,378 +0,0 @@ -/* - * Find I2O capable controllers on the PCI bus, and register/install - * them with the I2O layer - * - * (C) Copyright 1999 Red Hat Software - * - * Written by Alan Cox, Building Number Three Ltd - * Modified by Deepak Saxena - * Modified by Boji T Kannanthanam - * - * 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. - * - * TODO: - * Support polled I2O PCI controllers. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_MTRR -#include -#endif // CONFIG_MTRR - -#ifdef MODULE -/* - * Core function table - * See for an explanation - */ -static struct i2o_core_func_table *core; - -/* Core attach function */ -extern int i2o_pci_core_attach(struct i2o_core_func_table *); -extern void i2o_pci_core_detach(void); -#endif /* MODULE */ - -/* - * Free bus specific resources - */ -static void i2o_pci_dispose(struct i2o_controller *c) -{ - I2O_IRQ_WRITE32(c,0xFFFFFFFF); - if(c->bus.pci.irq > 0) - free_irq(c->bus.pci.irq, c); - iounmap(((u8 *)c->post_port)-0x40); - -#ifdef CONFIG_MTRR - if(c->bus.pci.mtrr_reg0 > 0) - mtrr_del(c->bus.pci.mtrr_reg0, 0, 0); - if(c->bus.pci.mtrr_reg1 > 0) - mtrr_del(c->bus.pci.mtrr_reg1, 0, 0); -#endif -} - -/* - * No real bus specific handling yet (note that later we will - * need to 'steal' PCI devices on i960 mainboards) - */ - -static int i2o_pci_bind(struct i2o_controller *c, struct i2o_device *dev) -{ - MOD_INC_USE_COUNT; - return 0; -} - -static int i2o_pci_unbind(struct i2o_controller *c, struct i2o_device *dev) -{ - MOD_DEC_USE_COUNT; - return 0; -} - -/* - * Bus specific enable/disable functions - */ -static void i2o_pci_enable(struct i2o_controller *c) -{ - I2O_IRQ_WRITE32(c, 0); - c->enabled = 1; -} - -static void i2o_pci_disable(struct i2o_controller *c) -{ - I2O_IRQ_WRITE32(c, 0xFFFFFFFF); - c->enabled = 0; -} - -/* - * Bus specific interrupt handler - */ - -static void i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r) -{ - struct i2o_controller *c = dev_id; -#ifdef MODULE - core->run_queue(c); -#else - i2o_run_queue(c); -#endif /* MODULE */ -} - -/* - * Install a PCI (or in theory AGP) i2o controller - * - * TODO: Add support for polled controllers - */ -int __init i2o_pci_install(struct pci_dev *dev) -{ - struct i2o_controller *c=kmalloc(sizeof(struct i2o_controller), - GFP_KERNEL); - u8 *mem; - u32 memptr = 0; - u32 size; - - int i; - - if(c==NULL) - { - printk(KERN_ERR "i2o: Insufficient memory to add controller.\n"); - return -ENOMEM; - } - memset(c, 0, sizeof(*c)); - - for(i=0; i<6; i++) - { - /* Skip I/O spaces */ - if(!(pci_resource_flags(dev, i) & IORESOURCE_IO)) - { - memptr = pci_resource_start(dev, i); - break; - } - } - - if(i==6) - { - printk(KERN_ERR "i2o: I2O controller has no memory regions defined.\n"); - kfree(c); - return -EINVAL; - } - - size = dev->resource[i].end-dev->resource[i].start+1; - /* Map the I2O controller */ - - printk(KERN_INFO "i2o: PCI I2O controller at 0x%08X size=%d\n", memptr, size); - mem = ioremap(memptr, size); - if(mem==NULL) - { - printk(KERN_ERR "i2o: Unable to map controller.\n"); - kfree(c); - return -EINVAL; - } - - c->bus.pci.irq = -1; - - c->bus.pci.queue_buggy = 0; - c->bus.pci.dpt = 0; - c->bus.pci.short_req = 0; - - c->irq_mask = (volatile u32 *)(mem+0x34); - c->post_port = (volatile u32 *)(mem+0x40); - c->reply_port = (volatile u32 *)(mem+0x44); - - c->mem_phys = memptr; - c->mem_offset = (u32)mem; - c->destructor = i2o_pci_dispose; - - c->bind = i2o_pci_bind; - c->unbind = i2o_pci_unbind; - c->bus_enable = i2o_pci_enable; - c->bus_disable = i2o_pci_disable; - - c->type = I2O_TYPE_PCI; - - /* - * Cards that fall apart if you hit them with large I/O - * loads... - */ - - if(dev->vendor == PCI_VENDOR_ID_NCR && dev->device == 0x0630) - { - c->bus.pci.short_req=1; - printk(KERN_INFO "I2O: Symbios FC920 workarounds activated.\n"); - } - if(dev->subsystem_vendor == PCI_VENDOR_ID_PROMISE) - { - c->bus.pci.queue_buggy=1; - printk(KERN_INFO "I2O: Promise workarounds activated.\n"); - } - - /* - * Cards that go bananas if you quiesce them before you reset - * them - */ - - if(dev->vendor == PCI_VENDOR_ID_DPT) - c->bus.pci.dpt=1; - - /* - * Enable Write Combining MTRR for IOP's memory region - */ -#ifdef CONFIG_MTRR - c->bus.pci.mtrr_reg0 = - mtrr_add(c->mem_phys, size, MTRR_TYPE_WRCOMB, 1); -/* -* If it is an INTEL i960 I/O processor then set the first 64K to Uncacheable -* since the region contains the Messaging unit which shouldn't be cached. -*/ - c->bus.pci.mtrr_reg1 = -1; - if(dev->vendor == PCI_VENDOR_ID_INTEL || dev->vendor == PCI_VENDOR_ID_DPT) - { - printk(KERN_INFO "I2O: MTRR workaround for Intel i960 processor\n"); - c->bus.pci.mtrr_reg1 = mtrr_add(c->mem_phys, 65536, MTRR_TYPE_UNCACHABLE, 1); - if(c->bus.pci.mtrr_reg1< 0) - printk(KERN_INFO "i2o_pci: Error in setting MTRR_TYPE_UNCACHABLE\n"); - } - -#endif - - I2O_IRQ_WRITE32(c,0xFFFFFFFF); - -#ifdef MODULE - i = core->install(c); -#else - i = i2o_install_controller(c); -#endif /* MODULE */ - - if(i<0) - { - printk(KERN_ERR "i2o: Unable to install controller.\n"); - kfree(c); - iounmap(mem); - return i; - } - - c->bus.pci.irq = dev->irq; - if(c->bus.pci.irq) - { - i=request_irq(dev->irq, i2o_pci_interrupt, SA_SHIRQ, - c->name, c); - if(i<0) - { - printk(KERN_ERR "%s: unable to allocate interrupt %d.\n", - c->name, dev->irq); - c->bus.pci.irq = -1; -#ifdef MODULE - core->delete(c); -#else - i2o_delete_controller(c); -#endif /* MODULE */ - kfree(c); - iounmap(mem); - return -EBUSY; - } - } - - printk(KERN_INFO "%s: Installed at IRQ%d\n", c->name, dev->irq); - I2O_IRQ_WRITE32(c,0x0); - c->enabled = 1; - return 0; -} - -int __init i2o_pci_scan(void) -{ - struct pci_dev *dev; - int count=0; - - printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n"); - - pci_for_each_dev(dev) - { - if((dev->class>>8)!=PCI_CLASS_INTELLIGENT_I2O) - continue; - if((dev->class&0xFF)>1) - { - printk(KERN_INFO "i2o: I2O Controller found but does not support I2O 1.5 (skipping).\n"); - continue; - } - if (pci_enable_device(dev)) - continue; - printk(KERN_INFO "i2o: I2O controller on bus %d at %d.\n", - dev->bus->number, dev->devfn); - pci_set_master(dev); - if(i2o_pci_install(dev)==0) - count++; - } - if(count) - printk(KERN_INFO "i2o: %d I2O controller%s found and installed.\n", count, - count==1?"":"s"); - return count?count:-ENODEV; -} - -#ifdef I2O_HOTPLUG_SUPPORT -/* - * Activate a newly found PCI I2O controller - * Not used now, but will be needed in future for - * hot plug PCI support - */ -static void i2o_pci_activate(i2o_controller * c) -{ - int i=0; - struct i2o_controller *c; - - if(c->type == I2O_TYPE_PCI) - { - I2O_IRQ_WRITE32(c,0); -#ifdef MODULE - if(core->activate(c)) -#else - if(i2o_activate_controller(c)) -#endif /* MODULE */ - { - printk("%s: Failed to initialize.\n", c->name); -#ifdef MODULE - core->unlock(c); - core->delete(c); -#else - i2o_unlock_controller(c); - i2o_delete_controller(c); -#endif - continue; - } - } -} -#endif // I2O_HOTPLUG_SUPPORT - -#ifdef MODULE - -int i2o_pci_core_attach(struct i2o_core_func_table *table) -{ - MOD_INC_USE_COUNT; - - core = table; - - return i2o_pci_scan(); -} - -void i2o_pci_core_detach(void) -{ - core = NULL; - - MOD_DEC_USE_COUNT; -} - -int init_module(void) -{ - printk(KERN_INFO "Linux I2O PCI support (c) 1999 Red Hat Software.\n"); - - core = NULL; - - return 0; - -} - -void cleanup_module(void) -{ -} - -EXPORT_SYMBOL(i2o_pci_core_attach); -EXPORT_SYMBOL(i2o_pci_core_detach); - -MODULE_AUTHOR("Red Hat Software"); -MODULE_DESCRIPTION("I2O PCI Interface"); - -#else -void __init i2o_pci_init(void) -{ - printk(KERN_INFO "Linux I2O PCI support (c) 1999 Red Hat Software.\n"); - i2o_pci_scan(); -} -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_proc.c linux.ac/drivers/i2o/i2o_proc.c --- linux.vanilla/drivers/i2o/i2o_proc.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/i2o/i2o_proc.c Thu Jan 1 01:00:00 1970 @@ -1,3372 +0,0 @@ -/* - * procfs handler for Linux I2O subsystem - * - * (c) Copyright 1999 Deepak Saxena - * - * Originally written by Deepak Saxena(deepak@plexity.net) - * - * 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 is an initial test release. The code is based on the design - * of the ide procfs system (drivers/block/ide-proc.c). Some code - * taken from i2o-core module by Alan Cox. - * - * DISCLAIMER: This code is still under development/test and may cause - * your system to behave unpredictably. Use at your own discretion. - * - * LAN entries by Juha Sievänen (Juha.Sievanen@cs.Helsinki.FI), - * Auvo Häkkinen (Auvo.Hakkinen@cs.Helsinki.FI) - * University of Helsinki, Department of Computer Science - */ - -/* - * set tabstop=3 - */ - -/* - * TODO List - * - * - Add support for any version 2.0 spec changes once 2.0 IRTOS is - * is available to test with - * - Clean up code to use official structure definitions - */ - -// FIXME! -#define FMT_U64_HEX "0x%08x%08x" -#define U64_VAL(pu64) *((u32*)(pu64)+1), *((u32*)(pu64)) - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "i2o_lan.h" - -/* - * Structure used to define /proc entries - */ -typedef struct _i2o_proc_entry_t -{ - char *name; /* entry name */ - mode_t mode; /* mode */ - read_proc_t *read_proc; /* read func */ - write_proc_t *write_proc; /* write func */ -} i2o_proc_entry; - -// #define DRIVERDEBUG - -static int i2o_proc_read_lct(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_hrt(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_status(char *, char **, off_t, int, int *, void *); - -static int i2o_proc_read_hw(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_ddm_table(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_driver_store(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_drivers_stored(char *, char **, off_t, int, int *, void *); - -static int i2o_proc_read_groups(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_phys_device(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_claimed(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_users(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_priv_msgs(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_authorized_users(char *, char **, off_t, int, int *, void *); - -static int i2o_proc_read_dev_name(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_dev_identity(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_ddm_identity(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_uinfo(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_sgl_limits(char *, char **, off_t, int, int *, void *); - -static int i2o_proc_read_sensors(char *, char **, off_t, int, int *, void *); - -static int print_serial_number(char *, int, u8 *, int); - -static int i2o_proc_create_entries(void *, i2o_proc_entry *, - struct proc_dir_entry *); -static void i2o_proc_remove_entries(i2o_proc_entry *, struct proc_dir_entry *); -static int i2o_proc_add_controller(struct i2o_controller *, - struct proc_dir_entry * ); -static void i2o_proc_remove_controller(struct i2o_controller *, - struct proc_dir_entry * ); -static void i2o_proc_add_device(struct i2o_device *, struct proc_dir_entry *); -static void i2o_proc_remove_device(struct i2o_device *); -static int create_i2o_procfs(void); -static int destroy_i2o_procfs(void); -static void i2o_proc_new_dev(struct i2o_controller *, struct i2o_device *); -static void i2o_proc_dev_del(struct i2o_controller *, struct i2o_device *); - -static int i2o_proc_read_lan_dev_info(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_mac_addr(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_mcast_addr(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_batch_control(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_operation(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_media_operation(char *, char **, off_t, int, - int *, void *); -static int i2o_proc_read_lan_alt_addr(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_tx_info(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_rx_info(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_hist_stats(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_eth_stats(char *, char **, off_t, int, - int *, void *); -static int i2o_proc_read_lan_tr_stats(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_fddi_stats(char *, char **, off_t, int, int *, - void *); - -static struct proc_dir_entry *i2o_proc_dir_root; - -/* - * I2O OSM descriptor - */ -static struct i2o_handler i2o_proc_handler = -{ - NULL, - i2o_proc_new_dev, - i2o_proc_dev_del, - NULL, - "I2O procfs Layer", - 0, - 0xffffffff // All classes -}; - -/* - * IOP specific entries...write field just in case someone - * ever wants one. - */ -static i2o_proc_entry generic_iop_entries[] = -{ - {"hrt", S_IFREG|S_IRUGO, i2o_proc_read_hrt, NULL}, - {"lct", S_IFREG|S_IRUGO, i2o_proc_read_lct, NULL}, - {"status", S_IFREG|S_IRUGO, i2o_proc_read_status, NULL}, - {"hw", S_IFREG|S_IRUGO, i2o_proc_read_hw, NULL}, - {"ddm_table", S_IFREG|S_IRUGO, i2o_proc_read_ddm_table, NULL}, - {"driver_store", S_IFREG|S_IRUGO, i2o_proc_read_driver_store, NULL}, - {"drivers_stored", S_IFREG|S_IRUGO, i2o_proc_read_drivers_stored, NULL}, - {NULL, 0, NULL, NULL} -}; - -/* - * Device specific entries - */ -static i2o_proc_entry generic_dev_entries[] = -{ - {"groups", S_IFREG|S_IRUGO, i2o_proc_read_groups, NULL}, - {"phys_dev", S_IFREG|S_IRUGO, i2o_proc_read_phys_device, NULL}, - {"claimed", S_IFREG|S_IRUGO, i2o_proc_read_claimed, NULL}, - {"users", S_IFREG|S_IRUGO, i2o_proc_read_users, NULL}, - {"priv_msgs", S_IFREG|S_IRUGO, i2o_proc_read_priv_msgs, NULL}, - {"authorized_users", S_IFREG|S_IRUGO, i2o_proc_read_authorized_users, NULL}, - {"dev_identity", S_IFREG|S_IRUGO, i2o_proc_read_dev_identity, NULL}, - {"ddm_identity", S_IFREG|S_IRUGO, i2o_proc_read_ddm_identity, NULL}, - {"user_info", S_IFREG|S_IRUGO, i2o_proc_read_uinfo, NULL}, - {"sgl_limits", S_IFREG|S_IRUGO, i2o_proc_read_sgl_limits, NULL}, - {"sensors", S_IFREG|S_IRUGO, i2o_proc_read_sensors, NULL}, - {NULL, 0, NULL, NULL} -}; - -/* - * Storage unit specific entries (SCSI Periph, BS) with device names - */ -static i2o_proc_entry rbs_dev_entries[] = -{ - {"dev_name", S_IFREG|S_IRUGO, i2o_proc_read_dev_name, NULL}, - {NULL, 0, NULL, NULL} -}; - -#define SCSI_TABLE_SIZE 13 -static char *scsi_devices[] = -{ - "Direct-Access Read/Write", - "Sequential-Access Storage", - "Printer", - "Processor", - "WORM Device", - "CD-ROM Device", - "Scanner Device", - "Optical Memory Device", - "Medium Changer Device", - "Communications Device", - "Graphics Art Pre-Press Device", - "Graphics Art Pre-Press Device", - "Array Controller Device" -}; - -/* private */ - -/* - * Generic LAN specific entries - * - * Should groups with r/w entries have their own subdirectory? - * - */ -static i2o_proc_entry lan_entries[] = -{ - {"lan_dev_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_dev_info, NULL}, - {"lan_mac_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_mac_addr, NULL}, - {"lan_mcast_addr", S_IFREG|S_IRUGO|S_IWUSR, - i2o_proc_read_lan_mcast_addr, NULL}, - {"lan_batch_ctrl", S_IFREG|S_IRUGO|S_IWUSR, - i2o_proc_read_lan_batch_control, NULL}, - {"lan_operation", S_IFREG|S_IRUGO, i2o_proc_read_lan_operation, NULL}, - {"lan_media_operation", S_IFREG|S_IRUGO, - i2o_proc_read_lan_media_operation, NULL}, - {"lan_alt_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_alt_addr, NULL}, - {"lan_tx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_tx_info, NULL}, - {"lan_rx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_rx_info, NULL}, - - {"lan_hist_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_hist_stats, NULL}, - {NULL, 0, NULL, NULL} -}; - -/* - * Port specific LAN entries - * - */ -static i2o_proc_entry lan_eth_entries[] = -{ - {"lan_eth_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_eth_stats, NULL}, - {NULL, 0, NULL, NULL} -}; - -static i2o_proc_entry lan_tr_entries[] = -{ - {"lan_tr_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_tr_stats, NULL}, - {NULL, 0, NULL, NULL} -}; - -static i2o_proc_entry lan_fddi_entries[] = -{ - {"lan_fddi_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_fddi_stats, NULL}, - {NULL, 0, NULL, NULL} -}; - - -static char *chtostr(u8 *chars, int n) -{ - char tmp[256]; - tmp[0] = 0; - return strncat(tmp, (char *)chars, n); -} - -static int i2o_report_query_status(char *buf, int block_status, char *group) -{ - switch (block_status) - { - case -ETIMEDOUT: - return sprintf(buf, "Timeout reading group %s.\n",group); - case -ENOMEM: - return sprintf(buf, "No free memory to read the table.\n"); - case -I2O_PARAMS_STATUS_INVALID_GROUP_ID: - return sprintf(buf, "Group %s not supported.\n", group); - default: - return sprintf(buf, "Error reading group %s. BlockStatus 0x%02X\n", - group, -block_status); - } -} - -static char* bus_strings[] = -{ - "Local Bus", - "ISA", - "EISA", - "MCA", - "PCI", - "PCMCIA", - "NUBUS", - "CARDBUS" -}; - -static spinlock_t i2o_proc_lock = SPIN_LOCK_UNLOCKED; - -int i2o_proc_read_hrt(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller *)data; - i2o_hrt *hrt = (i2o_hrt *)c->hrt; - u32 bus; - int count; - int i; - - spin_lock(&i2o_proc_lock); - - len = 0; - - if(hrt->hrt_version) - { - len += sprintf(buf+len, - "HRT table for controller is too new a version.\n"); - spin_unlock(&i2o_proc_lock); - return len; - } - - count = hrt->num_entries; - - if((count * hrt->entry_len + 8) > 2048) { - printk(KERN_WARNING "i2o_proc: HRT does not fit into buffer\n"); - len += sprintf(buf+len, - "HRT table too big to fit in buffer.\n"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "HRT has %d entries of %d bytes each.\n", - count, hrt->entry_len << 2); - - for(i = 0; i < count; i++) - { - len += sprintf(buf+len, "Entry %d:\n", i); - len += sprintf(buf+len, " Adapter ID: %0#10x\n", - hrt->hrt_entry[i].adapter_id); - len += sprintf(buf+len, " Controlling tid: %0#6x\n", - hrt->hrt_entry[i].parent_tid); - - if(hrt->hrt_entry[i].bus_type != 0x80) - { - bus = hrt->hrt_entry[i].bus_type; - len += sprintf(buf+len, " %s Information\n", bus_strings[bus]); - - switch(bus) - { - case I2O_BUS_LOCAL: - len += sprintf(buf+len, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.local_bus.LbBaseIOPort); - len += sprintf(buf+len, " MemoryBase: %0#10x\n", - hrt->hrt_entry[i].bus.local_bus.LbBaseMemoryAddress); - break; - - case I2O_BUS_ISA: - len += sprintf(buf+len, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.isa_bus.IsaBaseIOPort); - len += sprintf(buf+len, " MemoryBase: %0#10x,", - hrt->hrt_entry[i].bus.isa_bus.IsaBaseMemoryAddress); - len += sprintf(buf+len, " CSN: %0#4x,", - hrt->hrt_entry[i].bus.isa_bus.CSN); - break; - - case I2O_BUS_EISA: - len += sprintf(buf+len, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.eisa_bus.EisaBaseIOPort); - len += sprintf(buf+len, " MemoryBase: %0#10x,", - hrt->hrt_entry[i].bus.eisa_bus.EisaBaseMemoryAddress); - len += sprintf(buf+len, " Slot: %0#4x,", - hrt->hrt_entry[i].bus.eisa_bus.EisaSlotNumber); - break; - - case I2O_BUS_MCA: - len += sprintf(buf+len, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.mca_bus.McaBaseIOPort); - len += sprintf(buf+len, " MemoryBase: %0#10x,", - hrt->hrt_entry[i].bus.mca_bus.McaBaseMemoryAddress); - len += sprintf(buf+len, " Slot: %0#4x,", - hrt->hrt_entry[i].bus.mca_bus.McaSlotNumber); - break; - - case I2O_BUS_PCI: - len += sprintf(buf+len, " Bus: %0#4x", - hrt->hrt_entry[i].bus.pci_bus.PciBusNumber); - len += sprintf(buf+len, " Dev: %0#4x", - hrt->hrt_entry[i].bus.pci_bus.PciDeviceNumber); - len += sprintf(buf+len, " Func: %0#4x", - hrt->hrt_entry[i].bus.pci_bus.PciFunctionNumber); - len += sprintf(buf+len, " Vendor: %0#6x", - hrt->hrt_entry[i].bus.pci_bus.PciVendorID); - len += sprintf(buf+len, " Device: %0#6x\n", - hrt->hrt_entry[i].bus.pci_bus.PciDeviceID); - break; - - default: - len += sprintf(buf+len, " Unsupported Bus Type\n"); - } - } - else - len += sprintf(buf+len, " Unknown Bus Type\n"); - } - - spin_unlock(&i2o_proc_lock); - - return len; -} - -int i2o_proc_read_lct(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller*)data; - i2o_lct *lct = (i2o_lct *)c->lct; - int entries; - int i; - -#define BUS_TABLE_SIZE 3 - static char *bus_ports[] = - { - "Generic Bus", - "SCSI Bus", - "Fibre Channel Bus" - }; - - spin_lock(&i2o_proc_lock); - len = 0; - - entries = (lct->table_size - 3)/9; - - len += sprintf(buf, "LCT contains %d %s\n", entries, - entries == 1 ? "entry" : "entries"); - if(lct->boot_tid) - len += sprintf(buf+len, "Boot Device @ ID %d\n", lct->boot_tid); - - len += - sprintf(buf+len, "Current Change Indicator: %#10x\n", lct->change_ind); - - for(i = 0; i < entries; i++) - { - len += sprintf(buf+len, "Entry %d\n", i); - len += sprintf(buf+len, " Class, SubClass : %s", i2o_get_class_name(lct->lct_entry[i].class_id)); - - /* - * Classes which we'll print subclass info for - */ - switch(lct->lct_entry[i].class_id & 0xFFF) - { - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - switch(lct->lct_entry[i].sub_class) - { - case 0x00: - len += sprintf(buf+len, ", Direct-Access Read/Write"); - break; - - case 0x04: - len += sprintf(buf+len, ", WORM Drive"); - break; - - case 0x05: - len += sprintf(buf+len, ", CD-ROM Drive"); - break; - - case 0x07: - len += sprintf(buf+len, ", Optical Memory Device"); - break; - - default: - len += sprintf(buf+len, ", Unknown (0x%02x)", - lct->lct_entry[i].sub_class); - break; - } - break; - - case I2O_CLASS_LAN: - switch(lct->lct_entry[i].sub_class & 0xFF) - { - case 0x30: - len += sprintf(buf+len, ", Ethernet"); - break; - - case 0x40: - len += sprintf(buf+len, ", 100base VG"); - break; - - case 0x50: - len += sprintf(buf+len, ", IEEE 802.5/Token-Ring"); - break; - - case 0x60: - len += sprintf(buf+len, ", ANSI X3T9.5 FDDI"); - break; - - case 0x70: - len += sprintf(buf+len, ", Fibre Channel"); - break; - - default: - len += sprintf(buf+len, ", Unknown Sub-Class (0x%02x)", - lct->lct_entry[i].sub_class & 0xFF); - break; - } - break; - - case I2O_CLASS_SCSI_PERIPHERAL: - if(lct->lct_entry[i].sub_class < SCSI_TABLE_SIZE) - len += sprintf(buf+len, ", %s", - scsi_devices[lct->lct_entry[i].sub_class]); - else - len += sprintf(buf+len, ", Unknown Device Type"); - break; - - case I2O_CLASS_BUS_ADAPTER_PORT: - if(lct->lct_entry[i].sub_class < BUS_TABLE_SIZE) - len += sprintf(buf+len, ", %s", - bus_ports[lct->lct_entry[i].sub_class]); - else - len += sprintf(buf+len, ", Unknown Bus Type"); - break; - } - len += sprintf(buf+len, "\n"); - - len += sprintf(buf+len, " Local TID : 0x%03x\n", lct->lct_entry[i].tid); - len += sprintf(buf+len, " User TID : 0x%03x\n", lct->lct_entry[i].user_tid); - len += sprintf(buf+len, " Parent TID : 0x%03x\n", - lct->lct_entry[i].parent_tid); - len += sprintf(buf+len, " Identity Tag : 0x%x%x%x%x%x%x%x%x\n", - lct->lct_entry[i].identity_tag[0], - lct->lct_entry[i].identity_tag[1], - lct->lct_entry[i].identity_tag[2], - lct->lct_entry[i].identity_tag[3], - lct->lct_entry[i].identity_tag[4], - lct->lct_entry[i].identity_tag[5], - lct->lct_entry[i].identity_tag[6], - lct->lct_entry[i].identity_tag[7]); - len += sprintf(buf+len, " Change Indicator : %0#10x\n", - lct->lct_entry[i].change_ind); - len += sprintf(buf+len, " Event Capab Mask : %0#10x\n", - lct->lct_entry[i].device_flags); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - -int i2o_proc_read_status(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller*)data; - char prodstr[25]; - int version; - - spin_lock(&i2o_proc_lock); - len = 0; - - i2o_status_get(c); // reread the status block - - len += sprintf(buf+len,"Organization ID : %0#6x\n", - c->status_block->org_id); - - version = c->status_block->i2o_version; - -/* FIXME for Spec 2.0 - if (version == 0x02) { - len += sprintf(buf+len,"Lowest I2O version supported: "); - switch(workspace[2]) { - case 0x00: - len += sprintf(buf+len,"1.0\n"); - break; - case 0x01: - len += sprintf(buf+len,"1.5\n"); - break; - case 0x02: - len += sprintf(buf+len,"2.0\n"); - break; - } - - len += sprintf(buf+len, "Highest I2O version supported: "); - switch(workspace[3]) { - case 0x00: - len += sprintf(buf+len,"1.0\n"); - break; - case 0x01: - len += sprintf(buf+len,"1.5\n"); - break; - case 0x02: - len += sprintf(buf+len,"2.0\n"); - break; - } - } -*/ - len += sprintf(buf+len,"IOP ID : %0#5x\n", - c->status_block->iop_id); - len += sprintf(buf+len,"Host Unit ID : %0#6x\n", - c->status_block->host_unit_id); - len += sprintf(buf+len,"Segment Number : %0#5x\n", - c->status_block->segment_number); - - len += sprintf(buf+len, "I2O version : "); - switch (version) { - case 0x00: - len += sprintf(buf+len,"1.0\n"); - break; - case 0x01: - len += sprintf(buf+len,"1.5\n"); - break; - case 0x02: - len += sprintf(buf+len,"2.0\n"); - break; - default: - len += sprintf(buf+len,"Unknown version\n"); - } - - len += sprintf(buf+len, "IOP State : "); - switch (c->status_block->iop_state) { - case 0x01: - len += sprintf(buf+len,"INIT\n"); - break; - - case 0x02: - len += sprintf(buf+len,"RESET\n"); - break; - - case 0x04: - len += sprintf(buf+len,"HOLD\n"); - break; - - case 0x05: - len += sprintf(buf+len,"READY\n"); - break; - - case 0x08: - len += sprintf(buf+len,"OPERATIONAL\n"); - break; - - case 0x10: - len += sprintf(buf+len,"FAILED\n"); - break; - - case 0x11: - len += sprintf(buf+len,"FAULTED\n"); - break; - - default: - len += sprintf(buf+len,"Unknown\n"); - break; - } - - len += sprintf(buf+len,"Messenger Type : "); - switch (c->status_block->msg_type) { - case 0x00: - len += sprintf(buf+len,"Memory mapped\n"); - break; - case 0x01: - len += sprintf(buf+len,"Memory mapped only\n"); - break; - case 0x02: - len += sprintf(buf+len,"Remote only\n"); - break; - case 0x03: - len += sprintf(buf+len,"Memory mapped and remote\n"); - break; - default: - len += sprintf(buf+len,"Unknown\n"); - } - - len += sprintf(buf+len,"Inbound Frame Size : %d bytes\n", - c->status_block->inbound_frame_size<<2); - len += sprintf(buf+len,"Max Inbound Frames : %d\n", - c->status_block->max_inbound_frames); - len += sprintf(buf+len,"Current Inbound Frames : %d\n", - c->status_block->cur_inbound_frames); - len += sprintf(buf+len,"Max Outbound Frames : %d\n", - c->status_block->max_outbound_frames); - - /* Spec doesn't say if NULL terminated or not... */ - memcpy(prodstr, c->status_block->product_id, 24); - prodstr[24] = '\0'; - len += sprintf(buf+len,"Product ID : %s\n", prodstr); - len += sprintf(buf+len,"Expected LCT Size : %d bytes\n", - c->status_block->expected_lct_size); - - len += sprintf(buf+len,"IOP Capabilities\n"); - len += sprintf(buf+len," Context Field Size Support : "); - switch (c->status_block->iop_capabilities & 0x0000003) { - case 0: - len += sprintf(buf+len,"Supports only 32-bit context fields\n"); - break; - case 1: - len += sprintf(buf+len,"Supports only 64-bit context fields\n"); - break; - case 2: - len += sprintf(buf+len,"Supports 32-bit and 64-bit context fields, " - "but not concurrently\n"); - break; - case 3: - len += sprintf(buf+len,"Supports 32-bit and 64-bit context fields " - "concurrently\n"); - break; - default: - len += sprintf(buf+len,"0x%08x\n",c->status_block->iop_capabilities); - } - len += sprintf(buf+len," Current Context Field Size : "); - switch (c->status_block->iop_capabilities & 0x0000000C) { - case 0: - len += sprintf(buf+len,"not configured\n"); - break; - case 4: - len += sprintf(buf+len,"Supports only 32-bit context fields\n"); - break; - case 8: - len += sprintf(buf+len,"Supports only 64-bit context fields\n"); - break; - case 12: - len += sprintf(buf+len,"Supports both 32-bit or 64-bit context fields " - "concurrently\n"); - break; - default: - len += sprintf(buf+len,"\n"); - } - len += sprintf(buf+len," Inbound Peer Support : %s\n", - (c->status_block->iop_capabilities & 0x00000010) ? "Supported" : "Not supported"); - len += sprintf(buf+len," Outbound Peer Support : %s\n", - (c->status_block->iop_capabilities & 0x00000020) ? "Supported" : "Not supported"); - len += sprintf(buf+len," Peer to Peer Support : %s\n", - (c->status_block->iop_capabilities & 0x00000040) ? "Supported" : "Not supported"); - - len += sprintf(buf+len, "Desired private memory size : %d kB\n", - c->status_block->desired_mem_size>>10); - len += sprintf(buf+len, "Allocated private memory size : %d kB\n", - c->status_block->current_mem_size>>10); - len += sprintf(buf+len, "Private memory base address : %0#10x\n", - c->status_block->current_mem_base); - len += sprintf(buf+len, "Desired private I/O size : %d kB\n", - c->status_block->desired_io_size>>10); - len += sprintf(buf+len, "Allocated private I/O size : %d kB\n", - c->status_block->current_io_size>>10); - len += sprintf(buf+len, "Private I/O base address : %0#10x\n", - c->status_block->current_io_base); - - spin_unlock(&i2o_proc_lock); - - return len; -} - -int i2o_proc_read_hw(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller*)data; - static u32 work32[5]; - static u8 *work8 = (u8*)work32; - static u16 *work16 = (u16*)work32; - int token; - u32 hwcap; - - static char *cpu_table[] = - { - "Intel 80960 series", - "AMD2900 series", - "Motorola 68000 series", - "ARM series", - "MIPS series", - "Sparc series", - "PowerPC series", - "Intel x86 series" - }; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(c, ADAPTER_TID, 0x0000, -1, &work32, sizeof(work32)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0000 IOP Hardware"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "I2O Vendor ID : %0#6x\n", work16[0]); - len += sprintf(buf+len, "Product ID : %0#6x\n", work16[1]); - len += sprintf(buf+len, "CPU : "); - if(work8[16] > 8) - len += sprintf(buf+len, "Unknown\n"); - else - len += sprintf(buf+len, "%s\n", cpu_table[work8[16]]); - /* Anyone using ProcessorVersion? */ - - len += sprintf(buf+len, "RAM : %dkB\n", work32[1]>>10); - len += sprintf(buf+len, "Non-Volatile Mem : %dkB\n", work32[2]>>10); - - hwcap = work32[3]; - len += sprintf(buf+len, "Capabilities : 0x%08x\n", hwcap); - len += sprintf(buf+len, " [%s] Self booting\n", - (hwcap&0x00000001) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Upgradable IRTOS\n", - (hwcap&0x00000002) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Supports downloading DDMs\n", - (hwcap&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Supports installing DDMs\n", - (hwcap&0x00000008) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Battery-backed RAM\n", - (hwcap&0x00000010) ? "+" : "-"); - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -/* Executive group 0003h - Executing DDM List (table) */ -int i2o_proc_read_ddm_table(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller*)data; - int token; - int i; - - typedef struct _i2o_exec_execute_ddm_table { - u16 ddm_tid; - u8 module_type; - u8 reserved; - u16 i2o_vendor_id; - u16 module_id; - u8 module_name_version[28]; - u32 data_size; - u32 code_size; - } i2o_exec_execute_ddm_table; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - i2o_exec_execute_ddm_table ddm_table[MAX_I2O_MODULES]; - } result; - - i2o_exec_execute_ddm_table ddm_table; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - c, ADAPTER_TID, - 0x0003, -1, - NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0003 Executing DDM List"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Tid Module_type Vendor Mod_id Module_name Vrs Data_size Code_size\n"); - ddm_table=result.ddm_table[0]; - - for(i=0; i < result.row_count; ddm_table=result.ddm_table[++i]) - { - len += sprintf(buf+len, "0x%03x ", ddm_table.ddm_tid & 0xFFF); - - switch(ddm_table.module_type) - { - case 0x01: - len += sprintf(buf+len, "Downloaded DDM "); - break; - case 0x22: - len += sprintf(buf+len, "Embedded DDM "); - break; - default: - len += sprintf(buf+len, " "); - } - - len += sprintf(buf+len, "%-#7x", ddm_table.i2o_vendor_id); - len += sprintf(buf+len, "%-#8x", ddm_table.module_id); - len += sprintf(buf+len, "%-29s", chtostr(ddm_table.module_name_version, 28)); - len += sprintf(buf+len, "%9d ", ddm_table.data_size); - len += sprintf(buf+len, "%8d", ddm_table.code_size); - - len += sprintf(buf+len, "\n"); - } - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -/* Executive group 0004h - Driver Store (scalar) */ -int i2o_proc_read_driver_store(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_controller *c = (struct i2o_controller*)data; - u32 work32[8]; - int token; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(c, ADAPTER_TID, 0x0004, -1, &work32, sizeof(work32)); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0004 Driver Store"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Module limit : %d\n" - "Module count : %d\n" - "Current space : %d kB\n" - "Free space : %d kB\n", - work32[0], work32[1], work32[2]>>10, work32[3]>>10); - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -/* Executive group 0005h - Driver Store Table (table) */ -int i2o_proc_read_drivers_stored(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - typedef struct _i2o_driver_store { - u16 stored_ddm_index; - u8 module_type; - u8 reserved; - u16 i2o_vendor_id; - u16 module_id; - u8 module_name_version[28]; - u8 date[8]; - u32 module_size; - u32 mpb_size; - u32 module_flags; - } i2o_driver_store_table; - - struct i2o_controller *c = (struct i2o_controller*)data; - int token; - int i; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - i2o_driver_store_table dst[MAX_I2O_MODULES]; - } result; - - i2o_driver_store_table dst; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - c, ADAPTER_TID, 0x0005, -1, NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0005 DRIVER STORE TABLE"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "# Module_type Vendor Mod_id Module_name Vrs" - "Date Mod_size Par_size Flags\n"); - for(i=0, dst=result.dst[0]; i < result.row_count; dst=result.dst[++i]) - { - len += sprintf(buf+len, "%-3d", dst.stored_ddm_index); - switch(dst.module_type) - { - case 0x01: - len += sprintf(buf+len, "Downloaded DDM "); - break; - case 0x22: - len += sprintf(buf+len, "Embedded DDM "); - break; - default: - len += sprintf(buf+len, " "); - } - -#if 0 - if(c->i2oversion == 0x02) - len += sprintf(buf+len, "%-d", dst.module_state); -#endif - - len += sprintf(buf+len, "%-#7x", dst.i2o_vendor_id); - len += sprintf(buf+len, "%-#8x", dst.module_id); - len += sprintf(buf+len, "%-29s", chtostr(dst.module_name_version,28)); - len += sprintf(buf+len, "%-9s", chtostr(dst.date,8)); - len += sprintf(buf+len, "%8d ", dst.module_size); - len += sprintf(buf+len, "%8d ", dst.mpb_size); - len += sprintf(buf+len, "0x%04x", dst.module_flags); -#if 0 - if(c->i2oversion == 0x02) - len += sprintf(buf+len, "%d", - dst.notification_level); -#endif - len += sprintf(buf+len, "\n"); - } - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -/* Generic group F000h - Params Descriptor (table) */ -int i2o_proc_read_groups(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - u8 properties; - - typedef struct _i2o_group_info - { - u16 group_number; - u16 field_count; - u16 row_count; - u8 properties; - u8 reserved; - } i2o_group_info; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - i2o_group_info group[256]; - } result; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, 0xF000, -1, NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len = i2o_report_query_status(buf+len, token, "0xF000 Params Descriptor"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "# Group FieldCount RowCount Type Add Del Clear\n"); - - for (i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-3d", i); - len += sprintf(buf+len, "0x%04X ", result.group[i].group_number); - len += sprintf(buf+len, "%10d ", result.group[i].field_count); - len += sprintf(buf+len, "%8d ", result.group[i].row_count); - - properties = result.group[i].properties; - if (properties & 0x1) len += sprintf(buf+len, "Table "); - else len += sprintf(buf+len, "Scalar "); - if (properties & 0x2) len += sprintf(buf+len, " + "); - else len += sprintf(buf+len, " - "); - if (properties & 0x4) len += sprintf(buf+len, " + "); - else len += sprintf(buf+len, " - "); - if (properties & 0x8) len += sprintf(buf+len, " + "); - else len += sprintf(buf+len, " - "); - - len += sprintf(buf+len, "\n"); - } - - if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -/* Generic group F001h - Physical Device Table (table) */ -int i2o_proc_read_phys_device(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u32 adapter_id[64]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF001, -1, NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF001 Physical Device Table"); - spin_unlock(&i2o_proc_lock); - return len; - } - - if (result.row_count) - len += sprintf(buf+len, "# AdapterId\n"); - - for (i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-2d", i); - len += sprintf(buf+len, "%#7x\n", result.adapter_id[i]); - } - - if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* Generic group F002h - Claimed Table (table) */ -int i2o_proc_read_claimed(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - - struct { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u16 claimed_tid[64]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF002, -1, NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF002 Claimed Table"); - spin_unlock(&i2o_proc_lock); - return len; - } - - if (result.row_count) - len += sprintf(buf+len, "# ClaimedTid\n"); - - for (i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-2d", i); - len += sprintf(buf+len, "%#7x\n", result.claimed_tid[i]); - } - - if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* Generic group F003h - User Table (table) */ -int i2o_proc_read_users(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - - typedef struct _i2o_user_table - { - u16 instance; - u16 user_tid; - u8 claim_type; - u8 reserved1; - u16 reserved2; - } i2o_user_table; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - i2o_user_table user[64]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF003, -1, NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF003 User Table"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "# Instance UserTid ClaimType\n"); - - for(i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-3d", i); - len += sprintf(buf+len, "%#8x ", result.user[i].instance); - len += sprintf(buf+len, "%#7x ", result.user[i].user_tid); - len += sprintf(buf+len, "%#9x\n", result.user[i].claim_type); - } - - if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* Generic group F005h - Private message extensions (table) (optional) */ -int i2o_proc_read_priv_msgs(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - - typedef struct _i2o_private - { - u16 ext_instance; - u16 organization_id; - u16 x_function_code; - } i2o_private; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - i2o_private extension[64]; - } result; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF000, -1, - NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF005 Private Message Extensions (optional)"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Instance# OrgId FunctionCode\n"); - - for(i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%0#9x ", result.extension[i].ext_instance); - len += sprintf(buf+len, "%0#6x ", result.extension[i].organization_id); - len += sprintf(buf+len, "%0#6x", result.extension[i].x_function_code); - - len += sprintf(buf+len, "\n"); - } - - if(result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -/* Generic group F006h - Authorized User Table (table) */ -int i2o_proc_read_authorized_users(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u32 alternate_tid[64]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF006, -1, - NULL, 0, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF006 Autohorized User Table"); - spin_unlock(&i2o_proc_lock); - return len; - } - - if (result.row_count) - len += sprintf(buf+len, "# AlternateTid\n"); - - for(i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-2d", i); - len += sprintf(buf+len, "%#7x ", result.alternate_tid[i]); - } - - if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); - - spin_unlock(&i2o_proc_lock); - return len; -} - - -/* Generic group F100h - Device Identity (scalar) */ -int i2o_proc_read_dev_identity(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[128]; // allow for "stuff" + up to 256 byte (max) serial number - // == (allow) 512d bytes (max) - static u16 *work16 = (u16*)work32; - int token; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF100, -1, - &work32, sizeof(work32)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token ,"0xF100 Device Identity"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Device Class : %s\n", i2o_get_class_name(work16[0])); - len += sprintf(buf+len, "Owner TID : %0#5x\n", work16[2]); - len += sprintf(buf+len, "Parent TID : %0#5x\n", work16[3]); - len += sprintf(buf+len, "Vendor info : %s\n", chtostr((u8 *)(work32+2), 16)); - len += sprintf(buf+len, "Product info : %s\n", chtostr((u8 *)(work32+6), 16)); - len += sprintf(buf+len, "Description : %s\n", chtostr((u8 *)(work32+10), 16)); - len += sprintf(buf+len, "Product rev. : %s\n", chtostr((u8 *)(work32+14), 8)); - - len += sprintf(buf+len, "Serial number : "); - len = print_serial_number(buf, len, - (u8*)(work32+16), - /* allow for SNLen plus - * possible trailing '\0' - */ - sizeof(work32)-(16*sizeof(u32))-2 - ); - len += sprintf(buf+len, "\n"); - - spin_unlock(&i2o_proc_lock); - - return len; -} - - -int i2o_proc_read_dev_name(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - - if ( d->dev_name[0] == '\0' ) - return 0; - - len = sprintf(buf, "%s\n", d->dev_name); - - return len; -} - - -/* Generic group F101h - DDM Identity (scalar) */ -int i2o_proc_read_ddm_identity(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u16 ddm_tid; - u8 module_name[24]; - u8 module_rev[8]; - u8 sn_format; - u8 serial_number[12]; - u8 pad[256]; // allow up to 256 byte (max) serial number - } result; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF101, -1, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF101 DDM Identity"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Registering DDM TID : 0x%03x\n", result.ddm_tid); - len += sprintf(buf+len, "Module name : %s\n", chtostr(result.module_name, 24)); - len += sprintf(buf+len, "Module revision : %s\n", chtostr(result.module_rev, 8)); - - len += sprintf(buf+len, "Serial number : "); - len = print_serial_number(buf, len, result.serial_number, sizeof(result)-36); - /* allow for SNLen plus possible trailing '\0' */ - - len += sprintf(buf+len, "\n"); - - spin_unlock(&i2o_proc_lock); - - return len; -} - -/* Generic group F102h - User Information (scalar) */ -int i2o_proc_read_uinfo(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u8 device_name[64]; - u8 service_name[64]; - u8 physical_location[64]; - u8 instance_number[4]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF102, -1, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF102 User Information"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Device name : %s\n", chtostr(result.device_name, 64)); - len += sprintf(buf+len, "Service name : %s\n", chtostr(result.service_name, 64)); - len += sprintf(buf+len, "Physical name : %s\n", chtostr(result.physical_location, 64)); - len += sprintf(buf+len, "Instance number : %s\n", chtostr(result.instance_number, 4)); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* Generic group F103h - SGL Operating Limits (scalar) */ -int i2o_proc_read_sgl_limits(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[12]; - static u16 *work16 = (u16 *)work32; - static u8 *work8 = (u8 *)work32; - int token; - - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF103, -1, - &work32, sizeof(work32)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF103 SGL Operating Limits"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "SGL chain size : %d\n", work32[0]); - len += sprintf(buf+len, "Max SGL chain size : %d\n", work32[1]); - len += sprintf(buf+len, "SGL chain size target : %d\n", work32[2]); - len += sprintf(buf+len, "SGL frag count : %d\n", work16[6]); - len += sprintf(buf+len, "Max SGL frag count : %d\n", work16[7]); - len += sprintf(buf+len, "SGL frag count target : %d\n", work16[8]); - - if (d->i2oversion == 0x02) - { - len += sprintf(buf+len, "SGL data alignment : %d\n", work16[8]); - len += sprintf(buf+len, "SGL addr limit : %d\n", work8[20]); - len += sprintf(buf+len, "SGL addr sizes supported : "); - if (work8[21] & 0x01) - len += sprintf(buf+len, "32 bit "); - if (work8[21] & 0x02) - len += sprintf(buf+len, "64 bit "); - if (work8[21] & 0x04) - len += sprintf(buf+len, "96 bit "); - if (work8[21] & 0x08) - len += sprintf(buf+len, "128 bit "); - len += sprintf(buf+len, "\n"); - } - - spin_unlock(&i2o_proc_lock); - - return len; -} - -/* Generic group F200h - Sensors (scalar) */ -int i2o_proc_read_sensors(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u16 sensor_instance; - u8 component; - u16 component_instance; - u8 sensor_class; - u8 sensor_type; - u8 scaling_exponent; - u32 actual_reading; - u32 minimum_reading; - u32 low2lowcat_treshold; - u32 lowcat2low_treshold; - u32 lowwarn2low_treshold; - u32 low2lowwarn_treshold; - u32 norm2lowwarn_treshold; - u32 lowwarn2norm_treshold; - u32 nominal_reading; - u32 hiwarn2norm_treshold; - u32 norm2hiwarn_treshold; - u32 high2hiwarn_treshold; - u32 hiwarn2high_treshold; - u32 hicat2high_treshold; - u32 hi2hicat_treshold; - u32 maximum_reading; - u8 sensor_state; - u16 event_enable; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF200, -1, - &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF200 Sensors (optional)"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Sensor instance : %d\n", result.sensor_instance); - - len += sprintf(buf+len, "Component : %d = ", result.component); - switch (result.component) - { - case 0: len += sprintf(buf+len, "Other"); - break; - case 1: len += sprintf(buf+len, "Planar logic Board"); - break; - case 2: len += sprintf(buf+len, "CPU"); - break; - case 3: len += sprintf(buf+len, "Chassis"); - break; - case 4: len += sprintf(buf+len, "Power Supply"); - break; - case 5: len += sprintf(buf+len, "Storage"); - break; - case 6: len += sprintf(buf+len, "External"); - break; - } - len += sprintf(buf+len,"\n"); - - len += sprintf(buf+len, "Component instance : %d\n", result.component_instance); - len += sprintf(buf+len, "Sensor class : %s\n", - result.sensor_class ? "Analog" : "Digital"); - - len += sprintf(buf+len, "Sensor type : %d = ",result.sensor_type); - switch (result.sensor_type) - { - case 0: len += sprintf(buf+len, "Other\n"); - break; - case 1: len += sprintf(buf+len, "Thermal\n"); - break; - case 2: len += sprintf(buf+len, "DC voltage (DC volts)\n"); - break; - case 3: len += sprintf(buf+len, "AC voltage (AC volts)\n"); - break; - case 4: len += sprintf(buf+len, "DC current (DC amps)\n"); - break; - case 5: len += sprintf(buf+len, "AC current (AC volts)\n"); - break; - case 6: len += sprintf(buf+len, "Door open\n"); - break; - case 7: len += sprintf(buf+len, "Fan operational\n"); - break; - } - - len += sprintf(buf+len, "Scaling exponent : %d\n", result.scaling_exponent); - len += sprintf(buf+len, "Actual reading : %d\n", result.actual_reading); - len += sprintf(buf+len, "Minimum reading : %d\n", result.minimum_reading); - len += sprintf(buf+len, "Low2LowCat treshold : %d\n", result.low2lowcat_treshold); - len += sprintf(buf+len, "LowCat2Low treshold : %d\n", result.lowcat2low_treshold); - len += sprintf(buf+len, "LowWarn2Low treshold : %d\n", result.lowwarn2low_treshold); - len += sprintf(buf+len, "Low2LowWarn treshold : %d\n", result.low2lowwarn_treshold); - len += sprintf(buf+len, "Norm2LowWarn treshold : %d\n", result.norm2lowwarn_treshold); - len += sprintf(buf+len, "LowWarn2Norm treshold : %d\n", result.lowwarn2norm_treshold); - len += sprintf(buf+len, "Nominal reading : %d\n", result.nominal_reading); - len += sprintf(buf+len, "HiWarn2Norm treshold : %d\n", result.hiwarn2norm_treshold); - len += sprintf(buf+len, "Norm2HiWarn treshold : %d\n", result.norm2hiwarn_treshold); - len += sprintf(buf+len, "High2HiWarn treshold : %d\n", result.high2hiwarn_treshold); - len += sprintf(buf+len, "HiWarn2High treshold : %d\n", result.hiwarn2high_treshold); - len += sprintf(buf+len, "HiCat2High treshold : %d\n", result.hicat2high_treshold); - len += sprintf(buf+len, "High2HiCat treshold : %d\n", result.hi2hicat_treshold); - len += sprintf(buf+len, "Maximum reading : %d\n", result.maximum_reading); - - len += sprintf(buf+len, "Sensor state : %d = ", result.sensor_state); - switch (result.sensor_state) - { - case 0: len += sprintf(buf+len, "Normal\n"); - break; - case 1: len += sprintf(buf+len, "Abnormal\n"); - break; - case 2: len += sprintf(buf+len, "Unknown\n"); - break; - case 3: len += sprintf(buf+len, "Low Catastrophic (LoCat)\n"); - break; - case 4: len += sprintf(buf+len, "Low (Low)\n"); - break; - case 5: len += sprintf(buf+len, "Low Warning (LoWarn)\n"); - break; - case 6: len += sprintf(buf+len, "High Warning (HiWarn)\n"); - break; - case 7: len += sprintf(buf+len, "High (High)\n"); - break; - case 8: len += sprintf(buf+len, "High Catastrophic (HiCat)\n"); - break; - } - - len += sprintf(buf+len, "Event_enable : 0x%02X\n", result.event_enable); - len += sprintf(buf+len, " [%s] Operational state change. \n", - (result.event_enable & 0x01) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Low catastrophic. \n", - (result.event_enable & 0x02) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Low reading. \n", - (result.event_enable & 0x04) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Low warning. \n", - (result.event_enable & 0x08) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Change back to normal from out of range state. \n", - (result.event_enable & 0x10) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] High warning. \n", - (result.event_enable & 0x20) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] High reading. \n", - (result.event_enable & 0x40) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] High catastrophic. \n", - (result.event_enable & 0x80) ? "+" : "-" ); - - spin_unlock(&i2o_proc_lock); - return len; -} - - -static int print_serial_number(char *buff, int pos, u8 *serialno, int max_len) -{ - int i; - - /* 19990419 -sralston - * The I2O v1.5 (and v2.0 so far) "official specification" - * got serial numbers WRONG! - * Apparently, and despite what Section 3.4.4 says and - * Figure 3-35 shows (pg 3-39 in the pdf doc), - * the convention / consensus seems to be: - * + First byte is SNFormat - * + Second byte is SNLen (but only if SNFormat==7 (?)) - * + (v2.0) SCSI+BS may use IEEE Registered (64 or 128 bit) format - */ - switch(serialno[0]) - { - case I2O_SNFORMAT_BINARY: /* Binary */ - pos += sprintf(buff+pos, "0x"); - for(i = 0; i < serialno[1]; i++) - { - pos += sprintf(buff+pos, "%02X", serialno[2+i]); - } - break; - - case I2O_SNFORMAT_ASCII: /* ASCII */ - if ( serialno[1] < ' ' ) /* printable or SNLen? */ - { - /* sanity */ - max_len = (max_len < serialno[1]) ? max_len : serialno[1]; - serialno[1+max_len] = '\0'; - - /* just print it */ - pos += sprintf(buff+pos, "%s", &serialno[2]); - } - else - { - /* print chars for specified length */ - for(i = 0; i < serialno[1]; i++) - { - pos += sprintf(buff+pos, "%c", serialno[2+i]); - } - } - break; - - case I2O_SNFORMAT_UNICODE: /* UNICODE */ - pos += sprintf(buff+pos, "UNICODE Format. Can't Display\n"); - break; - - case I2O_SNFORMAT_LAN48_MAC: /* LAN-48 MAC Address */ - pos += sprintf(buff+pos, - "LAN-48 MAC address @ %02X:%02X:%02X:%02X:%02X:%02X", - serialno[2], serialno[3], - serialno[4], serialno[5], - serialno[6], serialno[7]); - break; - - case I2O_SNFORMAT_WAN: /* WAN MAC Address */ - /* FIXME: Figure out what a WAN access address looks like?? */ - pos += sprintf(buff+pos, "WAN Access Address"); - break; - -/* plus new in v2.0 */ - case I2O_SNFORMAT_LAN64_MAC: /* LAN-64 MAC Address */ - /* FIXME: Figure out what a LAN-64 address really looks like?? */ - pos += sprintf(buff+pos, - "LAN-64 MAC address @ [?:%02X:%02X:?] %02X:%02X:%02X:%02X:%02X:%02X", - serialno[8], serialno[9], - serialno[2], serialno[3], - serialno[4], serialno[5], - serialno[6], serialno[7]); - break; - - - case I2O_SNFORMAT_DDM: /* I2O DDM */ - pos += sprintf(buff+pos, - "DDM: Tid=%03Xh, Rsvd=%04Xh, OrgId=%04Xh", - *(u16*)&serialno[2], - *(u16*)&serialno[4], - *(u16*)&serialno[6]); - break; - - case I2O_SNFORMAT_IEEE_REG64: /* IEEE Registered (64-bit) */ - case I2O_SNFORMAT_IEEE_REG128: /* IEEE Registered (128-bit) */ - /* FIXME: Figure if this is even close?? */ - pos += sprintf(buff+pos, - "IEEE NodeName(hi,lo)=(%08Xh:%08Xh), PortName(hi,lo)=(%08Xh:%08Xh)\n", - *(u32*)&serialno[2], - *(u32*)&serialno[6], - *(u32*)&serialno[10], - *(u32*)&serialno[14]); - break; - - - case I2O_SNFORMAT_UNKNOWN: /* Unknown 0 */ - case I2O_SNFORMAT_UNKNOWN2: /* Unknown 0xff */ - default: - pos += sprintf(buff+pos, "Unknown data format (0x%02x)", - serialno[0]); - break; - } - - return pos; -} - -const char * i2o_get_connector_type(int conn) -{ - int idx = 16; - static char *i2o_connector_type[] = { - "OTHER", - "UNKNOWN", - "AUI", - "UTP", - "BNC", - "RJ45", - "STP DB9", - "FIBER MIC", - "APPLE AUI", - "MII", - "DB9", - "HSSDC", - "DUPLEX SC FIBER", - "DUPLEX ST FIBER", - "TNC/BNC", - "HW DEFAULT" - }; - - switch(conn) - { - case 0x00000000: - idx = 0; - break; - case 0x00000001: - idx = 1; - break; - case 0x00000002: - idx = 2; - break; - case 0x00000003: - idx = 3; - break; - case 0x00000004: - idx = 4; - break; - case 0x00000005: - idx = 5; - break; - case 0x00000006: - idx = 6; - break; - case 0x00000007: - idx = 7; - break; - case 0x00000008: - idx = 8; - break; - case 0x00000009: - idx = 9; - break; - case 0x0000000A: - idx = 10; - break; - case 0x0000000B: - idx = 11; - break; - case 0x0000000C: - idx = 12; - break; - case 0x0000000D: - idx = 13; - break; - case 0x0000000E: - idx = 14; - break; - case 0xFFFFFFFF: - idx = 15; - break; - } - - return i2o_connector_type[idx]; -} - - -const char * i2o_get_connection_type(int conn) -{ - int idx = 0; - static char *i2o_connection_type[] = { - "Unknown", - "AUI", - "10BASE5", - "FIORL", - "10BASE2", - "10BROAD36", - "10BASE-T", - "10BASE-FP", - "10BASE-FB", - "10BASE-FL", - "100BASE-TX", - "100BASE-FX", - "100BASE-T4", - "1000BASE-SX", - "1000BASE-LX", - "1000BASE-CX", - "1000BASE-T", - "100VG-ETHERNET", - "100VG-TOKEN RING", - "4MBIT TOKEN RING", - "16 Mb Token Ring", - "125 MBAUD FDDI", - "Point-to-point", - "Arbitrated loop", - "Public loop", - "Fabric", - "Emulation", - "Other", - "HW default" - }; - - switch(conn) - { - case I2O_LAN_UNKNOWN: - idx = 0; - break; - case I2O_LAN_AUI: - idx = 1; - break; - case I2O_LAN_10BASE5: - idx = 2; - break; - case I2O_LAN_FIORL: - idx = 3; - break; - case I2O_LAN_10BASE2: - idx = 4; - break; - case I2O_LAN_10BROAD36: - idx = 5; - break; - case I2O_LAN_10BASE_T: - idx = 6; - break; - case I2O_LAN_10BASE_FP: - idx = 7; - break; - case I2O_LAN_10BASE_FB: - idx = 8; - break; - case I2O_LAN_10BASE_FL: - idx = 9; - break; - case I2O_LAN_100BASE_TX: - idx = 10; - break; - case I2O_LAN_100BASE_FX: - idx = 11; - break; - case I2O_LAN_100BASE_T4: - idx = 12; - break; - case I2O_LAN_1000BASE_SX: - idx = 13; - break; - case I2O_LAN_1000BASE_LX: - idx = 14; - break; - case I2O_LAN_1000BASE_CX: - idx = 15; - break; - case I2O_LAN_1000BASE_T: - idx = 16; - break; - case I2O_LAN_100VG_ETHERNET: - idx = 17; - break; - case I2O_LAN_100VG_TR: - idx = 18; - break; - case I2O_LAN_4MBIT: - idx = 19; - break; - case I2O_LAN_16MBIT: - idx = 20; - break; - case I2O_LAN_125MBAUD: - idx = 21; - break; - case I2O_LAN_POINT_POINT: - idx = 22; - break; - case I2O_LAN_ARB_LOOP: - idx = 23; - break; - case I2O_LAN_PUBLIC_LOOP: - idx = 24; - break; - case I2O_LAN_FABRIC: - idx = 25; - break; - case I2O_LAN_EMULATION: - idx = 26; - break; - case I2O_LAN_OTHER: - idx = 27; - break; - case I2O_LAN_DEFAULT: - idx = 28; - break; - } - - return i2o_connection_type[idx]; -} - - -/* LAN group 0000h - Device info (scalar) */ -int i2o_proc_read_lan_dev_info(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[56]; - static u8 *work8 = (u8*)work32; - static u16 *work16 = (u16*)work32; - static u64 *work64 = (u64*)work32; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0000, -1, &work32, 56*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x0000 LAN Device Info"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "LAN Type : "); - switch (work16[0]) - { - case 0x0030: - len += sprintf(buf+len, "Ethernet, "); - break; - case 0x0040: - len += sprintf(buf+len, "100Base VG, "); - break; - case 0x0050: - len += sprintf(buf+len, "Token Ring, "); - break; - case 0x0060: - len += sprintf(buf+len, "FDDI, "); - break; - case 0x0070: - len += sprintf(buf+len, "Fibre Channel, "); - break; - default: - len += sprintf(buf+len, "Unknown type (0x%04x), ", work16[0]); - break; - } - - if (work16[1]&0x00000001) - len += sprintf(buf+len, "emulated LAN, "); - else - len += sprintf(buf+len, "physical LAN port, "); - - if (work16[1]&0x00000002) - len += sprintf(buf+len, "full duplex\n"); - else - len += sprintf(buf+len, "simplex\n"); - - len += sprintf(buf+len, "Address format : "); - switch(work8[4]) { - case 0x00: - len += sprintf(buf+len, "IEEE 48bit\n"); - break; - case 0x01: - len += sprintf(buf+len, "FC IEEE\n"); - break; - default: - len += sprintf(buf+len, "Unknown (0x%02x)\n", work8[4]); - break; - } - - len += sprintf(buf+len, "State : "); - switch(work8[5]) - { - case 0x00: - len += sprintf(buf+len, "Unknown\n"); - break; - case 0x01: - len += sprintf(buf+len, "Unclaimed\n"); - break; - case 0x02: - len += sprintf(buf+len, "Operational\n"); - break; - case 0x03: - len += sprintf(buf+len, "Suspended\n"); - break; - case 0x04: - len += sprintf(buf+len, "Resetting\n"); - break; - case 0x05: - len += sprintf(buf+len, "ERROR: "); - if(work16[3]&0x0001) - len += sprintf(buf+len, "TxCU inoperative "); - if(work16[3]&0x0002) - len += sprintf(buf+len, "RxCU inoperative "); - if(work16[3]&0x0004) - len += sprintf(buf+len, "Local mem alloc "); - len += sprintf(buf+len, "\n"); - break; - case 0x06: - len += sprintf(buf+len, "Operational no Rx\n"); - break; - case 0x07: - len += sprintf(buf+len, "Suspended no Rx\n"); - break; - default: - len += sprintf(buf+len, "Unspecified\n"); - break; - } - - len += sprintf(buf+len, "Min packet size : %d\n", work32[2]); - len += sprintf(buf+len, "Max packet size : %d\n", work32[3]); - len += sprintf(buf+len, "HW address : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[16],work8[17],work8[18],work8[19], - work8[20],work8[21],work8[22],work8[23]); - - len += sprintf(buf+len, "Max Tx wire speed : %d bps\n", (int)work64[3]); - len += sprintf(buf+len, "Max Rx wire speed : %d bps\n", (int)work64[4]); - - len += sprintf(buf+len, "Min SDU packet size : 0x%08x\n", work32[10]); - len += sprintf(buf+len, "Max SDU packet size : 0x%08x\n", work32[11]); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0001h - MAC address table (scalar) */ -int i2o_proc_read_lan_mac_addr(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[48]; - static u8 *work8 = (u8*)work32; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0001, -1, &work32, 48*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0001 LAN MAC Address"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Active address : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[0],work8[1],work8[2],work8[3], - work8[4],work8[5],work8[6],work8[7]); - len += sprintf(buf+len, "Current address : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[8],work8[9],work8[10],work8[11], - work8[12],work8[13],work8[14],work8[15]); - len += sprintf(buf+len, "Functional address mask : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[16],work8[17],work8[18],work8[19], - work8[20],work8[21],work8[22],work8[23]); - - len += sprintf(buf+len,"HW/DDM capabilities : 0x%08x\n", work32[7]); - len += sprintf(buf+len," [%s] Unicast packets supported\n", - (work32[7]&0x00000001)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous mode supported\n", - (work32[7]&0x00000002)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous multicast mode supported\n", - (work32[7]&0x00000004)?"+":"-"); - len += sprintf(buf+len," [%s] Broadcast reception disabling supported\n", - (work32[7]&0x00000100)?"+":"-"); - len += sprintf(buf+len," [%s] Multicast reception disabling supported\n", - (work32[7]&0x00000200)?"+":"-"); - len += sprintf(buf+len," [%s] Functional address disabling supported\n", - (work32[7]&0x00000400)?"+":"-"); - len += sprintf(buf+len," [%s] MAC reporting supported\n", - (work32[7]&0x00000800)?"+":"-"); - - len += sprintf(buf+len,"Filter mask : 0x%08x\n", work32[6]); - len += sprintf(buf+len," [%s] Unicast packets disable\n", - (work32[6]&0x00000001)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous mode enable\n", - (work32[6]&0x00000002)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous multicast mode enable\n", - (work32[6]&0x00000004)?"+":"-"); - len += sprintf(buf+len," [%s] Broadcast packets disable\n", - (work32[6]&0x00000100)?"+":"-"); - len += sprintf(buf+len," [%s] Multicast packets disable\n", - (work32[6]&0x00000200)?"+":"-"); - len += sprintf(buf+len," [%s] Functional address disable\n", - (work32[6]&0x00000400)?"+":"-"); - - if (work32[7]&0x00000800) { - len += sprintf(buf+len, " MAC reporting mode : "); - if (work32[6]&0x00000800) - len += sprintf(buf+len, "Pass only priority MAC packets to user\n"); - else if (work32[6]&0x00001000) - len += sprintf(buf+len, "Pass all MAC packets to user\n"); - else if (work32[6]&0x00001800) - len += sprintf(buf+len, "Pass all MAC packets (promiscuous) to user\n"); - else - len += sprintf(buf+len, "Do not pass MAC packets to user\n"); - } - len += sprintf(buf+len, "Number of multicast addresses : %d\n", work32[8]); - len += sprintf(buf+len, "Perfect filtering for max %d multicast addresses\n", - work32[9]); - len += sprintf(buf+len, "Imperfect filtering for max %d multicast addresses\n", - work32[10]); - - spin_unlock(&i2o_proc_lock); - - return len; -} - -/* LAN group 0002h - Multicast MAC address table (table) */ -int i2o_proc_read_lan_mcast_addr(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - u8 mc_addr[8]; - - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u8 mc_addr[256][8]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, 0x0002, -1, - NULL, 0, &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x002 LAN Multicast MAC Address"); - spin_unlock(&i2o_proc_lock); - return len; - } - - for (i = 0; i < result.row_count; i++) - { - memcpy(mc_addr, result.mc_addr[i], 8); - - len += sprintf(buf+len, "MC MAC address[%d]: " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - i, mc_addr[0], mc_addr[1], mc_addr[2], - mc_addr[3], mc_addr[4], mc_addr[5], - mc_addr[6], mc_addr[7]); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0003h - Batch Control (scalar) */ -int i2o_proc_read_lan_batch_control(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[9]; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0003, -1, &work32, 9*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0003 LAN Batch Control"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Batch mode "); - if (work32[0]&0x00000001) - len += sprintf(buf+len, "disabled"); - else - len += sprintf(buf+len, "enabled"); - if (work32[0]&0x00000002) - len += sprintf(buf+len, " (current setting)"); - if (work32[0]&0x00000004) - len += sprintf(buf+len, ", forced"); - else - len += sprintf(buf+len, ", toggle"); - len += sprintf(buf+len, "\n"); - - len += sprintf(buf+len, "Max Rx batch count : %d\n", work32[5]); - len += sprintf(buf+len, "Max Rx batch delay : %d\n", work32[6]); - len += sprintf(buf+len, "Max Tx batch delay : %d\n", work32[7]); - len += sprintf(buf+len, "Max Tx batch count : %d\n", work32[8]); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0004h - LAN Operation (scalar) */ -int i2o_proc_read_lan_operation(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[5]; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0004, -1, &work32, 20); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0004 LAN Operation"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Packet prepadding (32b words) : %d\n", work32[0]); - len += sprintf(buf+len, "Transmission error reporting : %s\n", - (work32[1]&1)?"on":"off"); - len += sprintf(buf+len, "Bad packet handling : %s\n", - (work32[1]&0x2)?"by host":"by DDM"); - len += sprintf(buf+len, "Packet orphan limit : %d\n", work32[2]); - - len += sprintf(buf+len, "Tx modes : 0x%08x\n", work32[3]); - len += sprintf(buf+len, " [%s] HW CRC suppression\n", - (work32[3]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW IPv4 checksum\n", - (work32[3]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW TCP checksum\n", - (work32[3]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW UDP checksum\n", - (work32[3]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW RSVP checksum\n", - (work32[3]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW ICMP checksum\n", - (work32[3]&0x00001000) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Loopback suppression enable\n", - (work32[3]&0x00002000) ? "+" : "-"); - - len += sprintf(buf+len, "Rx modes : 0x%08x\n", work32[4]); - len += sprintf(buf+len, " [%s] FCS in payload\n", - (work32[4]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW IPv4 checksum validation\n", - (work32[4]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW TCP checksum validation\n", - (work32[4]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW UDP checksum validation\n", - (work32[4]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW RSVP checksum validation\n", - (work32[4]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW ICMP checksum validation\n", - (work32[4]&0x00001000) ? "+" : "-"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0005h - Media operation (scalar) */ -int i2o_proc_read_lan_media_operation(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u32 connector_type; - u32 connection_type; - u64 current_tx_wire_speed; - u64 current_rx_wire_speed; - u8 duplex_mode; - u8 link_status; - u8 reserved; - u8 duplex_mode_target; - u32 connector_type_target; - u32 connection_type_target; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0005, -1, &result, sizeof(result)); - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x0005 LAN Media Operation"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Connector type : %s\n", - i2o_get_connector_type(result.connector_type)); - len += sprintf(buf+len, "Connection type : %s\n", - i2o_get_connection_type(result.connection_type)); - - len += sprintf(buf+len, "Current Tx wire speed : %d bps\n", (int)result.current_tx_wire_speed); - len += sprintf(buf+len, "Current Rx wire speed : %d bps\n", (int)result.current_rx_wire_speed); - len += sprintf(buf+len, "Duplex mode : %s duplex\n", - (result.duplex_mode)?"Full":"Half"); - - len += sprintf(buf+len, "Link status : "); - switch (result.link_status) - { - case 0x00: - len += sprintf(buf+len, "Unknown\n"); - break; - case 0x01: - len += sprintf(buf+len, "Normal\n"); - break; - case 0x02: - len += sprintf(buf+len, "Failure\n"); - break; - case 0x03: - len += sprintf(buf+len, "Reset\n"); - break; - default: - len += sprintf(buf+len, "Unspecified\n"); - } - - len += sprintf(buf+len, "Duplex mode target : "); - switch (result.duplex_mode_target){ - case 0: - len += sprintf(buf+len, "Half duplex\n"); - break; - case 1: - len += sprintf(buf+len, "Full duplex\n"); - break; - default: - len += sprintf(buf+len, "\n"); - } - - len += sprintf(buf+len, "Connector type target : %s\n", - i2o_get_connector_type(result.connector_type_target)); - len += sprintf(buf+len, "Connection type target : %s\n", - i2o_get_connection_type(result.connection_type_target)); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0006h - Alternate address (table) (optional) */ -int i2o_proc_read_lan_alt_addr(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - u8 alt_addr[8]; - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u8 alt_addr[256][8]; - } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0x0006, -1, NULL, 0, &result, sizeof(result)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x0006 LAN Alternate Address (optional)"); - spin_unlock(&i2o_proc_lock); - return len; - } - - for (i=0; i < result.row_count; i++) - { - memcpy(alt_addr,result.alt_addr[i],8); - len += sprintf(buf+len, "Alternate address[%d]: " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - i, alt_addr[0], alt_addr[1], alt_addr[2], - alt_addr[3], alt_addr[4], alt_addr[5], - alt_addr[6], alt_addr[7]); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - - -/* LAN group 0007h - Transmit info (scalar) */ -int i2o_proc_read_lan_tx_info(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[8]; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0007, -1, &work32, 8*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0007 LAN Transmit Info"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Tx Max SG elements per packet : %d\n", work32[0]); - len += sprintf(buf+len, "Tx Max SG elements per chain : %d\n", work32[1]); - len += sprintf(buf+len, "Tx Max outstanding packets : %d\n", work32[2]); - len += sprintf(buf+len, "Tx Max packets per request : %d\n", work32[3]); - - len += sprintf(buf+len, "Tx modes : 0x%08x\n", work32[4]); - len += sprintf(buf+len, " [%s] No DA in SGL\n", - (work32[4]&0x00000002) ? "+" : "-"); - len += sprintf(buf+len, " [%s] CRC suppression\n", - (work32[4]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] MAC insertion\n", - (work32[4]&0x00000010) ? "+" : "-"); - len += sprintf(buf+len, " [%s] RIF insertion\n", - (work32[4]&0x00000020) ? "+" : "-"); - len += sprintf(buf+len, " [%s] IPv4 checksum generation\n", - (work32[4]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] TCP checksum generation\n", - (work32[4]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] UDP checksum generation\n", - (work32[4]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] RSVP checksum generation\n", - (work32[4]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] ICMP checksum generation\n", - (work32[4]&0x00001000) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Loopback enabled\n", - (work32[4]&0x00010000) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Loopback suppression enabled\n", - (work32[4]&0x00020000) ? "+" : "-"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0008h - Receive info (scalar) */ -int i2o_proc_read_lan_rx_info(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[8]; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0008, -1, &work32, 8*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0008 LAN Receive Info"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf ,"Rx Max size of chain element : %d\n", work32[0]); - len += sprintf(buf+len, "Rx Max Buckets : %d\n", work32[1]); - len += sprintf(buf+len, "Rx Max Buckets in Reply : %d\n", work32[3]); - len += sprintf(buf+len, "Rx Max Packets in Bucket : %d\n", work32[4]); - len += sprintf(buf+len, "Rx Max Buckets in Post : %d\n", work32[5]); - - len += sprintf(buf+len, "Rx Modes : 0x%08x\n", work32[2]); - len += sprintf(buf+len, " [%s] FCS reception\n", - (work32[2]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] IPv4 checksum validation \n", - (work32[2]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] TCP checksum validation \n", - (work32[2]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] UDP checksum validation \n", - (work32[2]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] RSVP checksum validation \n", - (work32[2]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] ICMP checksum validation \n", - (work32[2]&0x00001000) ? "+" : "-"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -static int i2o_report_opt_field(char *buf, char *field_name, - int field_nbr, int supp_fields, u64 *value) -{ - if (supp_fields & (1 << field_nbr)) - return sprintf(buf, "%-24s : " FMT_U64_HEX "\n", field_name, U64_VAL(value)); - else - return sprintf(buf, "%-24s : Not supported\n", field_name); -} - -/* LAN group 0100h - LAN Historical statistics (scalar) */ -/* LAN group 0180h - Supported Optional Historical Statistics (scalar) */ -/* LAN group 0182h - Optional Non Media Specific Transmit Historical Statistics (scalar) */ -/* LAN group 0183h - Optional Non Media Specific Receive Historical Statistics (scalar) */ - -int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u64 tx_packets; - u64 tx_bytes; - u64 rx_packets; - u64 rx_bytes; - u64 tx_errors; - u64 rx_errors; - u64 rx_dropped; - u64 adapter_resets; - u64 adapter_suspends; - } stats; // 0x0100 - - static u64 supp_groups[4]; // 0x0180 - - struct - { - u64 tx_retries; - u64 tx_directed_bytes; - u64 tx_directed_packets; - u64 tx_multicast_bytes; - u64 tx_multicast_packets; - u64 tx_broadcast_bytes; - u64 tx_broadcast_packets; - u64 tx_group_addr_packets; - u64 tx_short_packets; - } tx_stats; // 0x0182 - - struct - { - u64 rx_crc_errors; - u64 rx_directed_bytes; - u64 rx_directed_packets; - u64 rx_multicast_bytes; - u64 rx_multicast_packets; - u64 rx_broadcast_bytes; - u64 rx_broadcast_packets; - u64 rx_group_addr_packets; - u64 rx_short_packets; - u64 rx_long_packets; - u64 rx_runt_packets; - } rx_stats; // 0x0183 - - struct - { - u64 ipv4_generate; - u64 ipv4_validate_success; - u64 ipv4_validate_errors; - u64 tcp_generate; - u64 tcp_validate_success; - u64 tcp_validate_errors; - u64 udp_generate; - u64 udp_validate_success; - u64 udp_validate_errors; - u64 rsvp_generate; - u64 rsvp_validate_success; - u64 rsvp_validate_errors; - u64 icmp_generate; - u64 icmp_validate_success; - u64 icmp_validate_errors; - } chksum_stats; // 0x0184 - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0100, -1, &stats, sizeof(stats)); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x100 LAN Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Tx packets : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_packets)); - len += sprintf(buf+len, "Tx bytes : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_bytes)); - len += sprintf(buf+len, "Rx packets : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_packets)); - len += sprintf(buf+len, "Rx bytes : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_bytes)); - len += sprintf(buf+len, "Tx errors : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_errors)); - len += sprintf(buf+len, "Rx errors : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_errors)); - len += sprintf(buf+len, "Rx dropped : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_dropped)); - len += sprintf(buf+len, "Adapter resets : " FMT_U64_HEX "\n", - U64_VAL(&stats.adapter_resets)); - len += sprintf(buf+len, "Adapter suspends : " FMT_U64_HEX "\n", - U64_VAL(&stats.adapter_suspends)); - - /* Optional statistics follows */ - /* Get 0x0180 to see which optional groups/fields are supported */ - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0180, -1, &supp_groups, sizeof(supp_groups)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x180 LAN Supported Optional Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - if (supp_groups[1]) /* 0x0182 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0182, -1, &tx_stats, sizeof(tx_stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x182 LAN Optional Tx Historical Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "==== Optional TX statistics (group 0182h)\n"); - - len += i2o_report_opt_field(buf+len, "Tx RetryCount", - 0, supp_groups[1], &tx_stats.tx_retries); - len += i2o_report_opt_field(buf+len, "Tx DirectedBytes", - 1, supp_groups[1], &tx_stats.tx_directed_bytes); - len += i2o_report_opt_field(buf+len, "Tx DirectedPackets", - 2, supp_groups[1], &tx_stats.tx_directed_packets); - len += i2o_report_opt_field(buf+len, "Tx MulticastBytes", - 3, supp_groups[1], &tx_stats.tx_multicast_bytes); - len += i2o_report_opt_field(buf+len, "Tx MulticastPackets", - 4, supp_groups[1], &tx_stats.tx_multicast_packets); - len += i2o_report_opt_field(buf+len, "Tx BroadcastBytes", - 5, supp_groups[1], &tx_stats.tx_broadcast_bytes); - len += i2o_report_opt_field(buf+len, "Tx BroadcastPackets", - 6, supp_groups[1], &tx_stats.tx_broadcast_packets); - len += i2o_report_opt_field(buf+len, "Tx TotalGroupAddrPackets", - 7, supp_groups[1], &tx_stats.tx_group_addr_packets); - len += i2o_report_opt_field(buf+len, "Tx TotalPacketsTooShort", - 8, supp_groups[1], &tx_stats.tx_short_packets); - } - - if (supp_groups[2]) /* 0x0183 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0183, -1, &rx_stats, sizeof(rx_stats)); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x183 LAN Optional Rx Historical Stats"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "==== Optional RX statistics (group 0183h)\n"); - - len += i2o_report_opt_field(buf+len, "Rx CRCErrorCount", - 0, supp_groups[2], &rx_stats.rx_crc_errors); - len += i2o_report_opt_field(buf+len, "Rx DirectedBytes", - 1, supp_groups[2], &rx_stats.rx_directed_bytes); - len += i2o_report_opt_field(buf+len, "Rx DirectedPackets", - 2, supp_groups[2], &rx_stats.rx_directed_packets); - len += i2o_report_opt_field(buf+len, "Rx MulticastBytes", - 3, supp_groups[2], &rx_stats.rx_multicast_bytes); - len += i2o_report_opt_field(buf+len, "Rx MulticastPackets", - 4, supp_groups[2], &rx_stats.rx_multicast_packets); - len += i2o_report_opt_field(buf+len, "Rx BroadcastBytes", - 5, supp_groups[2], &rx_stats.rx_broadcast_bytes); - len += i2o_report_opt_field(buf+len, "Rx BroadcastPackets", - 6, supp_groups[2], &rx_stats.rx_broadcast_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalGroupAddrPackets", - 7, supp_groups[2], &rx_stats.rx_group_addr_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalPacketsTooShort", - 8, supp_groups[2], &rx_stats.rx_short_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalPacketsTooLong", - 9, supp_groups[2], &rx_stats.rx_long_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalPacketsRunt", - 10, supp_groups[2], &rx_stats.rx_runt_packets); - } - - if (supp_groups[3]) /* 0x0184 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0184, -1, &chksum_stats, sizeof(chksum_stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x184 LAN Optional Chksum Historical Stats"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "==== Optional CHKSUM statistics (group 0x0184)\n"); - - len += i2o_report_opt_field(buf+len, "IPv4 Generate", - 0, supp_groups[3], &chksum_stats.ipv4_generate); - len += i2o_report_opt_field(buf+len, "IPv4 ValidateSuccess", - 1, supp_groups[3], &chksum_stats.ipv4_validate_success); - len += i2o_report_opt_field(buf+len, "IPv4 ValidateError", - 2, supp_groups[3], &chksum_stats.ipv4_validate_errors); - len += i2o_report_opt_field(buf+len, "TCP Generate", - 3, supp_groups[3], &chksum_stats.tcp_generate); - len += i2o_report_opt_field(buf+len, "TCP ValidateSuccess", - 4, supp_groups[3], &chksum_stats.tcp_validate_success); - len += i2o_report_opt_field(buf+len, "TCP ValidateError", - 5, supp_groups[3], &chksum_stats.tcp_validate_errors); - len += i2o_report_opt_field(buf+len, "UDP Generate", - 6, supp_groups[3], &chksum_stats.udp_generate); - len += i2o_report_opt_field(buf+len, "UDP ValidateSuccess", - 7, supp_groups[3], &chksum_stats.udp_validate_success); - len += i2o_report_opt_field(buf+len, "UDP ValidateError", - 8, supp_groups[3], &chksum_stats.udp_validate_errors); - len += i2o_report_opt_field(buf+len, "RSVP Generate", - 9, supp_groups[3], &chksum_stats.rsvp_generate); - len += i2o_report_opt_field(buf+len, "RSVP ValidateSuccess", - 10, supp_groups[3], &chksum_stats.rsvp_validate_success); - len += i2o_report_opt_field(buf+len, "RSVP ValidateError", - 11, supp_groups[3], &chksum_stats.rsvp_validate_errors); - len += i2o_report_opt_field(buf+len, "ICMP Generate", - 12, supp_groups[3], &chksum_stats.icmp_generate); - len += i2o_report_opt_field(buf+len, "ICMP ValidateSuccess", - 13, supp_groups[3], &chksum_stats.icmp_validate_success); - len += i2o_report_opt_field(buf+len, "ICMP ValidateError", - 14, supp_groups[3], &chksum_stats.icmp_validate_errors); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0200h - Required Ethernet Statistics (scalar) */ -/* LAN group 0280h - Optional Ethernet Statistics Supported (scalar) */ -/* LAN group 0281h - Optional Ethernet Historical Statistics (scalar) */ -int i2o_proc_read_lan_eth_stats(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u64 rx_align_errors; - u64 tx_one_collisions; - u64 tx_multiple_collisions; - u64 tx_deferred; - u64 tx_late_collisions; - u64 tx_max_collisions; - u64 tx_carrier_lost; - u64 tx_excessive_deferrals; - } stats; - - static u64 supp_fields; - struct - { - u64 rx_overrun; - u64 tx_underrun; - u64 tx_heartbeat_failure; - } hist_stats; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0200, -1, &stats, sizeof(stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0200 LAN Ethernet Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Rx alignment errors : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_align_errors)); - len += sprintf(buf+len, "Tx one collisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_one_collisions)); - len += sprintf(buf+len, "Tx multicollisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_multiple_collisions)); - len += sprintf(buf+len, "Tx deferred : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_deferred)); - len += sprintf(buf+len, "Tx late collisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_late_collisions)); - len += sprintf(buf+len, "Tx max collisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_max_collisions)); - len += sprintf(buf+len, "Tx carrier lost : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_carrier_lost)); - len += sprintf(buf+len, "Tx excessive deferrals : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_excessive_deferrals)); - - /* Optional Ethernet statistics follows */ - /* Get 0x0280 to see which optional fields are supported */ - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0280, -1, &supp_fields, sizeof(supp_fields)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0280 LAN Supported Optional Ethernet Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - if (supp_fields) /* 0x0281 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0281, -1, &stats, sizeof(stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0281 LAN Optional Ethernet Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "==== Optional ETHERNET statistics (group 0x0281)\n"); - - len += i2o_report_opt_field(buf+len, "Rx Overrun", - 0, supp_fields, &hist_stats.rx_overrun); - len += i2o_report_opt_field(buf+len, "Tx Underrun", - 1, supp_fields, &hist_stats.tx_underrun); - len += i2o_report_opt_field(buf+len, "Tx HeartbeatFailure", - 2, supp_fields, &hist_stats.tx_heartbeat_failure); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0300h - Required Token Ring Statistics (scalar) */ -/* LAN group 0380h, 0381h - Optional Statistics not yet defined (TODO) */ -int i2o_proc_read_lan_tr_stats(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u64 work64[13]; - int token; - - static char *ring_status[] = - { - "", - "", - "", - "", - "", - "Ring Recovery", - "Single Station", - "Counter Overflow", - "Remove Received", - "", - "Auto-Removal Error 1", - "Lobe Wire Fault", - "Transmit Beacon", - "Soft Error", - "Hard Error", - "Signal Loss" - }; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0300, -1, &work64, sizeof(work64)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0300 Token Ring Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "LineErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[0])); - len += sprintf(buf+len, "LostFrames : " FMT_U64_HEX "\n", - U64_VAL(&work64[1])); - len += sprintf(buf+len, "ACError : " FMT_U64_HEX "\n", - U64_VAL(&work64[2])); - len += sprintf(buf+len, "TxAbortDelimiter : " FMT_U64_HEX "\n", - U64_VAL(&work64[3])); - len += sprintf(buf+len, "BursErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[4])); - len += sprintf(buf+len, "FrameCopiedErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[5])); - len += sprintf(buf+len, "FrequencyErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[6])); - len += sprintf(buf+len, "InternalErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[7])); - len += sprintf(buf+len, "LastRingStatus : %s\n", ring_status[work64[8]]); - len += sprintf(buf+len, "TokenError : " FMT_U64_HEX "\n", - U64_VAL(&work64[9])); - len += sprintf(buf+len, "UpstreamNodeAddress : " FMT_U64_HEX "\n", - U64_VAL(&work64[10])); - len += sprintf(buf+len, "LastRingID : " FMT_U64_HEX "\n", - U64_VAL(&work64[11])); - len += sprintf(buf+len, "LastBeaconType : " FMT_U64_HEX "\n", - U64_VAL(&work64[12])); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0400h - Required FDDI Statistics (scalar) */ -/* LAN group 0480h, 0481h - Optional Statistics, not yet defined (TODO) */ -int i2o_proc_read_lan_fddi_stats(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u64 work64[11]; - int token; - - static char *conf_state[] = - { - "Isolated", - "Local a", - "Local b", - "Local ab", - "Local s", - "Wrap a", - "Wrap b", - "Wrap ab", - "Wrap s", - "C-Wrap a", - "C-Wrap b", - "C-Wrap s", - "Through", - }; - - static char *ring_state[] = - { - "Isolated", - "Non-op", - "Rind-op", - "Detect", - "Non-op-Dup", - "Ring-op-Dup", - "Directed", - "Trace" - }; - - static char *link_state[] = - { - "Off", - "Break", - "Trace", - "Connect", - "Next", - "Signal", - "Join", - "Verify", - "Active", - "Maintenance" - }; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0400, -1, &work64, sizeof(work64)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0400 FDDI Required Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "ConfigurationState : %s\n", conf_state[work64[0]]); - len += sprintf(buf+len, "UpstreamNode : " FMT_U64_HEX "\n", - U64_VAL(&work64[1])); - len += sprintf(buf+len, "DownStreamNode : " FMT_U64_HEX "\n", - U64_VAL(&work64[2])); - len += sprintf(buf+len, "FrameErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[3])); - len += sprintf(buf+len, "FramesLost : " FMT_U64_HEX "\n", - U64_VAL(&work64[4])); - len += sprintf(buf+len, "RingMgmtState : %s\n", ring_state[work64[5]]); - len += sprintf(buf+len, "LCTFailures : " FMT_U64_HEX "\n", - U64_VAL(&work64[6])); - len += sprintf(buf+len, "LEMRejects : " FMT_U64_HEX "\n", - U64_VAL(&work64[7])); - len += sprintf(buf+len, "LEMCount : " FMT_U64_HEX "\n", - U64_VAL(&work64[8])); - len += sprintf(buf+len, "LConnectionState : %s\n", - link_state[work64[9]]); - - spin_unlock(&i2o_proc_lock); - return len; -} - -static int i2o_proc_create_entries(void *data, i2o_proc_entry *pentry, - struct proc_dir_entry *parent) -{ - struct proc_dir_entry *ent; - - while(pentry->name != NULL) - { - ent = create_proc_entry(pentry->name, pentry->mode, parent); - if(!ent) return -1; - - ent->data = data; - ent->read_proc = pentry->read_proc; - ent->write_proc = pentry->write_proc; - ent->nlink = 1; - - pentry++; - } - - return 0; -} - -static void i2o_proc_remove_entries(i2o_proc_entry *pentry, - struct proc_dir_entry *parent) -{ - while(pentry->name != NULL) - { - remove_proc_entry(pentry->name, parent); - pentry++; - } -} - -static int i2o_proc_add_controller(struct i2o_controller *pctrl, - struct proc_dir_entry *root ) -{ - struct proc_dir_entry *dir, *dir1; - struct i2o_device *dev; - char buff[10]; - - sprintf(buff, "iop%d", pctrl->unit); - - dir = proc_mkdir(buff, root); - if(!dir) - return -1; - - pctrl->proc_entry = dir; - - i2o_proc_create_entries(pctrl, generic_iop_entries, dir); - - for(dev = pctrl->devices; dev; dev = dev->next) - { - sprintf(buff, "%0#5x", dev->lct_data.tid); - - dir1 = proc_mkdir(buff, dir); - dev->proc_entry = dir1; - - if(!dir1) - printk(KERN_INFO "i2o_proc: Could not allocate proc dir\n"); - - i2o_proc_add_device(dev, dir1); - } - - return 0; -} - -void i2o_proc_new_dev(struct i2o_controller *c, struct i2o_device *d) -{ - char buff[10]; - -#ifdef DRIVERDEBUG - printk(KERN_INFO "Adding new device to /proc/i2o/iop%d\n", c->unit); -#endif - sprintf(buff, "%0#5x", d->lct_data.tid); - - d->proc_entry = proc_mkdir(buff, c->proc_entry); - - if(!d->proc_entry) - { - printk(KERN_WARNING "i2o: Could not allocate procdir!\n"); - return; - } - - i2o_proc_add_device(d, d->proc_entry); -} - -void i2o_proc_add_device(struct i2o_device *dev, struct proc_dir_entry *dir) -{ - i2o_proc_create_entries(dev, generic_dev_entries, dir); - - /* Inform core that we want updates about this device's status */ - i2o_device_notify_on(dev, &i2o_proc_handler); - switch(dev->lct_data.class_id) - { - case I2O_CLASS_SCSI_PERIPHERAL: - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - i2o_proc_create_entries(dev, rbs_dev_entries, dir); - break; - case I2O_CLASS_LAN: - i2o_proc_create_entries(dev, lan_entries, dir); - switch(dev->lct_data.sub_class) - { - case I2O_LAN_ETHERNET: - i2o_proc_create_entries(dev, lan_eth_entries, dir); - break; - case I2O_LAN_FDDI: - i2o_proc_create_entries(dev, lan_fddi_entries, dir); - break; - case I2O_LAN_TR: - i2o_proc_create_entries(dev, lan_tr_entries, dir); - break; - default: - break; - } - break; - default: - break; - } -} - -static void i2o_proc_remove_controller(struct i2o_controller *pctrl, - struct proc_dir_entry *parent) -{ - char buff[10]; - struct i2o_device *dev; - - /* Remove unused device entries */ - for(dev=pctrl->devices; dev; dev=dev->next) - i2o_proc_remove_device(dev); - - if(!atomic_read(&pctrl->proc_entry->count)) - { - sprintf(buff, "iop%d", pctrl->unit); - - i2o_proc_remove_entries(generic_iop_entries, pctrl->proc_entry); - - remove_proc_entry(buff, parent); - pctrl->proc_entry = NULL; - } -} - -void i2o_proc_remove_device(struct i2o_device *dev) -{ - struct proc_dir_entry *de=dev->proc_entry; - char dev_id[10]; - - sprintf(dev_id, "%0#5x", dev->lct_data.tid); - - i2o_device_notify_off(dev, &i2o_proc_handler); - /* Would it be safe to remove _files_ even if they are in use? */ - if((de) && (!atomic_read(&de->count))) - { - i2o_proc_remove_entries(generic_dev_entries, de); - switch(dev->lct_data.class_id) - { - case I2O_CLASS_SCSI_PERIPHERAL: - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - i2o_proc_remove_entries(rbs_dev_entries, de); - break; - case I2O_CLASS_LAN: - { - i2o_proc_remove_entries(lan_entries, de); - switch(dev->lct_data.sub_class) - { - case I2O_LAN_ETHERNET: - i2o_proc_remove_entries(lan_eth_entries, de); - break; - case I2O_LAN_FDDI: - i2o_proc_remove_entries(lan_fddi_entries, de); - break; - case I2O_LAN_TR: - i2o_proc_remove_entries(lan_tr_entries, de); - break; - } - } - remove_proc_entry(dev_id, dev->controller->proc_entry); - } - } -} - -void i2o_proc_dev_del(struct i2o_controller *c, struct i2o_device *d) -{ -#ifdef DRIVERDEBUG - printk(KERN_INFO, "Deleting device %d from iop%d\n", - d->lct_data.tid, c->unit); -#endif - - i2o_proc_remove_device(d); -} - -static int create_i2o_procfs(void) -{ - struct i2o_controller *pctrl = NULL; - int i; - - i2o_proc_dir_root = proc_mkdir("i2o", 0); - if(!i2o_proc_dir_root) - return -1; - - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - pctrl = i2o_find_controller(i); - if(pctrl) - { - i2o_proc_add_controller(pctrl, i2o_proc_dir_root); - i2o_unlock_controller(pctrl); - } - }; - - return 0; -} - -static int __exit destroy_i2o_procfs(void) -{ - struct i2o_controller *pctrl = NULL; - int i; - - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - pctrl = i2o_find_controller(i); - if(pctrl) - { - i2o_proc_remove_controller(pctrl, i2o_proc_dir_root); - i2o_unlock_controller(pctrl); - } - } - - if(!atomic_read(&i2o_proc_dir_root->count)) - remove_proc_entry("i2o", 0); - else - return -1; - - return 0; -} - -int __init i2o_proc_init(void) -{ - if (i2o_install_handler(&i2o_proc_handler) < 0) - { - printk(KERN_ERR "i2o_proc: Unable to install PROC handler.\n"); - return 0; - } - - if(create_i2o_procfs()) - return -EBUSY; - - return 0; -} - -MODULE_AUTHOR("Deepak Saxena"); -MODULE_DESCRIPTION("I2O procfs Handler"); - -static void __exit i2o_proc_exit(void) -{ - destroy_i2o_procfs(); - i2o_remove_handler(&i2o_proc_handler); -} - -#ifdef MODULE -module_init(i2o_proc_init); -#endif -module_exit(i2o_proc_exit); - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_scsi.c linux.ac/drivers/i2o/i2o_scsi.c --- linux.vanilla/drivers/i2o/i2o_scsi.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/i2o/i2o_scsi.c Thu Jan 1 01:00:00 1970 @@ -1,911 +0,0 @@ -/* - * 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, 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. - * - * Complications for I2O scsi - * - * o Each (bus,lun) is a logical device in I2O. We keep a map - * table. We spoof failed selection for unmapped units - * o Request sense buffers can come back for free. - * o Scatter gather is a bit dynamic. We have to investigate at - * setup time. - * o Some of our resources are dynamically shared. The i2o core - * needs a message reservation protocol to avoid swap v net - * deadlocking. We need to back off queue requests. - * - * In general the firmware wants to help. Where its help isn't performance - * useful we just ignore the aid. Its not worth the code in truth. - * - * Fixes: - * Steve Ralston : Scatter gather now works - * - * To Do - * 64bit cleanups - * Fix the resource management problems. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../scsi/scsi.h" -#include "../scsi/hosts.h" -#include "../scsi/sd.h" -#include "i2o_scsi.h" - -#define VERSION_STRING "Version 0.0.1" - -#define dprintk(x) - -#define MAXHOSTS 32 - -struct i2o_scsi_host -{ - struct i2o_controller *controller; - s16 task[16][8]; /* Allow 16 devices for now */ - unsigned long tagclock[16][8]; /* Tag clock for queueing */ - s16 bus_task; /* The adapter TID */ -}; - -static int scsi_context; -static int lun_done; -static int i2o_scsi_hosts; - -static u32 *retry[32]; -static struct i2o_controller *retry_ctrl[32]; -static struct timer_list retry_timer; -static int retry_ct = 0; - -static atomic_t queue_depth; - -/* - * SG Chain buffer support... - */ - -#define SG_MAX_FRAGS 64 - -/* - * FIXME: we should allocate one of these per bus we find as we - * locate them not in a lump at boot. - */ - -typedef struct _chain_buf -{ - u32 sg_flags_cnt[SG_MAX_FRAGS]; - u32 sg_buf[SG_MAX_FRAGS]; -} chain_buf; - -#define SG_CHAIN_BUF_SZ sizeof(chain_buf) - -#define SG_MAX_BUFS (i2o_num_controllers * I2O_SCSI_CAN_QUEUE) -#define SG_CHAIN_POOL_SZ (SG_MAX_BUFS * SG_CHAIN_BUF_SZ) - -static int max_sg_len = 0; -static chain_buf *sg_chain_pool = NULL; -static int sg_chain_tag = 0; -static int sg_max_frags = SG_MAX_FRAGS; - -/* - * Retry congested frames. This actually needs pushing down into - * i2o core. We should only bother the OSM with this when we can't - * queue and retry the frame. Or perhaps we should call the OSM - * and its default handler should be this in the core, and this - * call a 2nd "I give up" handler in the OSM ? - */ - -static void i2o_retry_run(unsigned long f) -{ - int i; - unsigned long flags; - - save_flags(flags); - cli(); - - for(i=0;i>12)&0xFFF, - m[1]&0xFFF, - m[1]>>24); - printk("Failure Code %d.\n", m[4]>>24); - if(m[4]&(1<<16)) - printk("Format error.\n"); - if(m[4]&(1<<17)) - printk("Path error.\n"); - if(m[4]&(1<<18)) - printk("Path State.\n"); - if(m[4]&(1<<18)) - printk("Congestion.\n"); - - m=(u32 *)bus_to_virt(m[7]); - printk("Failing message is %p.\n", m); - - if((m[4]&(1<<18)) && retry_ct < 32) - { - retry_ctrl[retry_ct]=c; - retry[retry_ct]=m; - if(!retry_ct++) - { - retry_timer.expires=jiffies+1; - add_timer(&retry_timer); - } - } - else - { - /* Create a scsi error for this */ - current_command = (Scsi_Cmnd *)m[3]; - printk("Aborted %ld\n", current_command->serial_number); - - spin_lock_irq(&io_request_lock); - current_command->result = DID_ERROR << 16; - current_command->scsi_done(current_command); - spin_unlock_irq(&io_request_lock); - - /* Now flush the message by making it a NOP */ - m[0]&=0x00FFFFFF; - m[0]|=(I2O_CMD_UTIL_NOP)<<24; - i2o_post_message(c,virt_to_bus(m)); - } - return; - } - - - /* - * Low byte is device status, next is adapter status, - * (then one byte reserved), then request status. - */ - ds=(u8)m[4]; - as=(u8)(m[4]>>8); - st=(u8)(m[4]>>24); - - dprintk(("i2o got a scsi reply %08X: ", m[0])); - dprintk(("m[2]=%08X: ", m[2])); - dprintk(("m[4]=%08X\n", m[4])); - - if(m[2]&0x80000000) - { - if(m[2]&0x40000000) - { - dprintk(("Event.\n")); - lun_done=1; - return; - } - printk(KERN_ERR "i2o_scsi: bus reset reply.\n"); - return; - } - - current_command = (Scsi_Cmnd *)m[3]; - - /* - * Is this a control request coming back - eg an abort ? - */ - - if(current_command==NULL) - { - if(st) - dprintk(("SCSI abort: %08X", m[4])); - dprintk(("SCSI abort completed.\n")); - return; - } - - dprintk(("Completed %ld\n", current_command->serial_number)); - - atomic_dec(&queue_depth); - - if(st == 0x06) - { - if(m[5] < current_command->underflow) - { - int i; - printk(KERN_ERR "SCSI: underflow 0x%08X 0x%08X\n", - m[5], current_command->underflow); - printk("Cmd: "); - for(i=0;i<15;i++) - printk("%02X ", current_command->cmnd[i]); - printk(".\n"); - } - else st=0; - } - - if(st) - { - /* An error has occurred */ - - dprintk((KERN_DEBUG "SCSI error %08X", m[4])); - - if (as == 0x0E) - /* SCSI Reset */ - current_command->result = DID_RESET << 16; - else if (as == 0x0F) - current_command->result = DID_PARITY << 16; - else - current_command->result = DID_ERROR << 16; - } - else - /* - * It worked maybe ? - */ - current_command->result = DID_OK << 16 | ds; - spin_lock(&io_request_lock); - current_command->scsi_done(current_command); - spin_unlock(&io_request_lock); - return; -} - -struct i2o_handler i2o_scsi_handler= -{ - i2o_scsi_reply, - NULL, - NULL, - NULL, - "I2O SCSI OSM", - 0, - I2O_CLASS_SCSI_PERIPHERAL -}; - -static int i2o_find_lun(struct i2o_controller *c, struct i2o_device *d, int *target, int *lun) -{ - u8 reply[8]; - - if(i2o_query_scalar(c, d->lct_data.tid, 0, 3, reply, 4)<0) - return -1; - - *target=reply[0]; - - if(i2o_query_scalar(c, d->lct_data.tid, 0, 4, reply, 8)<0) - return -1; - - *lun=reply[1]; - - dprintk(("SCSI (%d,%d)\n", *target, *lun)); - return 0; -} - -void i2o_scsi_init(struct i2o_controller *c, struct i2o_device *d, struct Scsi_Host *shpnt) -{ - struct i2o_device *unit; - struct i2o_scsi_host *h =(struct i2o_scsi_host *)shpnt->hostdata; - int lun; - int target; - - h->controller=c; - h->bus_task=d->lct_data.tid; - - for(target=0;target<16;target++) - for(lun=0;lun<8;lun++) - h->task[target][lun] = -1; - - for(unit=c->devices;unit!=NULL;unit=unit->next) - { - dprintk(("Class %03X, parent %d, want %d.\n", - unit->lct_data.class_id, unit->lct_data.parent_tid, d->lct_data.tid)); - - /* Only look at scsi and fc devices */ - if ( (unit->lct_data.class_id != I2O_CLASS_SCSI_PERIPHERAL) - && (unit->lct_data.class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL) - ) - continue; - - /* On our bus ? */ - dprintk(("Found a disk (%d).\n", unit->lct_data.tid)); - if ((unit->lct_data.parent_tid == d->lct_data.tid) - || (unit->lct_data.parent_tid == d->lct_data.parent_tid) - ) - { - u16 limit; - dprintk(("Its ours.\n")); - if(i2o_find_lun(c, unit, &target, &lun)==-1) - { - printk(KERN_ERR "i2o_scsi: Unable to get lun for tid %d.\n", unit->lct_data.tid); - continue; - } - dprintk(("Found disk %d %d.\n", target, lun)); - h->task[target][lun]=unit->lct_data.tid; - h->tagclock[target][lun]=jiffies; - - /* Get the max fragments/request */ - i2o_query_scalar(c, d->lct_data.tid, 0xF103, 3, &limit, 2); - - /* sanity */ - if ( limit == 0 ) - { - printk(KERN_WARNING "i2o_scsi: Ignoring unreasonable SG limit of 0 from IOP!\n"); - limit = 1; - } - - shpnt->sg_tablesize = limit; - - dprintk(("i2o_scsi: set scatter-gather to %d.\n", - shpnt->sg_tablesize)); - } - } -} - -int i2o_scsi_detect(Scsi_Host_Template * tpnt) -{ - unsigned long flags; - struct Scsi_Host *shpnt = NULL; - int i; - int count; - - printk("i2o_scsi.c: %s\n", VERSION_STRING); - - if(i2o_install_handler(&i2o_scsi_handler)<0) - { - printk(KERN_ERR "i2o_scsi: Unable to install OSM handler.\n"); - return 0; - } - scsi_context = i2o_scsi_handler.context; - - if((sg_chain_pool = kmalloc(SG_CHAIN_POOL_SZ, GFP_KERNEL)) == NULL) - { - printk("i2o_scsi: Unable to alloc %d byte SG chain buffer pool.\n", SG_CHAIN_POOL_SZ); - printk("i2o_scsi: SG chaining DISABLED!\n"); - sg_max_frags = 11; - } - else - { - printk(" chain_pool: %d bytes @ %p\n", SG_CHAIN_POOL_SZ, sg_chain_pool); - printk(" (%d byte buffers X %d can_queue X %d i2o controllers)\n", - SG_CHAIN_BUF_SZ, I2O_SCSI_CAN_QUEUE, i2o_num_controllers); - sg_max_frags = SG_MAX_FRAGS; // 64 - } - - init_timer(&retry_timer); - retry_timer.data = 0UL; - retry_timer.function = i2o_retry_run; - -// printk("SCSI OSM at %d.\n", scsi_context); - - for (count = 0, i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - struct i2o_controller *c=i2o_find_controller(i); - struct i2o_device *d; - /* - * This controller doesn't exist. - */ - - if(c==NULL) - continue; - - /* - * Fixme - we need some altered device locking. This - * is racing with device addition in theory. Easy to fix. - */ - - for(d=c->devices;d!=NULL;d=d->next) - { - /* - * bus_adapter, SCSI (obsolete), or FibreChannel busses only - */ - if( (d->lct_data.class_id!=I2O_CLASS_BUS_ADAPTER_PORT) // bus_adapter -// && (d->lct_data.class_id!=I2O_CLASS_FIBRE_CHANNEL_PORT) // FC_PORT - ) - continue; - - shpnt = scsi_register(tpnt, sizeof(struct i2o_scsi_host)); - if(shpnt==NULL) - continue; - save_flags(flags); - cli(); - shpnt->unique_id = (u32)d; - shpnt->io_port = 0; - shpnt->n_io_port = 0; - shpnt->irq = 0; - shpnt->this_id = /* Good question */15; - restore_flags(flags); - i2o_scsi_init(c, d, shpnt); - count++; - } - } - i2o_scsi_hosts = count; - - if(count==0) - { - if(sg_chain_pool!=NULL) - { - kfree(sg_chain_pool); - sg_chain_pool = NULL; - } - flush_pending(); - del_timer(&retry_timer); - i2o_remove_handler(&i2o_scsi_handler); - } - - return count; -} - -int i2o_scsi_release(struct Scsi_Host *host) -{ - if(--i2o_scsi_hosts==0) - { - if(sg_chain_pool!=NULL) - { - kfree(sg_chain_pool); - sg_chain_pool = NULL; - } - flush_pending(); - del_timer(&retry_timer); - i2o_remove_handler(&i2o_scsi_handler); - } - return 0; -} - - -const char *i2o_scsi_info(struct Scsi_Host *SChost) -{ - struct i2o_scsi_host *hostdata; - - hostdata = (struct i2o_scsi_host *)SChost->hostdata; - - return(&hostdata->controller->name[0]); -} - - -/* - * From the wd93 driver: - * Returns true if there will be a DATA_OUT phase with this command, - * false otherwise. - * (Thanks to Joerg Dorchain for the research and suggestion.) - * - */ -static int is_dir_out(Scsi_Cmnd *cmd) -{ - switch (cmd->cmnd[0]) - { - case WRITE_6: case WRITE_10: case WRITE_12: - case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER: - case WRITE_VERIFY: case WRITE_VERIFY_12: - case COMPARE: case COPY: case COPY_VERIFY: - case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: - case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: - case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: - case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: - case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK: - case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: - case 0xea: - return 1; - default: - return 0; - } -} - -int i2o_scsi_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) -{ - int i; - int tid; - struct i2o_controller *c; - Scsi_Cmnd *current_command; - struct Scsi_Host *host; - struct i2o_scsi_host *hostdata; - u32 *msg, *mptr; - u32 m; - u32 *lenptr; - int direction; - int scsidir; - u32 len; - u32 reqlen; - u32 tag; - - static int max_qd = 1; - - /* - * Do the incoming paperwork - */ - - host = SCpnt->host; - hostdata = (struct i2o_scsi_host *)host->hostdata; - SCpnt->scsi_done = done; - - if(SCpnt->target > 15) - { - printk(KERN_ERR "i2o_scsi: Wild target %d.\n", SCpnt->target); - return -1; - } - - tid = hostdata->task[SCpnt->target][SCpnt->lun]; - - dprintk(("qcmd: Tid = %d\n", tid)); - - current_command = SCpnt; /* set current command */ - current_command->scsi_done = done; /* set ptr to done function */ - - /* We don't have such a device. Pretend we did the command - and that selection timed out */ - - if(tid == -1) - { - SCpnt->result = DID_NO_CONNECT << 16; - done(SCpnt); - return 0; - } - - dprintk(("Real scsi messages.\n")); - - c = hostdata->controller; - - /* - * Obtain an I2O message. Right now we _have_ to obtain one - * until the scsi layer stuff is cleaned up. - */ - - do - { - mb(); - m = I2O_POST_READ32(c); - } - while(m==0xFFFFFFFF); - msg = (u32 *)(c->mem_offset + m); - - /* - * Put together a scsi execscb message - */ - - len = SCpnt->request_bufflen; - direction = 0x00000000; // SGL IN (osm<--iop) - - /* - * The scsi layer should be handling this stuff - */ - - scsidir = 0x00000000; // DATA NO XFER - if(len) - { - if(is_dir_out(SCpnt)) - { - direction=0x04000000; // SGL OUT (osm-->iop) - scsidir =0x80000000; // DATA OUT (iop-->dev) - } - else - { - scsidir =0x40000000; // DATA IN (iop<--dev) - } - } - - __raw_writel(I2O_CMD_SCSI_EXEC<<24|HOST_TID<<12|tid, &msg[1]); - __raw_writel(scsi_context, &msg[2]); /* So the I2O layer passes to us */ - /* Sorry 64bit folks. FIXME */ - __raw_writel((u32)SCpnt, &msg[3]); /* We want the SCSI control block back */ - - /* LSI_920_PCI_QUIRK - * - * Intermittant observations of msg frame word data corruption - * observed on msg[4] after: - * WRITE, READ-MODIFY-WRITE - * operations. 19990606 -sralston - * - * (Hence we build this word via tag. Its good practice anyway - * we don't want fetches over PCI needlessly) - */ - - tag=0; - - /* - * Attach tags to the devices - */ - if(SCpnt->device->tagged_supported) - { - /* - * Some drives are too stupid to handle fairness issues - * with tagged queueing. We throw in the odd ordered - * tag to stop them starving themselves. - */ - if((jiffies - hostdata->tagclock[SCpnt->target][SCpnt->lun]) > (5*HZ)) - { - tag=0x01800000; /* ORDERED! */ - hostdata->tagclock[SCpnt->target][SCpnt->lun]=jiffies; - } - else - { - /* Hmmm... I always see value of 0 here, - * of which {HEAD_OF, ORDERED, SIMPLE} are NOT! -sralston - */ - if(SCpnt->tag == HEAD_OF_QUEUE_TAG) - tag=0x01000000; - else if(SCpnt->tag == ORDERED_QUEUE_TAG) - tag=0x01800000; - } - } - - /* Direction, disconnect ok, tag, CDBLen */ - __raw_writel(scsidir|0x20000000|SCpnt->cmd_len|tag, &msg[4]); - - mptr=msg+5; - - /* - * Write SCSI command into the message - always 16 byte block - */ - - memcpy_toio(mptr, SCpnt->cmnd, 16); - mptr+=4; - lenptr=mptr++; /* Remember me - fill in when we know */ - - reqlen = 12; // SINGLE SGE - - /* - * Now fill in the SGList and command - * - * FIXME: we need to set the sglist limits according to the - * message size of the I2O controller. We might only have room - * for 6 or so worst case - */ - - if(SCpnt->use_sg) - { - struct scatterlist *sg = (struct scatterlist *)SCpnt->request_buffer; - int chain = 0; - - len = 0; - - if((sg_max_frags > 11) && (SCpnt->use_sg > 11)) - { - chain = 1; - /* - * Need to chain! - */ - __raw_writel(direction|0xB0000000|(SCpnt->use_sg*2*4), mptr++); - __raw_writel(virt_to_bus(sg_chain_pool + sg_chain_tag), mptr); - mptr = (u32*)(sg_chain_pool + sg_chain_tag); - if (SCpnt->use_sg > max_sg_len) - { - max_sg_len = SCpnt->use_sg; - printk("i2o_scsi: Chain SG! SCpnt=%p, SG_FragCnt=%d, SG_idx=%d\n", - SCpnt, SCpnt->use_sg, sg_chain_tag); - } - if ( ++sg_chain_tag == SG_MAX_BUFS ) - sg_chain_tag = 0; - for(i = 0 ; i < SCpnt->use_sg; i++) - { - *mptr++=direction|0x10000000|sg->length; - len+=sg->length; - *mptr++=virt_to_bus(sg->address); - sg++; - } - mptr[-2]=direction|0xD0000000|(sg-1)->length; - } - else - { - for(i = 0 ; i < SCpnt->use_sg; i++) - { - __raw_writel(direction|0x10000000|sg->length, mptr++); - len+=sg->length; - __raw_writel(virt_to_bus(sg->address), mptr++); - sg++; - } - - /* Make this an end of list. Again evade the 920 bug and - unwanted PCI read traffic */ - - __raw_writel(direction|0xD0000000|(sg-1)->length, &mptr[-2]); - } - - if(!chain) - reqlen = mptr - msg; - - __raw_writel(len, lenptr); - - if(len != SCpnt->underflow) - printk("Cmd len %08X Cmd underflow %08X\n", - len, SCpnt->underflow); - } - else - { - dprintk(("non sg for %p, %d\n", SCpnt->request_buffer, - SCpnt->request_bufflen)); - __raw_writel(len = SCpnt->request_bufflen, lenptr); - if(len == 0) - { - reqlen = 9; - } - else - { - __raw_writel(0xD0000000|direction|SCpnt->request_bufflen, mptr++); - __raw_writel(virt_to_bus(SCpnt->request_buffer), mptr++); - } - } - - /* - * Stick the headers on - */ - - __raw_writel(reqlen<<16 | SGL_OFFSET_10, msg); - - /* Queue the message */ - i2o_post_message(c,m); - - atomic_inc(&queue_depth); - - if(atomic_read(&queue_depth)> max_qd) - { - max_qd=atomic_read(&queue_depth); - printk("Queue depth now %d.\n", max_qd); - } - - mb(); - dprintk(("Issued %ld\n", current_command->serial_number)); - - return 0; -} - -static void internal_done(Scsi_Cmnd * SCpnt) -{ - SCpnt->SCp.Status++; -} - -int i2o_scsi_command(Scsi_Cmnd * SCpnt) -{ - i2o_scsi_queuecommand(SCpnt, internal_done); - SCpnt->SCp.Status = 0; - while (!SCpnt->SCp.Status) - barrier(); - return SCpnt->result; -} - -int i2o_scsi_abort(Scsi_Cmnd * SCpnt) -{ - struct i2o_controller *c; - struct Scsi_Host *host; - struct i2o_scsi_host *hostdata; - u32 *msg; - u32 m; - int tid; - - printk("i2o_scsi: Aborting command block.\n"); - - host = SCpnt->host; - hostdata = (struct i2o_scsi_host *)host->hostdata; - tid = hostdata->task[SCpnt->target][SCpnt->lun]; - if(tid==-1) - { - printk(KERN_ERR "impossible command to abort.\n"); - return SCSI_ABORT_NOT_RUNNING; - } - c = hostdata->controller; - - /* - * Obtain an I2O message. Right now we _have_ to obtain one - * until the scsi layer stuff is cleaned up. - */ - - do - { - mb(); - m = I2O_POST_READ32(c); - } - while(m==0xFFFFFFFF); - msg = bus_to_virt(c->mem_offset + m); - - __raw_writel(FIVE_WORD_MSG_SIZE, &msg[0]); - __raw_writel(I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|tid, &msg[1]); - __raw_writel(scsi_context, &msg[2]); - __raw_writel(0, &msg[3]); /* Not needed for an abort */ - __raw_writel((u32)SCpnt, &msg[4]); - wmb(); - i2o_post_message(c,m); - wmb(); - return SCSI_ABORT_PENDING; -} - -int i2o_scsi_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) -{ - int tid; - struct i2o_controller *c; - struct Scsi_Host *host; - struct i2o_scsi_host *hostdata; - u32 m; - u32 *msg; - - /* - * Find the TID for the bus - */ - - printk("i2o_scsi: Attempting to reset the bus.\n"); - - host = SCpnt->host; - hostdata = (struct i2o_scsi_host *)host->hostdata; - tid = hostdata->bus_task; - c = hostdata->controller; - - /* - * Now send a SCSI reset request. Any remaining commands - * will be aborted by the IOP. We need to catch the reply - * possibly ? - */ - - m = I2O_POST_READ32(c); - - /* - * No free messages, try again next time - no big deal - */ - - if(m == 0xFFFFFFFF) - return SCSI_RESET_PUNT; - - msg = bus_to_virt(c->mem_offset + m); - __raw_writel(FOUR_WORD_MSG_SIZE|SGL_OFFSET_0, &msg[0]); - __raw_writel(I2O_CMD_SCSI_BUSRESET<<24|HOST_TID<<12|tid, &msg[1]); - __raw_writel(scsi_context|0x80000000, &msg[2]); - /* We use the top bit to split controller and unit transactions */ - /* Now store unit,tid so we can tie the completion back to a specific device */ - __raw_writel(c->unit << 16 | tid, &msg[3]); - i2o_post_message(c,m); - return SCSI_RESET_PENDING; -} - -/* - * This is anyones guess quite frankly. - */ - -int i2o_scsi_bios_param(Disk * disk, kdev_t dev, int *ip) -{ - int size; - - size = disk->capacity; - ip[0] = 64; /* heads */ - ip[1] = 32; /* sectors */ - if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */ - ip[0] = 255; /* heads */ - ip[1] = 63; /* sectors */ - ip[2] = size / (255 * 63); /* cylinders */ - } - return 0; -} - -MODULE_AUTHOR("Red Hat Software"); - -static Scsi_Host_Template driver_template = I2OSCSI; - -#include "../scsi/scsi_module.c" diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/i2o/i2o_scsi.h linux.ac/drivers/i2o/i2o_scsi.h --- linux.vanilla/drivers/i2o/i2o_scsi.h Mon Dec 11 21:20:04 2000 +++ linux.ac/drivers/i2o/i2o_scsi.h Thu Jan 1 01:00:00 1970 @@ -1,47 +0,0 @@ -#ifndef _I2O_SCSI_H -#define _I2O_SCSI_H - -#if !defined(LINUX_VERSION_CODE) -#include -#endif - -#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) - -#include -#include - -#define I2O_SCSI_ID 15 -#define I2O_SCSI_CAN_QUEUE 4 -#define I2O_SCSI_CMD_PER_LUN 6 - -extern int i2o_scsi_detect(Scsi_Host_Template *); -extern const char *i2o_scsi_info(struct Scsi_Host *); -extern int i2o_scsi_command(Scsi_Cmnd *); -extern int i2o_scsi_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -extern int i2o_scsi_abort(Scsi_Cmnd *); -extern int i2o_scsi_reset(Scsi_Cmnd *, unsigned int); -extern int i2o_scsi_bios_param(Disk *, kdev_t, int *); -extern void i2o_scsi_setup(char *str, int *ints); -extern int i2o_scsi_release(struct Scsi_Host *host); - -#define I2OSCSI { \ - next: NULL, \ - proc_name: "i2o_scsi", \ - name: "I2O SCSI Layer", \ - detect: i2o_scsi_detect, \ - release: i2o_scsi_release, \ - info: i2o_scsi_info, \ - command: i2o_scsi_command, \ - queuecommand: i2o_scsi_queuecommand, \ - abort: i2o_scsi_abort, \ - reset: i2o_scsi_reset, \ - bios_param: i2o_scsi_bios_param, \ - can_queue: I2O_SCSI_CAN_QUEUE, \ - this_id: I2O_SCSI_ID, \ - sg_tablesize: 8, \ - cmd_per_lun: I2O_SCSI_CMD_PER_LUN, \ - unchecked_isa_dma: 0, \ - use_clustering: ENABLE_CLUSTERING \ - } - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/Config.in linux.ac/drivers/ide/Config.in --- linux.vanilla/drivers/ide/Config.in Fri Feb 16 01:22:08 2001 +++ linux.ac/drivers/ide/Config.in Tue Apr 3 17:54:41 2001 @@ -64,6 +64,10 @@ dep_mbool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX $CONFIG_BLK_DEV_IDEDMA_PCI dep_mbool ' PIIXn Tuning support' CONFIG_PIIX_TUNING $CONFIG_BLK_DEV_PIIX $CONFIG_IDEDMA_PCI_AUTO fi + if [ "$CONFIG_MIPS_ITE8172" = "y" -o "$CONFIG_MIPS_IVR" = "y" ]; then + dep_mbool ' IT8172 IDE support' CONFIG_BLK_DEV_IT8172 $CONFIG_BLK_DEV_IDEDMA_PCI + dep_mbool ' IT8172 IDE Tuning support' CONFIG_IT8172_TUNING $CONFIG_BLK_DEV_IT8172 $CONFIG_IDEDMA_PCI_AUTO + fi dep_bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 $CONFIG_BLK_DEV_IDEDMA_PCI dep_bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_EXPERIMENTAL dep_bool ' PROMISE PDC20246/PDC20262/PDC20267 support' CONFIG_BLK_DEV_PDC202XX $CONFIG_BLK_DEV_IDEDMA_PCI @@ -97,17 +101,20 @@ dep_bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_ARCH_ACORN fi if [ "$CONFIG_AMIGA" = "y" ]; then - dep_bool ' Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE $CONFIG_AMIGA - dep_mbool ' Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE $CONFIG_EXPERIMENTAL + dep_bool ' Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE $CONFIG_AMIGA + dep_mbool ' Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE $CONFIG_EXPERIMENTAL fi if [ "$CONFIG_ZORRO" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then - dep_mbool ' Buddha/Catweasel IDE interface support (EXPERIMENTAL)' CONFIG_BLK_DEV_BUDDHA $CONFIG_ZORRO $CONFIG_EXPERIMENTAL + dep_mbool ' Buddha/Catweasel IDE interface support (EXPERIMENTAL)' CONFIG_BLK_DEV_BUDDHA $CONFIG_ZORRO $CONFIG_EXPERIMENTAL fi if [ "$CONFIG_ATARI" = "y" ]; then - dep_bool ' Falcon IDE interface support' CONFIG_BLK_DEV_FALCON_IDE $CONFIG_ATARI + dep_bool ' Falcon IDE interface support' CONFIG_BLK_DEV_FALCON_IDE $CONFIG_ATARI fi if [ "$CONFIG_MAC" = "y" ]; then - dep_bool ' Macintosh Quadra/Powerbook IDE interface support' CONFIG_BLK_DEV_MAC_IDE $CONFIG_MAC + dep_bool ' Macintosh Quadra/Powerbook IDE interface support' CONFIG_BLK_DEV_MAC_IDE $CONFIG_MAC + fi + if [ "$CONFIG_Q40" = "y" ]; then + dep_bool ' Q40/Q60 IDE interface support' CONFIG_BLK_DEV_Q40IDE $CONFIG_Q40 fi bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS @@ -163,6 +170,7 @@ "$CONFIG_BLK_DEV_OSB4" = "y" -o \ "$CONFIG_BLK_DEV_PDC202XX" = "y" -o \ "$CONFIG_BLK_DEV_PIIX" = "y" -o \ + "$CONFIG_BLK_DEV_IT8172" = "y" -o \ "$CONFIG_BLK_DEV_SIS5513" = "y" -o \ "$CONFIG_BLK_DEV_SLC90E66" = "y" -o \ "$CONFIG_BLK_DEV_SL82C105" = "y" -o \ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/Makefile linux.ac/drivers/ide/Makefile --- linux.vanilla/drivers/ide/Makefile Fri Dec 29 22:07:22 2000 +++ linux.ac/drivers/ide/Makefile Tue May 8 18:25:00 2001 @@ -24,6 +24,7 @@ obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd.o obj-$(CONFIG_BLK_DEV_IDETAPE) += ide-tape.o obj-$(CONFIG_BLK_DEV_IDEFLOPPY) += ide-floppy.o +obj-$(CONFIG_BLK_DEV_IT8172) += it8172.o ide-obj-$(CONFIG_BLK_DEV_AEC62XX) += aec62xx.o ide-obj-$(CONFIG_BLK_DEV_ALI14XX) += ali14xx.o @@ -42,7 +43,7 @@ ide-obj-$(CONFIG_BLK_DEV_HPT366) += hpt366.o ide-obj-$(CONFIG_BLK_DEV_HT6560B) += ht6560b.o ide-obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o -ide-obj-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o +ide-obj-$(CONFIG_BLK_DEV_IDEDMA_PCI) += ide-dma.o ide-obj-$(CONFIG_BLK_DEV_IDEPCI) += ide-pci.o ide-obj-$(CONFIG_BLK_DEV_ISAPNP) += ide-pnp.o ide-obj-$(CONFIG_BLK_DEV_IDE_PMAC) += ide-pmac.o diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ali14xx.c linux.ac/drivers/ide/ali14xx.c --- linux.vanilla/drivers/ide/ali14xx.c Fri Apr 14 06:54:26 2000 +++ linux.ac/drivers/ide/ali14xx.c Tue Apr 3 17:54:41 2001 @@ -81,9 +81,9 @@ {0x2d, 0x32, 0x2e, 0x33}, /* drive 3 */ }; -static int basePort = 0; /* base port address */ -static int regPort = 0; /* port for register number */ -static int dataPort = 0; /* port for register data */ +static int basePort; /* base port address */ +static int regPort; /* port for register number */ +static int dataPort; /* port for register data */ static byte regOn; /* output to base port to access registers */ static byte regOff; /* output to base port to close registers */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/alim15x3.c linux.ac/drivers/ide/alim15x3.c --- linux.vanilla/drivers/ide/alim15x3.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/ide/alim15x3.c Tue May 22 18:43:18 2001 @@ -679,19 +679,21 @@ hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; hwif->speedproc = &ali15x3_tune_chipset; -#ifndef CONFIG_BLK_DEV_IDEDMA - hwif->autodma = 0; - return; -#endif /* CONFIG_BLK_DEV_IDEDMA */ +#ifdef CONFIG_BLK_DEV_IDEDMA if ((hwif->dma_base) && (m5229_revision >= 0x20)) { /* * M1543C or newer for DMAing */ hwif->dmaproc = &ali15x3_dmaproc; - if (!noautodma) - hwif->autodma = 1; + hwif->autodma = 1; } + + if (noautodma) + hwif->autodma = 0; +#else + hwif->autodma = 0; +#endif /* CONFIG_BLK_DEV_IDEDMA */ } void __init ide_dmacapable_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/buddha.c linux.ac/drivers/ide/buddha.c --- linux.vanilla/drivers/ide/buddha.c Tue Nov 28 01:57:34 2000 +++ linux.ac/drivers/ide/buddha.c Tue Apr 3 17:54:41 2001 @@ -87,7 +87,7 @@ * Board information */ -static u_long buddha_board = 0; +static u_long buddha_board; static int buddha_num_hwifs = -1; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/hpt366.c linux.ac/drivers/ide/hpt366.c --- linux.vanilla/drivers/ide/hpt366.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/ide/hpt366.c Mon May 21 00:12:36 2001 @@ -51,6 +51,16 @@ }; const char *bad_ata100_5[] = { + "IBM-DTLA-307075", + "IBM-DTLA-307060", + "IBM-DTLA-307045", + "IBM-DTLA-307030", + "IBM-DTLA-307020", + "IBM-DTLA-307015", + "IBM-DTLA-305040", + "IBM-DTLA-305030", + "IBM-DTLA-305020", + "WDC AC310200R", NULL }; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-cd.c linux.ac/drivers/ide/ide-cd.c --- linux.vanilla/drivers/ide/ide-cd.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/ide/ide-cd.c Sat May 26 17:03:46 2001 @@ -254,7 +254,7 @@ * They will disappear later when I get the time to * do it cleanly. * - Minimize the TOC reading - only do it when we - * know a media change has occured. + * know a media change has occurred. * - Moved all the CDROMREADx ioctls to the Uniform layer. * - Heiko Eissfeldt supplied * some fixes for CDI. @@ -977,8 +977,7 @@ /* If we've filled the present buffer but there's another chained buffer after it, move on. */ - if (rq->current_nr_sectors == 0 && - rq->nr_sectors > 0) + if (rq->current_nr_sectors == 0 && rq->nr_sectors) cdrom_end_request (1, drive); /* If the buffers are full, cache the rest of the data in our @@ -1192,6 +1191,55 @@ return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation); } +static inline int cdrom_merge_requests(struct request *rq, struct request *nxt) +{ + int ret = 1; + + /* + * partitions not really working, but better check anyway... + */ + if (rq->cmd == nxt->cmd && rq->rq_dev == nxt->rq_dev) { + rq->nr_sectors += nxt->nr_sectors; + rq->hard_nr_sectors += nxt->nr_sectors; + rq->bhtail->b_reqnext = nxt->bh; + rq->bhtail = nxt->bhtail; + list_del(&nxt->queue); + blkdev_release_request(nxt); + ret = 0; + } + + return ret; +} + +/* + * the current request will always be the first one on the list + */ +static void cdrom_attempt_remerge(ide_drive_t *drive, struct request *rq) +{ + struct list_head *entry; + struct request *nxt; + unsigned long flags; + + spin_lock_irqsave(&io_request_lock, flags); + + while (1) { + entry = rq->queue.next; + if (entry == &drive->queue.queue_head) + break; + + nxt = blkdev_entry_to_request(entry); + if (rq->sector + rq->nr_sectors != nxt->sector) + break; + else if (rq->nr_sectors + nxt->nr_sectors > SECTORS_MAX) + break; + + if (cdrom_merge_requests(rq, nxt)) + break; + } + + spin_unlock_irqrestore(&io_request_lock, flags); +} + /* Fix up a possibly partially-processed request so that we can start it over entirely, or even put it back on the request queue. */ static void restore_request (struct request *rq) @@ -1203,6 +1251,8 @@ rq->sector -= n; } rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS; + rq->hard_nr_sectors = rq->nr_sectors; + rq->hard_sector = rq->sector; } /* @@ -1216,20 +1266,22 @@ /* If the request is relative to a partition, fix it up to refer to the absolute address. */ - if ((minor & PARTN_MASK) != 0) { + if (minor & PARTN_MASK) { rq->sector = block; minor &= ~PARTN_MASK; - rq->rq_dev = MKDEV (MAJOR(rq->rq_dev), minor); + rq->rq_dev = MKDEV(MAJOR(rq->rq_dev), minor); } /* We may be retrying this request after an error. Fix up any weirdness which might be present in the request packet. */ - restore_request (rq); + restore_request(rq); /* Satisfy whatever we can of this request from our cached sector. */ if (cdrom_read_from_buffer(drive)) return ide_stopped; + cdrom_attempt_remerge(drive, rq); + /* Clear the local sector buffer. */ info->nsectors_buffered = 0; @@ -1477,7 +1529,7 @@ static ide_startstop_t cdrom_write_intr(ide_drive_t *drive) { - int stat, ireason, len, sectors_to_transfer; + int stat, ireason, len, sectors_to_transfer, uptodate; struct cdrom_info *info = drive->driver_data; int i, dma_error = 0, dma = info->dma; ide_startstop_t startstop; @@ -1498,6 +1550,9 @@ return startstop; } + /* + * using dma, transfer is complete now + */ if (dma) { if (dma_error) return ide_error(drive, "dma error", stat); @@ -1519,12 +1574,13 @@ /* If we're not done writing, complain. * Otherwise, complete the command normally. */ + uptodate = 1; if (rq->current_nr_sectors > 0) { printk("%s: write_intr: data underrun (%ld blocks)\n", - drive->name, rq->current_nr_sectors); - cdrom_end_request(0, drive); - } else - cdrom_end_request(1, drive); + drive->name, rq->current_nr_sectors); + uptodate = 0; + } + cdrom_end_request(uptodate, drive); return ide_stopped; } @@ -1533,26 +1589,42 @@ if (cdrom_write_check_ireason(drive, len, ireason)) return ide_stopped; - /* The number of sectors we need to read from the drive. */ sectors_to_transfer = len / SECTOR_SIZE; - /* Now loop while we still have data to read from the drive. DMA - * transfers will already have been complete + /* + * now loop and write out the data */ while (sectors_to_transfer > 0) { - /* If we've filled the present buffer but there's another - chained buffer after it, move on. */ - if (rq->current_nr_sectors == 0 && rq->nr_sectors > 0) - cdrom_end_request(1, drive); + int this_transfer; + + if (!rq->current_nr_sectors) { + printk("ide-cd: write_intr: oops\n"); + break; + } + + /* + * Figure out how many sectors we can transfer + */ + this_transfer = MIN(sectors_to_transfer,rq->current_nr_sectors); + + while (this_transfer > 0) { + atapi_output_bytes(drive, rq->buffer, SECTOR_SIZE); + rq->buffer += SECTOR_SIZE; + --rq->nr_sectors; + --rq->current_nr_sectors; + ++rq->sector; + --this_transfer; + --sectors_to_transfer; + } - atapi_output_bytes(drive, rq->buffer, rq->current_nr_sectors); - rq->nr_sectors -= rq->current_nr_sectors; - rq->current_nr_sectors = 0; - rq->sector += rq->current_nr_sectors; - sectors_to_transfer -= rq->current_nr_sectors; + /* + * current buffer complete, move on + */ + if (rq->current_nr_sectors == 0 && rq->nr_sectors) + cdrom_end_request (1, drive); } - /* arm handler */ + /* re-arm handler */ ide_set_handler(drive, &cdrom_write_intr, 5 * WAIT_CMD, NULL); return ide_started; } @@ -1583,10 +1655,26 @@ return cdrom_transfer_packet_command(drive, &pc, cdrom_write_intr); } -static ide_startstop_t cdrom_start_write(ide_drive_t *drive) +static ide_startstop_t cdrom_start_write(ide_drive_t *drive, struct request *rq) { struct cdrom_info *info = drive->driver_data; + /* + * writes *must* be 2kB frame aligned + */ + if ((rq->nr_sectors & 3) || (rq->sector & 3)) { + cdrom_end_request(0, drive); + return ide_stopped; + } + + /* + * for dvd-ram and such media, it's a really big deal to get + * big writes all the time. so scour the queue and attempt to + * remerge requests, often the plugging will not have had time + * to do this properly + */ + cdrom_attempt_remerge(drive, rq); + info->nsectors_buffered = 0; /* use dma, if possible. we don't need to check more, since we @@ -1629,7 +1717,7 @@ if (rq->cmd == READ) action = cdrom_start_read(drive, block); else - action = cdrom_start_write(drive); + action = cdrom_start_write(drive, rq); } info->last_block = block; return action; @@ -1832,6 +1920,7 @@ pc.buffer = buf; pc.buflen = buflen; + pc.quiet = 1; pc.c[0] = GPCMD_READ_TOC_PMA_ATIP; pc.c[6] = trackno; pc.c[7] = (buflen >> 8); @@ -1984,7 +2073,7 @@ /* Now try to get the total cdrom capacity. */ minor = (drive->select.b.unit) << PARTN_BITS; dev = MKDEV(HWIF(drive)->major, minor); - stat = cdrom_get_last_written(dev, (long *)&toc->capacity); + stat = cdrom_get_last_written(dev, &toc->capacity); if (stat) stat = cdrom_read_capacity(drive, &toc->capacity, sense); if (stat) @@ -2785,7 +2874,7 @@ MOD_INC_USE_COUNT; if (info->buffer == NULL) info->buffer = (char *) kmalloc(SECTOR_BUFFER_SIZE, GFP_KERNEL); - if ((info->buffer == NULL) || (rc = cdrom_fops.open(ip, fp))) { + if ((info->buffer == NULL) || (rc = cdrom_fops.open(ip, fp))) { drive->usage--; MOD_DEC_USE_COUNT; } @@ -2826,7 +2915,12 @@ drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME; HWIF(drive)->gd->sizes[minor] = toc->capacity * BLOCKS_PER_FRAME; + /* + * reset block size, ide_revalidate_disk incorrectly sets it to + * 1024 even for CDROM's + */ blk_size[HWIF(drive)->major] = HWIF(drive)->gd->sizes; + set_blocksize(MKDEV(HWIF(drive)->major, minor), CD_FRAMESIZE); } static diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-cd.h linux.ac/drivers/ide/ide-cd.h --- linux.vanilla/drivers/ide/ide-cd.h Thu Feb 22 00:09:59 2001 +++ linux.ac/drivers/ide/ide-cd.h Sat May 26 17:51:04 2001 @@ -37,11 +37,12 @@ /************************************************************************/ -#define SECTOR_SIZE 512 #define SECTOR_BITS 9 -#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE) +#define SECTOR_SIZE (1 << SECTOR_BITS) +#define SECTORS_PER_FRAME (CD_FRAMESIZE >> SECTOR_BITS) #define SECTOR_BUFFER_SIZE (CD_FRAMESIZE * 32) -#define SECTORS_BUFFER (SECTOR_BUFFER_SIZE / SECTOR_SIZE) +#define SECTORS_BUFFER (SECTOR_BUFFER_SIZE >> SECTOR_BITS) +#define SECTORS_MAX (131072 >> SECTOR_BITS) #define BLOCKS_PER_FRAME (CD_FRAMESIZE / BLOCK_SIZE) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-dma.c linux.ac/drivers/ide/ide-dma.c --- linux.vanilla/drivers/ide/ide-dma.c Mon Jan 15 21:08:15 2001 +++ linux.ac/drivers/ide/ide-dma.c Fri May 11 18:17:51 2001 @@ -119,6 +119,12 @@ { "WDC AC31600H" , "ALL" }, { "WDC AC32100H" , "24.09P07" }, { "WDC AC23200L" , "21.10N21" }, + { "Compaq CRD-8241B" , "ALL" }, + { "CRD-8400B" , "ALL" }, + { "SanDisk SDP3B-64" , "ALL" }, + { "SAMSUNG CD-ROM SN-124", "ALL" }, + { "PLEXTOR CD-R PX-W8432T", "ALL" }, + { "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" }, { 0 , 0 } }; @@ -217,6 +223,9 @@ struct scatterlist *sg = hwif->sg_table; int nents = 0; + if (hwif->sg_dma_active) + BUG(); + if (rq->cmd == READ) hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; else @@ -286,6 +295,7 @@ HWIF(drive)->sg_table, HWIF(drive)->sg_nents, HWIF(drive)->sg_dma_direction); + HWIF(drive)->sg_dma_active = 0; return 0; /* revert to PIO for this request */ } else { u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff); @@ -322,6 +332,7 @@ int nents = HWIF(drive)->sg_nents; pci_unmap_sg(dev, sg, nents, HWIF(drive)->sg_dma_direction); + HWIF(drive)->sg_dma_active = 0; } /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-floppy.c linux.ac/drivers/ide/ide-floppy.c --- linux.vanilla/drivers/ide/ide-floppy.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/ide/ide-floppy.c Tue Apr 3 17:54:41 2001 @@ -1,5 +1,5 @@ /* - * linux/drivers/ide/ide-floppy.c Version 0.9 Jul 4, 1999 + * linux/drivers/ide/ide-floppy.c Version 0.9.sv Jan 6 2001 * * Copyright (C) 1996 - 1999 Gadi Oxman */ @@ -29,9 +29,18 @@ * Ver 0.9 Jul 4 99 Fix a bug which might have caused the number of * bytes requested on each interrupt to be zero. * Thanks to for pointing this out. + * Ver 0.9.sv Jan 6 01 Sam Varshavchik + * Implement low level formatting. Reimplemented + * IDEFLOPPY_CAPABILITIES_PAGE, since we need the srfp + * bit. My LS-120 drive barfs on + * IDEFLOPPY_CAPABILITIES_PAGE, but maybe it's just me. + * Compromise by not reporting a failure to get this + * mode page. Implemented four IOCTLs in order to + * implement formatting. IOCTls begin with 0x4600, + * 0x46 is 'F' as in Format. */ -#define IDEFLOPPY_VERSION "0.9" +#define IDEFLOPPY_VERSION "0.9.sv" #include #include @@ -228,6 +237,7 @@ * Last error information */ byte sense_key, asc, ascq; + int progress_indication; /* * Device information @@ -236,7 +246,7 @@ idefloppy_capacity_descriptor_t capacity; /* Last format capacity */ idefloppy_flexible_disk_page_t flexible_disk_page; /* Copy of the flexible disk page */ int wp; /* Write protect */ - + int srfp; /* Supports format progress report */ unsigned int flags; /* Status/Action flags */ } idefloppy_floppy_t; @@ -246,6 +256,7 @@ #define IDEFLOPPY_DRQ_INTERRUPT 0 /* DRQ interrupt device */ #define IDEFLOPPY_MEDIA_CHANGED 1 /* Media may have changed */ #define IDEFLOPPY_USE_READ12 2 /* Use READ12/WRITE12 or READ10/WRITE10 */ +#define IDEFLOPPY_FORMAT_IN_PROGRESS 3 /* Format in progress */ /* * ATAPI floppy drive packet commands @@ -276,6 +287,15 @@ #define MODE_SENSE_SAVED 0x03 /* + * IOCTLs used in low-level formatting. + */ + +#define IDEFLOPPY_IOCTL_FORMAT_SUPPORTED 0x4600 +#define IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY 0x4601 +#define IDEFLOPPY_IOCTL_FORMAT_START 0x4602 +#define IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS 0x4603 + +/* * Special requests for our block device strategy routine. */ #define IDEFLOPPY_FIRST_RQ 90 @@ -559,7 +579,7 @@ u8 asc; /* Additional Sense Code */ u8 ascq; /* Additional Sense Code Qualifier */ u8 replaceable_unit_code; /* Field Replaceable Unit Code */ - u8 reserved[3]; + u8 sksv[3]; u8 pad[2]; /* Padding to 20 bytes */ } idefloppy_request_sense_result_t; @@ -746,6 +766,8 @@ idefloppy_floppy_t *floppy = drive->driver_data; floppy->sense_key = result->sense_key; floppy->asc = result->asc; floppy->ascq = result->ascq; + floppy->progress_indication= result->sksv[0] & 0x80 ? + (unsigned short)get_unaligned((u16 *)(result->sksv+1)):0x10000; #if IDEFLOPPY_DEBUG_LOG if (floppy->failed_pc) printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq); @@ -954,7 +976,7 @@ return ide_do_reset (drive); } ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL); /* Set the interrupt routine */ - atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */ + atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */ return ide_started; } @@ -1062,6 +1084,22 @@ pc->request_transfer = 255; } +static void idefloppy_create_format_unit_cmd (idefloppy_pc_t *pc, int b, int l) +{ + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_FORMAT_UNIT_CMD; + pc->c[1] = 0x17; + + memset(pc->buffer, 0, 12); + pc->buffer[1] = 0xA2; /* Format list header, byte 1: FOV/DCRT/IMM */ + pc->buffer[3] = 8; + + put_unaligned(htonl(b), (unsigned int *)(&pc->buffer[4])); + put_unaligned(htonl(l), (unsigned int *)(&pc->buffer[8])); + pc->buffer_size=12; + set_bit(PC_WRITING, &pc->flags); +} + /* * A mode sense command is used to "sense" floppy parameters. */ @@ -1235,6 +1273,25 @@ return 0; } +static int idefloppy_get_capability_page(ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_pc_t pc; + idefloppy_mode_parameter_header_t *header; + idefloppy_capabilities_page_t *page; + + floppy->srfp=0; + idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_CAPABILITIES_PAGE, + MODE_SENSE_CURRENT); + if (idefloppy_queue_pc_tail (drive,&pc)) { + return 1; + } + header = (idefloppy_mode_parameter_header_t *) pc.buffer; + page= (idefloppy_capabilities_page_t *)(header+1); + floppy->srfp=page->srfp; + return (0); +} + /* * Determine if a media is present in the floppy drive, and if so, * its LBA capacity. @@ -1286,11 +1343,172 @@ #endif /* IDEFLOPPY_DEBUG_INFO */ } (void) idefloppy_get_flexible_disk_page (drive); + (void) idefloppy_get_capability_page (drive); + drive->part[0].nr_sects = floppy->blocks * floppy->bs_factor; return rc; } /* +** Obtain the list of formattable capacities. +** Very similar to idefloppy_get_capacity, except that we push the capacity +** descriptors to userland, instead of our own structures. +** +** Userland gives us the following structure: +** +** struct idefloppy_format_capacities { +** int nformats; +** struct { +** int nblocks; +** int blocksize; +** } formats[]; +** } ; +** +** userland initializes nformats to the number of allocated formats[] +** records. On exit we set nformats to the number of records we've +** actually initialized. +** +*/ + +static int idefloppy_get_format_capacities (ide_drive_t *drive, + struct inode *inode, + struct file *file, + int *arg) /* Cheater */ +{ + idefloppy_pc_t pc; + idefloppy_capacity_header_t *header; + idefloppy_capacity_descriptor_t *descriptor; + int i, descriptors, blocks, length; + int u_array_size; + int u_index; + int *argp; + + if (get_user(u_array_size, arg)) + return (-EFAULT); + + if (u_array_size <= 0) + return (-EINVAL); + + idefloppy_create_read_capacity_cmd (&pc); + if (idefloppy_queue_pc_tail (drive, &pc)) { + printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n"); + return (-EIO); + } + header = (idefloppy_capacity_header_t *) pc.buffer; + descriptors = header->length / + sizeof (idefloppy_capacity_descriptor_t); + descriptor = (idefloppy_capacity_descriptor_t *) (header + 1); + + u_index=0; + argp=arg+1; + + /* + ** We always skip the first capacity descriptor. That's the + ** current capacity. We are interested in the remaining descriptors, + ** the formattable capacities. + */ + + for (i=0; i= u_array_size) + break; /* User-supplied buffer too small */ + if (i == 0) + continue; /* Skip the first descriptor */ + + blocks = ntohl (descriptor->blocks); + length = ntohs (descriptor->length); + + if (put_user(blocks, argp)) + return (-EFAULT); + ++argp; + + if (put_user(length, argp)) + return (-EFAULT); + ++argp; + + ++u_index; + } + + if (put_user(u_index, arg)) + return (-EFAULT); + return (0); +} + +/* +** Send ATAPI_FORMAT_UNIT to the drive. +** +** Userland gives us the following structure: +** +** struct idefloppy_format_command { +** int nblocks; +** int blocksize; +** } ; +*/ + +static int idefloppy_begin_format(ide_drive_t *drive, + struct inode *inode, + struct file *file, + int *arg) +{ + int blocks; + int length; + idefloppy_pc_t pc; + + if (get_user(blocks, arg) + || get_user(length, arg+1)) + { + return (-EFAULT); + } + + idefloppy_create_format_unit_cmd(&pc, blocks, length); + if (idefloppy_queue_pc_tail (drive, &pc)) + { + return (-EIO); + } + return (0); +} + +/* +** Get ATAPI_FORMAT_UNIT progress indication. +** +** Userland gives a pointer to an int. The int is set to a progresss +** indicator 0-65536, with 65536=100%. +** +** If the drive does not support format progress indication, we just return +** a 65536, screw it. +*/ + +static int idefloppy_get_format_progress(ide_drive_t *drive, + struct inode *inode, + struct file *file, + int *arg) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_pc_t pc; + int progress_indication=0x10000; + + if (floppy->srfp) + { + idefloppy_create_request_sense_cmd(&pc); + if (idefloppy_queue_pc_tail (drive, &pc)) + { + return (-EIO); + } + + if (floppy->sense_key == 2 && floppy->asc == 4 && + floppy->ascq == 4) + { + progress_indication=floppy->progress_indication; + } + } + + if (put_user(progress_indication, arg)) + return (-EFAULT); + + return (0); +} + +/* * Our special ide-floppy ioctl's. * * Currently there aren't any ioctl's. @@ -1299,15 +1517,67 @@ unsigned int cmd, unsigned long arg) { idefloppy_pc_t pc; + int prevent = (arg) ? 1 : 0; - if (cmd == CDROMEJECT) { + switch (cmd) { + case CDROMEJECT: + prevent = 0; + /* fall through */ + case CDROM_LOCKDOOR: if (drive->usage > 1) return -EBUSY; - idefloppy_create_prevent_cmd (&pc, 0); - (void) idefloppy_queue_pc_tail (drive, &pc); - idefloppy_create_start_stop_cmd (&pc, 2); + idefloppy_create_prevent_cmd (&pc, prevent); (void) idefloppy_queue_pc_tail (drive, &pc); + if (cmd == CDROMEJECT) { + idefloppy_create_start_stop_cmd (&pc, 2); + (void) idefloppy_queue_pc_tail (drive, &pc); + } return 0; + case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED: + return (0); + case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY: + return (idefloppy_get_format_capacities(drive, inode, file, + (int *)arg)); + case IDEFLOPPY_IOCTL_FORMAT_START: + + if (!(file->f_mode & 2)) + return (-EPERM); + + { + idefloppy_floppy_t *floppy = drive->driver_data; + + set_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); + + if (drive->usage > 1) + { + /* Don't format if someone is using the disk */ + + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, + &floppy->flags); + return -EBUSY; + } + else + { + int rc=idefloppy_begin_format(drive, inode, + file, + (int *)arg); + + if (rc) + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, + &floppy->flags); + return (rc); + + /* + ** Note, the bit will be cleared when the device is + ** closed. This is the cleanest way to handle the + ** situation where the drive does not support + ** format progress reporting. + */ + } + } + case IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS: + return (idefloppy_get_format_progress(drive, inode, file, + (int *)arg)); } return -EIO; } @@ -1326,16 +1596,22 @@ MOD_INC_USE_COUNT; if (drive->usage == 1) { + + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); + /* Just in case */ + idefloppy_create_test_unit_ready_cmd(&pc); if (idefloppy_queue_pc_tail(drive, &pc)) { idefloppy_create_start_stop_cmd (&pc, 1); (void) idefloppy_queue_pc_tail (drive, &pc); } + if (idefloppy_get_capacity (drive)) { drive->usage--; MOD_DEC_USE_COUNT; return -EIO; } + if (floppy->wp && (filp->f_mode & 2)) { drive->usage--; MOD_DEC_USE_COUNT; @@ -1346,6 +1622,12 @@ (void) idefloppy_queue_pc_tail (drive, &pc); check_disk_change(inode->i_rdev); } + else if (test_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags)) + { + drive->usage--; + MOD_DEC_USE_COUNT; + return -EBUSY; + } return 0; } @@ -1358,9 +1640,13 @@ #endif /* IDEFLOPPY_DEBUG_LOG */ if (!drive->usage) { + idefloppy_floppy_t *floppy = drive->driver_data; + invalidate_buffers (inode->i_rdev); idefloppy_create_prevent_cmd (&pc, 0); (void) idefloppy_queue_pc_tail (drive, &pc); + + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); } MOD_DEC_USE_COUNT; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-pci.c linux.ac/drivers/ide/ide-pci.c --- linux.vanilla/drivers/ide/ide-pci.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/ide/ide-pci.c Mon May 21 01:22:28 2001 @@ -34,7 +34,8 @@ #define DEVID_PIIX4U ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_1}) #define DEVID_PIIX4U2 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82372FB_1}) #define DEVID_PIIX4NX ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX}) -#define DEVID_PIIX4U3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82820FW_5}) +#define DEVID_PIIX4U3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_9}) +#define DEVID_PIIX4U4 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_8}) #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}) @@ -74,6 +75,7 @@ #define DEVID_AMD7409 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409}) #define DEVID_SLC90E66 ((ide_pci_devid_t){PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1}) #define DEVID_OSB4 ((ide_pci_devid_t){PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE}) +#define DEVID_ITE8172G ((ide_pci_devid_t){PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G}) #define IDE_IGNORE ((void *)-1) @@ -185,8 +187,8 @@ #define INIT_HPT366 &ide_init_hpt366 #define DMA_HPT366 &ide_dmacapable_hpt366 #else -static byte hpt363_shared_irq = 0; -static byte hpt363_shared_pin = 0; +static byte hpt363_shared_irq; +static byte hpt363_shared_pin; #define PCI_HPT366 NULL #define ATA66_HPT366 NULL #define INIT_HPT366 NULL @@ -246,6 +248,18 @@ #define INIT_PIIX NULL #endif +#ifdef CONFIG_BLK_DEV_IT8172 +extern unsigned int pci_init_it8172(struct pci_dev *, const char *); +extern unsigned int ata66_it8172(ide_hwif_t *); +extern void ide_init_it8172(ide_hwif_t *); +#define PCI_IT8172 &pci_init_it8172 +#define INIT_IT8172 &ide_init_it8172 +#else +#define PCI_IT8172 NULL +#define ATA66_IT8172 NULL +#define INIT_IT8172 NULL +#endif + #ifdef CONFIG_BLK_DEV_RZ1000 extern void ide_init_rz1000(ide_hwif_t *); #define INIT_RZ1000 &ide_init_rz1000 @@ -343,6 +357,7 @@ {DEVID_PIIX4U2, "PIIX4", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_PIIX4NX, "PIIX4", PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_PIIX4U3, "PIIX4", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4U4, "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 }, @@ -381,6 +396,7 @@ {DEVID_AMD7409, "AMD7409", PCI_AMD7409, ATA66_AMD7409, INIT_AMD7409, DMA_AMD7409, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, {DEVID_SLC90E66,"SLC90E66", PCI_SLC90E66, ATA66_SLC90E66, INIT_SLC90E66, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_OSB4, "ServerWorks OSB4", PCI_OSB4, ATA66_OSB4, INIT_OSB4, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_ITE8172G,"IT8172G", PCI_IT8172, NULL, INIT_IT8172, NULL, {{0x00,0x00,0x00}, {0x40,0x00,0x01}}, ON_BOARD, 0 }, {IDE_PCI_DEVID_NULL, "PCI_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }}; /* @@ -523,9 +539,11 @@ byte tmp = 0; ide_hwif_t *hwif, *mate = NULL; unsigned int class_rev; + static int secondpdc = 0; #ifdef CONFIG_IDEDMA_AUTO - autodma = 1; + if (!noautodma) + autodma = 1; #endif pci_enable_device(dev); @@ -623,8 +641,18 @@ for (port = 0; port <= 1; ++port) { unsigned long base = 0, ctl = 0; ide_pci_enablebit_t *e = &(d->enablebits[port]); + + /* + * If this is a Promise FakeRaid controller, the 2nd controller will be marked as + * disabled while it is actually there and enabled by the bios for raid purposes. + * Skip the normal "is it enabled" test for those. + */ + if ((IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20265)) && (secondpdc++==1) && (port==1) ) + goto controller_ok; + if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || (tmp & e->mask) != e->val)) continue; /* port not enabled */ +controller_ok: if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) && (port) && (class_rev < 0x03)) return; if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE || (dev->class & (port ? 4 : 1)) != 0) { @@ -774,6 +802,12 @@ if (hpt363_shared_pin && hpt363_shared_irq) { d->bootable = ON_BOARD; printk("%s: onboard version of chipset, pin1=%d pin2=%d\n", d->name, pin1, pin2); +#if 0 + /* I forgot why I did this once, but it fixed something. */ + pci_write_config_byte(dev2, PCI_INTERRUPT_PIN, dev->irq); + printk("PCI: %s: Fixing interrupt %d pin %d to ZERO \n", d->name, dev2->irq, pin2); + pci_write_config_byte(dev2, PCI_INTERRUPT_LINE, 0); +#endif } break; } @@ -805,6 +839,8 @@ return; else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) && (!(PCI_FUNC(dev->devfn) & 1) || !((dev->class >> 8) == PCI_CLASS_STORAGE_IDE))) return; /* CY82C693 is more than only a IDE controller */ + else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_ITE8172G) && (!(PCI_FUNC(dev->devfn) & 1) || !((dev->class >> 8) == PCI_CLASS_STORAGE_IDE))) + return; /* IT8172G is also more than only an IDE controller */ else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886A) && !(PCI_FUNC(dev->devfn) & 1)) return; /* UM8886A/BF pair */ else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366)) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-pmac.c linux.ac/drivers/ide/ide-pmac.c --- linux.vanilla/drivers/ide/ide-pmac.c Fri Feb 16 01:22:08 2001 +++ linux.ac/drivers/ide/ide-pmac.c Tue Apr 3 17:54:41 2001 @@ -176,7 +176,8 @@ if (pmac_ide[ix].dma_regs && pmac_ide[ix].dma_table) { ide_hwifs[ix].dmaproc = &pmac_ide_dmaproc; #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO - ide_hwifs[ix].autodma = 1; + if (!noautodma) + ide_hwifs[ix].autodma = 1; #endif } } @@ -676,7 +677,8 @@ ide_hwifs[ix].dmaproc = &pmac_ide_dmaproc; #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO - ide_hwifs[ix].autodma = 1; + if (!noautodma) + ide_hwifs[ix].autodma = 1; #endif } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-probe.c linux.ac/drivers/ide/ide-probe.c --- linux.vanilla/drivers/ide/ide-probe.c Tue Apr 3 17:32:02 2001 +++ linux.ac/drivers/ide/ide-probe.c Fri May 4 17:09:18 2001 @@ -681,9 +681,9 @@ */ if (!match || match->irq != hwif->irq) { #ifdef CONFIG_IDEPCI_SHARE_IRQ - int sa = (hwif->chipset == ide_pci) ? SA_SHIRQ : SA_INTERRUPT; + int sa = IDE_CHIPSET_IS_PCI(hwif->chipset) ? SA_SHIRQ : SA_INTERRUPT; #else /* !CONFIG_IDEPCI_SHARE_IRQ */ - int sa = (hwif->chipset == ide_pci) ? SA_INTERRUPT|SA_SHIRQ : SA_INTERRUPT; + int sa = IDE_CHIPSET_IS_PCI(hwif->chipset) ? SA_INTERRUPT|SA_SHIRQ : SA_INTERRUPT; #endif /* CONFIG_IDEPCI_SHARE_IRQ */ if (ide_request_irq(hwif->irq, &ide_intr, sa, hwif->name, hwgroup)) { if (!match) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide-tape.c linux.ac/drivers/ide/ide-tape.c --- linux.vanilla/drivers/ide/ide-tape.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/ide/ide-tape.c Tue Apr 3 17:54:41 2001 @@ -1,5 +1,5 @@ /* - * linux/drivers/ide/ide-tape.c Version 1.16f Dec 15, 1999 + * linux/drivers/ide/ide-tape.c Version 1.17 Jan, 2001 * * Copyright (C) 1995 - 1999 Gadi Oxman * @@ -274,6 +274,18 @@ * this section correctly, a hypothetical and unwanted situation * is being described) * Ver 1.16f Dec 15 99 Change place of the secondary OnStream header frames. + * Ver 1.17 Nov 2000 / Jan 2001 Marcel Mol, marcel@mesa.nl + * - Add idetape_onstream_mode_sense_tape_parameter_page + * function to get tape capacity in frames: tape->capacity. + * - Add support for DI-50 drives( or any DI- drive). + * - 'workaround' for read error/blank block arround block 3000. + * - Implement Early warning for end of media for Onstream. + * - Cosmetic code changes for readability. + * - Idetape_position_tape should not use SKIP bit during + * Onstream read recovery. + * - Add capacity, logical_blk_num and first/last_frame_position + * to /proc/ide/hd?/settings. + * - Module use count was gone in the Linux 2.4 driver. * * * Here are some words from the first releases of hd.c, which are quoted @@ -384,7 +396,7 @@ * sharing a (fast) ATA-2 disk with any (slow) new ATAPI device. */ -#define IDETAPE_VERSION "1.16f" +#define IDETAPE_VERSION "1.17" #include #include @@ -421,7 +433,11 @@ #define OS_CONFIG_PARTITION (0xff) #define OS_DATA_PARTITION (0) #define OS_PARTITION_VERSION (1) +#define OS_EW 300 +#define OS_ADR_MINREV 2 +#define OS_DATA_STARTFRAME1 20 +#define OS_DATA_ENDFRAME1 2980 /* * partition */ @@ -512,12 +528,33 @@ } os_header_t; /* + * OnStream Tape Parameters Page + */ +typedef struct { + unsigned page_code :6; /* Page code - Should be 0x2b */ + unsigned reserved1_6 :1; + unsigned ps :1; + __u8 reserved2; + __u8 density; /* kbpi */ + __u8 reserved3,reserved4; + __u16 segtrk; /* segment of per track */ + __u16 trks; /* tracks per tape */ + __u8 reserved5,reserved6,reserved7,reserved8,reserved9,reserved10; +} onstream_tape_paramtr_page_t; + +/* * OnStream ADRL frame */ #define OS_FRAME_SIZE (32 * 1024 + 512) #define OS_DATA_SIZE (32 * 1024) #define OS_AUX_SIZE (512) +/* + * internal error codes for onstream + */ +#define OS_PART_ERROR 2 +#define OS_WRITE_ERROR 1 + #include /**************************** Tunable parameters *****************************/ @@ -949,6 +986,7 @@ int eod_frame_addr; unsigned long cmd_start_time; unsigned long max_cmd_time; + unsigned capacity; /* * Optimize the number of "buffer filling" @@ -1157,7 +1195,7 @@ typedef union { unsigned all :8; struct { - unsigned dma :1; /* Using DMA of PIO */ + unsigned dma :1; /* Using DMA or PIO */ unsigned reserved321 :3; /* Reserved */ unsigned reserved654 :3; /* Reserved (Tag Type) */ unsigned reserved7 :1; /* Reserved */ @@ -1287,7 +1325,9 @@ * by ide-tape. */ #define IDETAPE_CAPABILITIES_PAGE 0x2a +#define IDETAPE_PARAMTR_PAGE 0x2b /* onstream only */ #define IDETAPE_BLOCK_SIZE_PAGE 0x30 +#define IDETAPE_BUFFER_FILLING_PAGE 0x33 /* * Mode Parameter Header for the MODE SENSE packet command @@ -1428,6 +1468,14 @@ #endif /* IDETAPE_DEBUG_LOG_VERBOSE */ /* + * Function declarations + * + */ +static void idetape_onstream_mode_sense_tape_parameter_page(ide_drive_t *drive, int debug); +static int idetape_chrdev_release (struct inode *inode, struct file *filp); +static void idetape_write_release (struct inode *inode); + +/* * Too bad. The drive wants to send us data which we are not ready to accept. * Just throw it away. */ @@ -1452,7 +1500,8 @@ #endif /* IDETAPE_DEBUG_BUGS */ count = IDE_MIN (bh->b_size - atomic_read(&bh->b_count), bcount); atapi_input_bytes (drive, bh->b_data + atomic_read(&bh->b_count), count); - bcount -= count; atomic_add(count, &bh->b_count); + bcount -= count; + atomic_add(count, &bh->b_count); if (atomic_read(&bh->b_count) == bh->b_size) { bh = bh->b_reqnext; if (bh) @@ -1476,7 +1525,9 @@ #endif /* IDETAPE_DEBUG_BUGS */ count = IDE_MIN (pc->b_count, bcount); atapi_output_bytes (drive, pc->b_data, count); - bcount -= count; pc->b_data += count; pc->b_count -= count; + bcount -= count; + pc->b_data += count; + pc->b_count -= count; if (!pc->b_count) { pc->bh = bh = bh->b_reqnext; if (bh) { @@ -1577,20 +1628,23 @@ * 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) +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 = *result; - tape->sense_key = result->sense_key; tape->asc = result->asc; tape->ascq = result->ascq; + 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. */ if (tape->debug_level >= 1) - 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); + 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); #if IDETAPE_DEBUG_LOG_VERBOSE if (tape->debug_level >= 1) printk (KERN_INFO "ide-tape: pc = %s, sense key = %x, asc = %x, ascq = %x\n", @@ -1660,7 +1714,7 @@ static void idetape_active_next_stage (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; - idetape_stage_t *stage=tape->next_stage; + idetape_stage_t *stage = tape->next_stage; struct request *rq = &stage->rq; #if IDETAPE_DEBUG_LOG @@ -1676,9 +1730,9 @@ rq->buffer = NULL; rq->bh = stage->bh; - tape->active_data_request=rq; - tape->active_stage=stage; - tape->next_stage=stage->next; + tape->active_data_request = rq; + tape->active_stage = stage; + tape->next_stage = stage->next; } /* @@ -1756,12 +1810,12 @@ return; } #endif /* IDETAPE_DEBUG_BUGS */ - stage=tape->first_stage; - tape->first_stage=stage->next; + stage = tape->first_stage; + tape->first_stage = stage->next; idetape_kfree_stage (tape, stage); tape->nr_stages--; if (tape->first_stage == NULL) { - tape->last_stage=NULL; + tape->last_stage = NULL; #if IDETAPE_DEBUG_BUGS if (tape->next_stage != NULL) printk (KERN_ERR "ide-tape: bug: tape->next_stage != NULL\n"); @@ -1821,12 +1875,12 @@ } #endif if (tape->onstream && !tape->raw) { - if (tape->first_frame_position == 0xba4) { + if (tape->first_frame_position == OS_DATA_ENDFRAME1) { #if ONSTREAM_DEBUG - if (tape->debug_level >= 2) - printk("ide-tape: %s: skipping over config parition..\n", tape->name); + if (tape->debug_level >= 2) + printk("ide-tape: %s: skipping over config parition..\n", tape->name); #endif - tape->onstream_write_error = 2; + tape->onstream_write_error = OS_PART_ERROR; if (tape->sem) up(tape->sem); } @@ -1839,7 +1893,7 @@ 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; + tape->onstream_write_error = OS_WRITE_ERROR; remove_stage = 0; tape->nr_pending_stages++; tape->next_stage = tape->first_stage; @@ -1883,11 +1937,11 @@ printk (KERN_INFO "ide-tape: Reached idetape_request_sense_callback\n"); #endif /* IDETAPE_DEBUG_LOG */ if (!tape->pc->error) { - idetape_analyze_error (drive,(idetape_request_sense_result_t *) tape->pc->buffer); - idetape_end_request (1,HWGROUP (drive)); + idetape_analyze_error (drive, (idetape_request_sense_result_t *) tape->pc->buffer); + idetape_end_request (1, HWGROUP (drive)); } else { printk (KERN_ERR "ide-tape: Error in REQUEST SENSE itself - Aborting request!\n"); - idetape_end_request (0,HWGROUP (drive)); + idetape_end_request (0, HWGROUP (drive)); } return ide_stopped; } @@ -1980,7 +2034,7 @@ idetape_status_reg_t status; idetape_bcount_reg_t bcount; idetape_ireason_reg_t ireason; - idetape_pc_t *pc=tape->pc; + idetape_pc_t *pc = tape->pc; unsigned int temp; unsigned long cmd_time; @@ -2011,7 +2065,7 @@ */ set_bit (PC_DMA_ERROR, &pc->flags); } else if (!status.b.check) { - pc->actually_transferred=pc->request_transfer; + pc->actually_transferred = pc->request_transfer; idetape_update_buffers (pc); } #if IDETAPE_DEBUG_LOG @@ -2064,7 +2118,7 @@ return ide_stopped; } if (tape->failed_pc == pc) - tape->failed_pc=NULL; + tape->failed_pc = NULL; return pc->callback(drive); /* Command finished - Call the callback function */ } #ifdef CONFIG_BLK_DEV_IDEDMA @@ -2075,9 +2129,9 @@ return ide_do_reset (drive); } #endif /* CONFIG_BLK_DEV_IDEDMA */ - bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */ - bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG); /* on this interrupt */ - ireason.all=IN_BYTE (IDE_IREASON_REG); + bcount.b.high = IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */ + bcount.b.low = IN_BYTE (IDE_BCOUNTL_REG); /* on this interrupt */ + ireason.all = IN_BYTE (IDE_IREASON_REG); if (ireason.b.cod) { printk (KERN_ERR "ide-tape: CoD != 0 in idetape_pc_intr\n"); @@ -2093,8 +2147,8 @@ if ( temp > pc->request_transfer) { if (temp > pc->buffer_size) { printk (KERN_ERR "ide-tape: The tape wants to send us more data than expected - discarding data\n"); - idetape_discard_data (drive,bcount.all); - ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD,NULL); + idetape_discard_data (drive, bcount.all); + ide_set_handler (drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); return ide_started; } #if IDETAPE_DEBUG_LOG @@ -2114,13 +2168,13 @@ else atapi_input_bytes (drive,pc->current_position,bcount.all); /* Read the current buffer */ } - pc->actually_transferred+=bcount.all; /* Update the current position */ + pc->actually_transferred += bcount.all; /* Update the current position */ pc->current_position+=bcount.all; #if IDETAPE_DEBUG_LOG if (tape->debug_level >= 2) printk(KERN_INFO "ide-tape: [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 */ + ide_set_handler (drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); /* And set the interrupt handler again */ return ide_started; } @@ -2178,7 +2232,7 @@ printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n"); return startstop; } - ireason.all=IN_BYTE (IDE_IREASON_REG); + ireason.all = IN_BYTE (IDE_IREASON_REG); while (retries-- && (!ireason.b.cod || ireason.b.io)) { printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing a packet command, retrying\n"); udelay(100); @@ -2203,7 +2257,7 @@ { idetape_tape_t *tape = drive->driver_data; idetape_bcount_reg_t bcount; - int dma_ok=0; + int dma_ok = 0; #if IDETAPE_DEBUG_BUGS if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) { @@ -2212,8 +2266,8 @@ #endif /* IDETAPE_DEBUG_BUGS */ if (tape->failed_pc == NULL && pc->c[0] != IDETAPE_REQUEST_SENSE_CMD) - tape->failed_pc=pc; - tape->pc=pc; /* Set the current packet command */ + tape->failed_pc = pc; + tape->pc = pc; /* Set the current packet command */ if (pc->retries > IDETAPE_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) { /* @@ -2223,24 +2277,25 @@ * example). */ if (!test_bit (PC_ABORT, &pc->flags)) { - if (!(pc->c[0] == 0 && tape->sense_key == 2 && tape->asc == 4 && (tape->ascq == 1 || tape->ascq == 8))) { + if (!(pc->c[0] == IDETAPE_TEST_UNIT_READY_CMD && 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 == 0x11) /* AJN-1: 11 should be 0x11 */ + if (tape->onstream && pc->c[0] == IDETAPE_READ_CMD && tape->sense_key == 3 && tape->asc == 0x11) /* AJN-1: 11 should be 0x11 */ printk(KERN_ERR "ide-tape: %s: enabling read error recovery\n", tape->name); } pc->error = IDETAPE_ERROR_GENERAL; /* Giving up */ } - tape->failed_pc=NULL; + tape->failed_pc = NULL; return pc->callback(drive); } #if IDETAPE_DEBUG_LOG if (tape->debug_level >= 2) - printk (KERN_INFO "ide-tape: Retry number - %d\n",pc->retries); + printk (KERN_INFO "ide-tape: Retry number - %d\n", pc->retries); #endif /* IDETAPE_DEBUG_LOG */ pc->retries++; - pc->actually_transferred=0; /* We haven't transferred any data yet */ + pc->actually_transferred = 0; /* We haven't transferred any data yet */ pc->current_position=pc->buffer; bcount.all=pc->request_transfer; /* Request to transfer the entire buffer at once */ @@ -2250,15 +2305,15 @@ (void) HWIF(drive)->dmaproc(ide_dma_off, drive); } if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) - dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); + dma_ok = !HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); #endif /* CONFIG_BLK_DEV_IDEDMA */ if (IDE_CONTROL_REG) - OUT_BYTE (drive->ctl,IDE_CONTROL_REG); - OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */ - OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG); - OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG); - OUT_BYTE (drive->select.all,IDE_SELECT_REG); + OUT_BYTE (drive->ctl, IDE_CONTROL_REG); + OUT_BYTE (dma_ok ? 1 : 0, IDE_FEATURE_REG); /* Use PIO/DMA */ + OUT_BYTE (bcount.b.high, IDE_BCOUNTH_REG); + OUT_BYTE (bcount.b.low, IDE_BCOUNTL_REG); + OUT_BYTE (drive->select.all, IDE_SELECT_REG); #ifdef CONFIG_BLK_DEV_IDEDMA if (dma_ok) { /* Begin DMA, if necessary */ set_bit (PC_DMA_IN_PROGRESS, &pc->flags); @@ -2287,7 +2342,7 @@ printk (KERN_INFO "ide-tape: Reached idetape_pc_callback\n"); #endif /* IDETAPE_DEBUG_LOG */ - idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive)); + idetape_end_request (tape->pc->error ? 0 : 1, HWGROUP(drive)); return ide_stopped; } @@ -2333,7 +2388,7 @@ if (tape->debug_level >= 1) printk(KERN_INFO "ide-tape: buffer fill callback, %d/%d\n", tape->cur_frames, tape->max_frames); #endif - idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive)); + idetape_end_request (tape->pc->error ? 0 : 1, HWGROUP(drive)); return ide_stopped; } @@ -2344,7 +2399,7 @@ pc = idetape_next_pc_storage (drive); rq = idetape_next_rq_storage (drive); - idetape_create_mode_sense_cmd (pc, 0x33); + idetape_create_mode_sense_cmd (pc, IDETAPE_BUFFER_FILLING_PAGE); pc->callback = idetape_onstream_buffer_fill_callback; idetape_queue_pc_head (drive, pc, rq); } @@ -2564,7 +2619,7 @@ * We do not support buffer cache originated requests. */ printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue (%d)\n", drive->name, rq->cmd); - ide_end_request (0,HWGROUP (drive)); /* Let the common code handle it */ + ide_end_request (0, HWGROUP (drive)); /* Let the common code handle it */ return ide_stopped; } @@ -2578,7 +2633,7 @@ if (postponed_rq != NULL) if (rq != postponed_rq) { printk (KERN_ERR "ide-tape: ide-tape.c bug - Two DSC requests were queued\n"); - idetape_end_request (0,HWGROUP (drive)); + idetape_end_request (0, HWGROUP (drive)); return ide_stopped; } #endif /* IDETAPE_DEBUG_BUGS */ @@ -2624,8 +2679,15 @@ 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))) { + ((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 if (tape->debug_level >= 4) printk(KERN_INFO "ide-tape: postponing request, cmd %d, cur %d, max %d\n", @@ -2672,7 +2734,7 @@ if (jiffies > tape->last_buffer_fill + 5 * HZ / 100) tape->req_buffer_fill = 1; } - pc=idetape_next_pc_storage (drive); + pc = idetape_next_pc_storage (drive); idetape_create_read_cmd (tape, pc, rq->current_nr_sectors, rq->bh); break; case IDETAPE_WRITE_RQ: @@ -2689,12 +2751,12 @@ tape->req_buffer_fill = 1; calculate_speeds(drive); } - pc=idetape_next_pc_storage (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); + 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: @@ -2710,7 +2772,7 @@ idetape_end_request (IDETAPE_ERROR_EOD, HWGROUP(drive)); return ide_stopped; case IDETAPE_PC_RQ1: - pc=(idetape_pc_t *) rq->buffer; + pc = (idetape_pc_t *) rq->buffer; rq->cmd = IDETAPE_PC_RQ2; break; case IDETAPE_PC_RQ2: @@ -2718,7 +2780,7 @@ return ide_stopped; default: printk (KERN_ERR "ide-tape: bug in IDETAPE_RQ_CMD macro\n"); - idetape_end_request (0,HWGROUP (drive)); + idetape_end_request (0, HWGROUP (drive)); return ide_stopped; } return idetape_issue_packet_command (drive, pc); @@ -2844,7 +2906,9 @@ #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; + 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) @@ -2868,7 +2932,10 @@ #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; + n -= count; + tape->b_data += count; + tape->b_count -= count; + buf += count; if (!tape->b_count) { tape->bh = bh = bh->b_reqnext; if (bh) { @@ -2920,10 +2987,10 @@ if (tape->last_stage != NULL) tape->last_stage->next=stage; else - tape->first_stage=tape->next_stage=stage; - tape->last_stage=stage; + tape->first_stage = tape->next_stage=stage; + tape->last_stage = stage; if (tape->next_stage == NULL) - tape->next_stage=tape->last_stage; + tape->next_stage = tape->last_stage; tape->nr_stages++; tape->nr_pending_stages++; spin_unlock_irqrestore(&tape->spinlock, flags); @@ -2953,26 +3020,21 @@ 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); + par->last_frame_addr = htonl(0xbb7); /* 2999 */ + aux->frame_seq_num = htonl(0); + aux->logical_blk_num_high = htonl(0); + aux->logical_blk_num = htonl(0); + aux->next_mark_addr = htonl(tape->first_mark_addr); } 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) { + par->first_frame_addr = htonl(OS_DATA_STARTFRAME1); + par->last_frame_addr = htonl(tape->capacity); 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; @@ -2987,11 +3049,10 @@ 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); + } + aux->filemark_cnt = ntohl(tape->filemark_cnt); /* shouldn't this be htonl ?? */ + aux->phys_fm = ntohl(0xffffffff); /* shouldn't this be htonl ?? */ + aux->last_mark_addr = ntohl(tape->last_mark_addr); /* shouldn't this be htonl ?? */ } /* @@ -3042,7 +3103,7 @@ 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)); + idetape_end_request (0, HWGROUP (drive)); } else { #if IDETAPE_DEBUG_LOG if (tape->debug_level >= 2) @@ -3053,10 +3114,10 @@ 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)); + idetape_end_request (1, HWGROUP (drive)); } } else { - idetape_end_request (0,HWGROUP (drive)); + idetape_end_request (0, HWGROUP (drive)); } return ide_stopped; } @@ -3076,8 +3137,8 @@ idetape_init_pc (pc); pc->c[0] = IDETAPE_WRITE_FILEMARK_CMD; if (tape->onstream) - pc->c[1] = 1; - pc->c[4] = write_filemark; + pc->c[1] = 1; /* Immed bit */ + pc->c[4] = write_filemark; /* not used for OnStream ?? */ set_bit (PC_WAIT_FOR_DSC, &pc->flags); pc->callback = &idetape_pc_callback; } @@ -3109,7 +3170,7 @@ * 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) +static int __idetape_queue_pc_tail (ide_drive_t *drive, idetape_pc_t *pc) { struct request rq; @@ -3150,7 +3211,7 @@ 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_queue_pc_tail(drive, &pc); idetape_create_test_unit_ready_cmd(&pc); if (!__idetape_queue_pc_tail(drive, &pc)) return 0; @@ -3169,7 +3230,8 @@ int rc; rc = __idetape_queue_pc_tail(drive, pc); - if (rc) return rc; + if (rc) + return rc; if (tape->onstream && test_bit(PC_WAIT_FOR_DSC, &pc->flags)) rc = idetape_wait_ready(drive, 60 * 10 * HZ); /* AJN-4: Changed from 5 to 10 minutes; because retension takes approx. 8:20 with Onstream 30GB tape */ @@ -3182,7 +3244,7 @@ int rc; idetape_create_write_filemark_cmd(drive, &pc, 0); - if ((rc = idetape_queue_pc_tail (drive,&pc))) + if ((rc = idetape_queue_pc_tail (drive, &pc))) return rc; idetape_wait_ready(drive, 60 * 5 * HZ); return 0; @@ -3206,7 +3268,7 @@ idetape_flush_tape_buffers(drive); #endif idetape_create_read_position_cmd(&pc); - if (idetape_queue_pc_tail (drive,&pc)) + if (idetape_queue_pc_tail (drive, &pc)) return -1; position = tape->first_frame_position; #ifdef NO_LONGER_REQUIRED @@ -3231,12 +3293,17 @@ idetape_init_pc (pc); pc->c[0] = IDETAPE_LOCATE_CMD; if (tape->onstream) - pc->c[1] = 1; + pc->c[1] = 1; /* Immediate bit */ else pc->c[1] = 2; put_unaligned (htonl (block), (unsigned int *) &pc->c[3]); pc->c[8] = partition; if (tape->onstream) + /* + * Set SKIP bit. + * In case of write error this will write buffered + * data in the drive to this new position! + */ pc->c[9] = skip << 7; set_bit (PC_WAIT_FOR_DSC, &pc->flags); pc->callback = &idetape_pc_callback; @@ -3307,11 +3374,12 @@ __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); + retval = idetape_queue_pc_tail (drive, &pc); + if (retval) + return (retval); idetape_create_read_position_cmd (&pc); - return (idetape_queue_pc_tail (drive,&pc)); + return (idetape_queue_pc_tail (drive, &pc)); } static void idetape_discard_read_pipeline (ide_drive_t *drive, int restore_position) @@ -3339,7 +3407,7 @@ { idetape_pc_t pc; - idetape_create_mode_sense_cmd (&pc, 0x33); + idetape_create_mode_sense_cmd (&pc, IDETAPE_BUFFER_FILLING_PAGE); pc.callback = idetape_onstream_buffer_fill_callback; (void) idetape_queue_pc_tail(drive, &pc); } @@ -3447,33 +3515,41 @@ 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 %u\n", tape->name, ntohl(tape->sense.information)); - block = ntohl(tape->sense.information) + 80; + if (tape->onstream_write_error == OS_WRITE_ERROR) { + printk(KERN_ERR "ide-tape: %s: onstream_write_error_recovery: detected physical bad block at %u, logical %u first frame %u last_frame %u bufblocks %u stages %u skipping %u frames\n", + tape->name, ntohl(tape->sense.information), tape->logical_blk_num, + tape->first_frame_position, tape->last_frame_position, + tape->blocks_in_buffer, tape->nr_stages, + (ntohl(tape->sense.command_specific) >> 16) & 0xff ); + block = ntohl(tape->sense.information) + ((ntohl(tape->sense.command_specific) >> 16) & 0xff); 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); +#if 0 /* isn't once enough ??? MM */ idetape_update_stats(drive); +#endif 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); } +#if 0 /* already done in idetape_position_tape MM */ idetape_read_position(drive); +#endif #if ONSTREAM_DEBUG if (tape->debug_level >= 1) 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) { + } else if (tape->onstream_write_error == OS_PART_ERROR) { #if ONSTREAM_DEBUG if (tape->debug_level >= 1) 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); + if (block != OS_DATA_ENDFRAME1) + printk(KERN_ERR "ide-tape: warning, current position %d, expected %d\n", block, OS_DATA_ENDFRAME1); + idetape_position_tape(drive, 0xbb8, 0, 0); /* 3000 */ } tape->onstream_write_error = 0; } @@ -3572,48 +3648,48 @@ return 1; } if (rq->errors == IDETAPE_ERROR_GENERAL) { - printk(KERN_INFO "ide-tape: %s: skipping frame, read error\n", tape->name); + printk(KERN_INFO "ide-tape: %s: skipping frame %d, read error\n", tape->name, tape->first_frame_position); return 0; } if (rq->errors == IDETAPE_ERROR_EOD) { - printk(KERN_INFO "ide-tape: %s: skipping frame, eod\n", tape->name); + printk(KERN_INFO "ide-tape: %s: skipping frame %d, eod\n", tape->name, tape->first_frame_position); return 0; } if (ntohl(aux->format_id) != 0) { - printk(KERN_INFO "ide-tape: %s: skipping frame, format_id %u\n", tape->name, ntohl(aux->format_id)); + printk(KERN_INFO "ide-tape: %s: skipping frame %d, format_id %u\n", tape->name, tape->first_frame_position, 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); + printk(KERN_INFO "ide-tape: %s: skipping frame %d, incorrect application signature\n", tape->name, tape->first_frame_position); 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); + printk(KERN_INFO "ide-tape: %s: skipping frame %d, frame type %x\n", tape->name, tape->first_frame_position, 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); + printk(KERN_INFO "ide-tape: %s: skipping frame %d, partition num %d\n", tape->name, tape->first_frame_position, 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); + printk(KERN_INFO "ide-tape: %s: skipping frame %d, partition version %d\n", tape->name, tape->first_frame_position, 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)(logical_blk_num %u)\n", tape->name, ntohs(par->wrt_pass_cntr), tape->wrt_pass_cntr, ntohl(aux->logical_blk_num)); + printk(KERN_INFO "ide-tape: %s: skipping frame %d, wrt_pass_cntr %d (expected %d)(logical_blk_num %u)\n", tape->name, tape->first_frame_position, ntohs(par->wrt_pass_cntr), tape->wrt_pass_cntr, ntohl(aux->logical_blk_num)); return 0; } if (aux->frame_seq_num != aux->logical_blk_num) { - printk(KERN_INFO "ide-tape: %s: skipping frame, seq != logical\n", tape->name); + printk(KERN_INFO "ide-tape: %s: skipping frame %d, seq != logical\n", tape->name, tape->first_frame_position); 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 %u (expected %d)\n", tape->name, ntohl(aux->logical_blk_num), logical_blk_num); + printk(KERN_INFO "ide-tape: %s: skipping frame %d, logical_blk_num %u (expected %d)\n", tape->name, tape->first_frame_position, ntohl(aux->logical_blk_num), logical_blk_num); return 0; } if (aux->frame_type == OS_FRAME_TYPE_MARKER) { @@ -3689,7 +3765,7 @@ 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); + 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); @@ -3766,7 +3842,7 @@ } #endif /* IDETAPE_DEBUG_BUGS */ if (tape->merge_stage_size) { - blocks=tape->merge_stage_size/tape->tape_block_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; @@ -3797,7 +3873,7 @@ tape->merge_stage = NULL; } clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags); - tape->chrdev_direction=idetape_direction_none; + tape->chrdev_direction = idetape_direction_none; /* * On the next backup, perform the feedback loop again. @@ -3874,13 +3950,13 @@ 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); + new_stage = idetape_kmalloc_stage (tape); while (new_stage != NULL) { - new_stage->rq=rq; - idetape_add_stage_tail (drive,new_stage); + new_stage->rq = rq; + idetape_add_stage_tail (drive, new_stage); if (tape->nr_stages >= max_stages) break; - new_stage=idetape_kmalloc_stage (tape); + new_stage = idetape_kmalloc_stage (tape); } } if (!idetape_pipeline_active(tape)) { @@ -3922,16 +3998,23 @@ #endif clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); position = idetape_read_position(drive); + printk(KERN_INFO "ide-tape: %s: blank block detected at %d\n", tape->name, position); if (position >= 3000 && position < 3080) - position += 32; - if (position >= 2980 && position < 3000) + position += 32; /* Why is this check and number ??? MM */ + if (position >= OS_DATA_ENDFRAME1 && position < 3000) position = 3000; else + /* + * compensate for write errors that generally skip 80 frames, + * expect around 20 read errors in a row... + */ position += 60; - if (position >= 2980 && position < 3000) + if (position >= OS_DATA_ENDFRAME1 && position < 3000) position = 3000; - printk(KERN_INFO "ide-tape: %s: blank block detected, positioning tape to block %d\n", tape->name, position); - idetape_position_tape(drive, position, 0, 1); + printk(KERN_INFO "ide-tape: %s: positioning tape to block %d\n", tape->name, position); + if (position == 3000) /* seems to be needed to correctly position at block 3000 MM */ + idetape_position_tape(drive, 0, 0, 0); + idetape_position_tape(drive, position, 0, 0); cnt += 40; continue; } else @@ -4089,12 +4172,14 @@ #endif /* IDETAPE_DEBUG_LOG */ idetape_create_rewind_cmd (drive, &pc); - retval=idetape_queue_pc_tail (drive,&pc); - if (retval) return retval; + retval = idetape_queue_pc_tail (drive, &pc); + if (retval) + return retval; idetape_create_read_position_cmd (&pc); - retval = idetape_queue_pc_tail (drive,&pc); - if (retval) return retval; + retval = idetape_queue_pc_tail (drive, &pc); + if (retval) + return retval; tape->logical_blk_num = 0; return 0; } @@ -4412,7 +4497,7 @@ switch (mt_op) { case MTFSF: idetape_create_space_cmd (&pc,mt_count-count,IDETAPE_SPACE_OVER_FILEMARK); - return (idetape_queue_pc_tail (drive,&pc)); + return (idetape_queue_pc_tail (drive, &pc)); case MTFSFM: if (!tape->capabilities.sprev) return (-EIO); @@ -4423,7 +4508,7 @@ if (!tape->capabilities.sprev) return (-EIO); idetape_create_space_cmd (&pc,-(mt_count+count),IDETAPE_SPACE_OVER_FILEMARK); - return (idetape_queue_pc_tail (drive,&pc)); + return (idetape_queue_pc_tail (drive, &pc)); case MTBSFM: if (!tape->capabilities.sprev) return (-EIO); @@ -4460,7 +4545,7 @@ 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, rc; + ssize_t bytes_read,temp, actually_read = 0, rc; if (ppos != &file->f_pos) { /* "A request was outside the capabilities of the device." */ @@ -4482,28 +4567,32 @@ } if ((rc = idetape_initiate_read(drive, tape->max_stages)) < 0) return rc; - if (count==0) + if (count == 0) return (0); if (tape->merge_stage_size) { - actually_read=IDE_MIN (tape->merge_stage_size,count); + actually_read = IDE_MIN (tape->merge_stage_size, count); idetape_copy_stage_to_user (tape, buf, tape->merge_stage, actually_read); - buf += actually_read; tape->merge_stage_size -= actually_read; count-=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); + 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; + 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); + 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; + actually_read += temp; + tape->merge_stage_size = bytes_read-temp; } finish: if (!actually_read && test_bit (IDETAPE_FILEMARK, &tape->flags)) { @@ -4515,7 +4604,8 @@ 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); + 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; } @@ -4576,6 +4666,37 @@ return; } +static void idetape_write_filler (ide_drive_t *drive, int block, int cnt) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage; + int rc; + + if (!tape->onstream || tape->raw) + return; + stage = __idetape_kmalloc_stage(tape, 1, 1); + if (stage == NULL) + return; + idetape_init_stage(drive, stage, OS_FRAME_TYPE_FILL, 0); + idetape_wait_ready(drive, 60 * 5 * HZ); + rc = idetape_position_tape(drive, block, 0, 0); +#if ONSTREAM_DEBUG + printk(KERN_INFO "write_filler: positioning failed it returned %d\n", rc); +#endif + if (rc != 0) + return; /* don't write fillers if we cannot position the tape. */ + + strcpy(stage->bh->b_data, "Filler"); + while (cnt--) { + if (!idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 1, stage->bh)) { + printk(KERN_INFO "ide-tape: %s: write_filler: couldn't write header frame\n", tape->name); + __idetape_kfree_stage (stage); + return; + } + } + __idetape_kfree_stage (stage); +} + static void __idetape_write_header (ide_drive_t *drive, int block, int cnt) { idetape_tape_t *tape = drive->driver_data; @@ -4591,12 +4712,12 @@ memset(&header, 0, sizeof(header)); strcpy(header.ident_str, "ADR_SEQ"); header.major_rev = 1; - header.minor_rev = 2; + header.minor_rev = OS_ADR_MINREV; 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.first_frame_addr = htonl(OS_DATA_STARTFRAME1); + header.partition.last_frame_addr = htonl(tape->capacity); 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)); @@ -4623,7 +4744,7 @@ return; tape->update_frame_cntr++; __idetape_write_header(drive, 5, 5); - __idetape_write_header(drive, 0xbae, 5); + __idetape_write_header(drive, 0xbae, 5); /* 2990 */ if (locate_eod) { #if ONSTREAM_DEBUG if (tape->debug_level >= 2) @@ -4639,22 +4760,39 @@ 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 retval,actually_written=0; + 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 (%Zd used)\n", tape->name, tape->tape_block_size, count); - return -EINVAL; - } + #if IDETAPE_DEBUG_LOG if (tape->debug_level >= 3) printk (KERN_INFO "ide-tape: Reached idetape_chrdev_write, count %Zd\n", count); #endif /* IDETAPE_DEBUG_LOG */ + if (tape->onstream) { + if (count != tape->tape_block_size) { + printk(KERN_ERR "ide-tape: %s: chrdev_write: use %d bytes as block size (%d used)\n", + tape->name, tape->tape_block_size, count); + return -EINVAL; + } + /* + * Check if we reach the end of the tape. Just assume the whole pipeline + * is filled with write requests! + */ + if (tape->first_frame_position + tape->nr_stages >= tape->capacity - OS_EW) { +#if ONSTREAM_DEBUG + printk(KERN_INFO, "chrdev_write: Write truncated at EOM early warning"); +#endif + if (tape->chrdev_direction == idetape_direction_write) + idetape_write_release(inode); + return -ENOSPC; + } + } + if (tape->chrdev_direction != idetape_direction_write) { /* Initialize write operation */ if (tape->chrdev_direction == idetape_direction_read) idetape_discard_read_pipeline (drive, 1); @@ -4671,17 +4809,17 @@ if (tape->onstream) { position = idetape_read_position(drive); - if (position <= 20) { + if (position <= OS_DATA_STARTFRAME1) { tape->logical_blk_num = 0; tape->wrt_pass_cntr++; #if ONSTREAM_DEBUG if (tape->debug_level >= 2) - printk(KERN_INFO "ide-tape: %s: logical block num 0, setting eod to 20\n", tape->name); + printk(KERN_INFO "ide-tape: %s: logical block num 0, setting eod to %d\n", tape->name, OS_DATA_STARTFRAME1); if (tape->debug_level >= 2) 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->eod_frame_addr = OS_DATA_STARTFRAME1; tape->first_mark_addr = tape->last_mark_addr = -1; idetape_write_header(drive, 1); } @@ -4715,7 +4853,7 @@ printk("ide-tape: first_frame_position %d\n", tape->first_frame_position); #endif } - if (count==0) + if (count == 0) return (0); if (tape->restart_speed_control_req) idetape_restart_speed_control(drive); @@ -4723,32 +4861,35 @@ #if IDETAPE_DEBUG_BUGS if (tape->merge_stage_size >= tape->stage_size) { printk (KERN_ERR "ide-tape: bug: merge buffer too big\n"); - tape->merge_stage_size=0; + tape->merge_stage_size = 0; } #endif /* IDETAPE_DEBUG_BUGS */ - actually_written=IDE_MIN (tape->stage_size-tape->merge_stage_size,count); + actually_written = IDE_MIN (tape->stage_size - tape->merge_stage_size, count); idetape_copy_stage_from_user (tape, tape->merge_stage, buf, actually_written); - buf+=actually_written;tape->merge_stage_size+=actually_written;count-=actually_written; + buf += actually_written; + tape->merge_stage_size += actually_written; + count -= actually_written; if (tape->merge_stage_size == tape->stage_size) { tape->merge_stage_size = 0; - retval=idetape_add_chrdev_write_request (drive, tape->capabilities.ctl); + retval = idetape_add_chrdev_write_request (drive, tape->capabilities.ctl); if (retval <= 0) return (retval); } } while (count >= tape->stage_size) { idetape_copy_stage_from_user (tape, tape->merge_stage, buf, tape->stage_size); - buf+=tape->stage_size;count-=tape->stage_size; - retval=idetape_add_chrdev_write_request (drive, tape->capabilities.ctl); - actually_written+=tape->stage_size; + buf += tape->stage_size; + count -= tape->stage_size; + retval = idetape_add_chrdev_write_request (drive, tape->capabilities.ctl); + actually_written += tape->stage_size; if (retval <= 0) return (retval); } if (count) { actually_written+=count; idetape_copy_stage_from_user (tape, tape->merge_stage, buf, count); - tape->merge_stage_size+=count; + tape->merge_stage_size += count; } return (actually_written); } @@ -4760,8 +4901,8 @@ 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)) { + 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; } @@ -4937,24 +5078,24 @@ if (idetape_rewind_tape(drive)) return -EIO; if (tape->onstream && !tape->raw) - return idetape_position_tape(drive, 20, 0, 0); + return idetape_position_tape(drive, OS_DATA_STARTFRAME1, 0, 0); return 0; case MTLOAD: idetape_discard_read_pipeline (drive, 0); idetape_create_load_unload_cmd (drive, &pc, IDETAPE_LU_LOAD_MASK); - return (idetape_queue_pc_tail (drive,&pc)); + return (idetape_queue_pc_tail (drive, &pc)); case MTUNLOAD: case MTOFFL: idetape_discard_read_pipeline (drive, 0); idetape_create_load_unload_cmd (drive, &pc,!IDETAPE_LU_LOAD_MASK); - return (idetape_queue_pc_tail (drive,&pc)); + return (idetape_queue_pc_tail (drive, &pc)); case MTNOP: idetape_discard_read_pipeline (drive, 0); return (idetape_flush_tape_buffers (drive)); case MTRETEN: 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)); + return (idetape_queue_pc_tail (drive, &pc)); case MTEOM: if (tape->onstream) { #if ONSTREAM_DEBUG @@ -4968,24 +5109,30 @@ return -EIO; return 0; } - idetape_create_space_cmd (&pc,0,IDETAPE_SPACE_TO_EOD); - return (idetape_queue_pc_tail (drive,&pc)); + 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->eod_frame_addr = OS_DATA_STARTFRAME1; 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); + /* + * write filler frames to the unused frames... + * REMOVE WHEN going to LIN4 application type... + */ + idetape_write_filler(drive, OS_DATA_STARTFRAME1 - 10, 10); + idetape_write_filler(drive, OS_DATA_ENDFRAME1, 10); 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)); + return (idetape_queue_pc_tail (drive, &pc)); case MTSETBLK: if (tape->onstream) { if (mt_count != tape->tape_block_size) { @@ -5028,14 +5175,14 @@ case MTLOCK: if (!idetape_create_prevent_cmd(drive, &pc, 1)) return 0; - retval = idetape_queue_pc_tail (drive,&pc); + retval = idetape_queue_pc_tail (drive, &pc); if (retval) return retval; tape->door_locked = DOOR_EXPLICITLY_LOCKED; return 0; case MTUNLOCK: if (!idetape_create_prevent_cmd(drive, &pc, 0)) return 0; - retval = idetape_queue_pc_tail (drive,&pc); + retval = idetape_queue_pc_tail (drive, &pc); if (retval) return retval; tape->door_locked = DOOR_UNLOCKED; return 0; @@ -5113,7 +5260,7 @@ 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) + if (position <= OS_DATA_STARTFRAME1) mtget.mt_gstat |= GMT_BOT(0xffffffff); } if (copy_to_user ((char *) arg,(char *) &mtget, sizeof (struct mtget))) @@ -5150,7 +5297,7 @@ tape->header_ok = tape->linux_media = 0; tape->update_frame_cntr = 0; tape->wrt_pass_cntr = 0; - tape->eod_frame_addr = 20; + tape->eod_frame_addr = OS_DATA_STARTFRAME1; tape->first_mark_addr = tape->last_mark_addr = -1; stage = __idetape_kmalloc_stage (tape, 0, 0); if (stage == NULL) @@ -5172,8 +5319,8 @@ __idetape_kfree_stage (stage); return 0; } - if (header->major_rev != 1 || (header->minor_rev != 1 && header->minor_rev != 2)) - printk(KERN_INFO "ide-tape: warning: revision %d.%d detected (1.1/1.2 supported)\n", header->major_rev, header->minor_rev); + if (header->major_rev != 1 || (header->minor_rev > OS_ADR_MINREV)) + printk(KERN_INFO "ide-tape: warning: revision %d.%d detected (up to 1.%d supported)\n", header->major_rev, header->minor_rev, OS_ADR_MINREV); 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); @@ -5182,12 +5329,14 @@ 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; + 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); + 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; @@ -5214,18 +5363,14 @@ for (block = 5; block < 10; block++) if (__idetape_analyze_headers(drive, block)) goto ok; -#if 0 - for (block = 0xbae; block < 0xbb8; block++) -#else - for (block = 0xbae; block < 0xbb3; block++) -#endif + for (block = 0xbae; block < 0xbb3; block++) /* 2990 - 2994 */ 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; + if (position < OS_DATA_STARTFRAME1) + position = OS_DATA_STARTFRAME1; idetape_position_tape(drive, position, 0, 0); tape->header_ok = 1; return 1; @@ -5251,6 +5396,7 @@ if (test_and_set_bit (IDETAPE_BUSY, &tape->flags)) return -EBUSY; + MOD_INC_USE_COUNT; if (!tape->onstream) { idetape_read_position(drive); if (!test_bit (IDETAPE_ADDRESS_VALID, &tape->flags)) @@ -5263,18 +5409,22 @@ tape->tape_block_size = tape->stage_size = 32768; tape->raw = 0; } + idetape_onstream_mode_sense_tape_parameter_page(drive, tape->debug_level); } if (idetape_wait_ready(drive, 60 * HZ)) { clear_bit(IDETAPE_BUSY, &tape->flags); printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name); + MOD_DEC_USE_COUNT; return -EBUSY; } idetape_read_position(drive); + MOD_DEC_USE_COUNT; clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags); if (tape->chrdev_direction == idetape_direction_none) { + MOD_INC_USE_COUNT; if (idetape_create_prevent_cmd(drive, &pc, 1)) { - if (!idetape_queue_pc_tail (drive,&pc)) { + if (!idetape_queue_pc_tail (drive, &pc)) { if (tape->door_locked != DOOR_EXPLICITLY_LOCKED) tape->door_locked = DOOR_LOCKED; } @@ -5287,6 +5437,28 @@ return 0; } +static void idetape_write_release (struct inode *inode) +{ + 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_empty_write_pipeline (drive); + 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_write_filemark(drive); + idetape_write_eod(drive); + idetape_flush_tape_buffers (drive); + idetape_write_header(drive, minor >= 128); + idetape_flush_tape_buffers (drive); + + return; +} + /* * Our character device release function. */ @@ -5305,18 +5477,7 @@ #endif /* IDETAPE_DEBUG_LOG */ if (tape->chrdev_direction == idetape_direction_write) { - idetape_empty_write_pipeline (drive); - 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_write_filemark(drive); - idetape_write_eod(drive); - idetape_flush_tape_buffers (drive); - idetape_write_header(drive, minor >= 128); - idetape_flush_tape_buffers (drive); + idetape_write_release(inode); } if (tape->chrdev_direction == idetape_direction_read) { if (minor < 128) @@ -5333,9 +5494,10 @@ if (tape->chrdev_direction == idetape_direction_none) { if (tape->door_locked != DOOR_EXPLICITLY_LOCKED) { if (idetape_create_prevent_cmd(drive, &pc, 0)) - if (!idetape_queue_pc_tail (drive,&pc)) + if (!idetape_queue_pc_tail (drive, &pc)) tape->door_locked = DOOR_UNLOCKED; } + MOD_DEC_USE_COUNT; } clear_bit (IDETAPE_BUSY, &tape->flags); unlock_kernel(); @@ -5491,7 +5653,7 @@ pc.buffer[4 + 5] = vendor[3]; pc.buffer[4 + 6] = 0; pc.buffer[4 + 7] = 0; - if (idetape_queue_pc_tail (drive,&pc)) + if (idetape_queue_pc_tail (drive, &pc)) printk (KERN_ERR "ide-tape: Couldn't set vendor name to %s\n", vendor); } @@ -5513,7 +5675,7 @@ pc.buffer[4 + 1] = 2; pc.buffer[4 + 2] = 4; pc.buffer[4 + 3] = retries; - if (idetape_queue_pc_tail (drive,&pc)) + if (idetape_queue_pc_tail (drive, &pc)) printk (KERN_ERR "ide-tape: Couldn't set retries to %d\n", retries); } #endif @@ -5530,8 +5692,8 @@ /* * 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)) + 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); @@ -5552,7 +5714,7 @@ bs->record32 = 0; bs->record32_5 = 1; idetape_create_mode_select_cmd(&pc, sizeof(*header) + sizeof(*bs)); - if (idetape_queue_pc_tail (drive,&pc)) + if (idetape_queue_pc_tail (drive, &pc)) printk (KERN_ERR "ide-tape: Couldn't set tape block size mode page\n"); #if ONSTREAM_DEBUG @@ -5575,7 +5737,7 @@ idetape_inquiry_result_t *inquiry; idetape_create_inquiry_cmd(&pc); - if (idetape_queue_pc_tail (drive,&pc)) { + if (idetape_queue_pc_tail (drive, &pc)) { printk (KERN_ERR "ide-tape: %s: can't get INQUIRY results\n", tape->name); return; } @@ -5618,6 +5780,34 @@ } /* + * idetape_get_mode_sense_parameters asks the tape about its various + * parameters. This may work for other drives to??? + */ +static void idetape_onstream_mode_sense_tape_parameter_page(ide_drive_t *drive, int debug) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + idetape_mode_parameter_header_t *header; + onstream_tape_paramtr_page_t *prm; + + idetape_create_mode_sense_cmd (&pc, IDETAPE_PARAMTR_PAGE); + if (idetape_queue_pc_tail (drive, &pc)) { + printk (KERN_ERR "ide-tape: Can't get tape parameters page - probably no tape inserted in onstream drive\n"); + return; + } + header = (idetape_mode_parameter_header_t *) pc.buffer; + prm = (onstream_tape_paramtr_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl); + + tape->capacity = ntohs(prm->segtrk) * ntohs(prm->trks); + if (debug) { + printk (KERN_INFO "ide-tape: %s <-> %s: Tape length %dMB (%d frames/track, %d tracks = %d blocks, density: %dKbpi)\n", + drive->name, tape->name, tape->capacity/32, ntohs(prm->segtrk), ntohs(prm->trks), tape->capacity, prm->density); + } + + return; +} + +/* * 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. @@ -5629,8 +5819,8 @@ idetape_mode_parameter_header_t *header; idetape_capabilities_page_t *capabilities; - idetape_create_mode_sense_cmd (&pc,IDETAPE_CAPABILITIES_PAGE); - if (idetape_queue_pc_tail (drive,&pc)) { + idetape_create_mode_sense_cmd (&pc, IDETAPE_CAPABILITIES_PAGE); + if (idetape_queue_pc_tail (drive, &pc)) { printk (KERN_ERR "ide-tape: Can't get tape parameters - assuming some default values\n"); tape->tape_block_size = 512; tape->capabilities.ctl = 52; tape->capabilities.speed = 450; tape->capabilities.buffer_size = 6 * 52; @@ -5721,6 +5911,9 @@ 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); + ide_add_setting(drive, "capacity", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->capacity, NULL); + ide_add_setting(drive, "first_frame", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->first_frame_position, NULL); + ide_add_setting(drive, "logical_blk", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->logical_blk_num, NULL); } } @@ -5747,7 +5940,7 @@ 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")) + if (strstr(drive->id->model, "OnStream DI-")) tape->onstream = 1; drive->dsc_overlap = 1; #ifdef CONFIG_BLK_DEV_IDEPCI @@ -5778,8 +5971,10 @@ idetape_get_inquiry_results(drive); idetape_get_mode_sense_results(drive); - if (tape->onstream) - idetape_configure_onstream(drive); + if (tape->onstream) { + idetape_onstream_mode_sense_tape_parameter_page(drive, 1); + idetape_configure_onstream(drive); + } tape->user_bs_factor = 1; tape->stage_size = tape->capabilities.ctl * tape->tape_block_size; @@ -5867,8 +6062,8 @@ char *out = page; int len; - len = sprintf(out,"%s\n", tape->name); - PROC_IDE_READ_RETURN(page,start,off,count,eof,len); + len = sprintf(out, "%s\n", tape->name); + PROC_IDE_READ_RETURN(page, start, off, count, eof, len); } static ide_proc_entry_t idetape_proc[] = { @@ -5962,7 +6157,7 @@ continue; } if (drive->scsi) { - if (strstr(drive->id->model, "OnStream DI-30")) { + if (strstr(drive->id->model, "OnStream DI-")) { 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); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/ide.c linux.ac/drivers/ide/ide.c --- linux.vanilla/drivers/ide/ide.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/ide/ide.c Tue May 8 18:25:00 2001 @@ -376,7 +376,19 @@ */ void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount) { - byte io_32bit = drive->io_32bit; + byte io_32bit; + + /* first check if this controller has defined a special function + * for handling polled ide transfers + */ + + if(HWIF(drive)->ideproc) { + HWIF(drive)->ideproc(ideproc_ide_input_data, + drive, buffer, wcount); + return; + } + + io_32bit = drive->io_32bit; if (io_32bit) { #if SUPPORT_VLB_SYNC @@ -409,7 +421,15 @@ */ void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) { - byte io_32bit = drive->io_32bit; + byte io_32bit; + + if(HWIF(drive)->ideproc) { + HWIF(drive)->ideproc(ideproc_ide_output_data, + drive, buffer, wcount); + return; + } + + io_32bit = drive->io_32bit; if (io_32bit) { #if SUPPORT_VLB_SYNC @@ -446,6 +466,12 @@ */ void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) { + if(HWIF(drive)->ideproc) { + HWIF(drive)->ideproc(ideproc_atapi_input_bytes, + drive, buffer, bytecount); + return; + } + ++bytecount; #if defined(CONFIG_ATARI) || defined(CONFIG_Q40) if (MACH_IS_ATARI || MACH_IS_Q40) { @@ -461,6 +487,12 @@ void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) { + if(HWIF(drive)->ideproc) { + HWIF(drive)->ideproc(ideproc_atapi_output_bytes, + drive, buffer, bytecount); + return; + } + ++bytecount; #if defined(CONFIG_ATARI) || defined(CONFIG_Q40) if (MACH_IS_ATARI || MACH_IS_Q40) { @@ -2088,6 +2120,7 @@ hwif->maskproc = old_hwif.maskproc; hwif->quirkproc = old_hwif.quirkproc; hwif->rwproc = old_hwif.rwproc; + hwif->ideproc = old_hwif.ideproc; hwif->dmaproc = old_hwif.dmaproc; hwif->dma_base = old_hwif.dma_base; hwif->dma_extra = old_hwif.dma_extra; @@ -3195,6 +3228,12 @@ } #endif /* CONFIG_PCI */ +#ifdef CONFIG_ETRAX_IDE + { + extern void init_e100_ide(void); + init_e100_ide(); + } +#endif /* CONFIG_ETRAX_IDE */ #ifdef CONFIG_BLK_DEV_CMD640 { extern void ide_probe_for_cmd640x(void); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/it8172.c linux.ac/drivers/ide/it8172.c --- linux.vanilla/drivers/ide/it8172.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/ide/it8172.c Tue Apr 3 17:54:41 2001 @@ -0,0 +1,283 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * IT8172 IDE controller support + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or support@mvista.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +/* + * Prototypes + */ +static void it8172_tune_drive (ide_drive_t *drive, byte pio); +#if defined(CONFIG_BLK_DEV_IDEDMA) && defined(CONFIG_IT8172_TUNING) +static byte it8172_dma_2_pio (byte xfer_rate); +static int it8172_tune_chipset (ide_drive_t *drive, byte speed); +static int it8172_config_drive_for_dma (ide_drive_t *drive); +static int it8172_dmaproc(ide_dma_action_t func, ide_drive_t *drive); +#endif +unsigned int __init pci_init_it8172 (struct pci_dev *dev, const char *name); +void __init ide_init_it8172 (ide_hwif_t *hwif); + + +/* + * Based on settings done by AMI BIOS + * (might be usefull if drive is not registered in CMOS for any reason). + */ +static void it8172_tune_drive (ide_drive_t *drive, byte pio) +{ + unsigned long flags; + u16 master_data; + byte slave_data; + int is_slave = (&HWIF(drive)->drives[1] == drive); + int master_port = HWIF(drive)->index ? 0x42 : 0x40; + int slave_port = 0x44; + /* ISP RTC */ + byte timings[][2] = { { 0, 0 }, + { 0, 0 }, + { 1, 0 }, + { 2, 1 }, + { 2, 3 }, }; + + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); + pci_read_config_word(HWIF(drive)->pci_dev, master_port, &master_data); + if (is_slave) { + master_data = master_data | 0x4000; + if (pio > 1) + /* enable PPE, IE and TIME */ + master_data = master_data | 0x0070; + pci_read_config_byte(HWIF(drive)->pci_dev, slave_port, &slave_data); + slave_data = slave_data & (HWIF(drive)->index ? 0x0f : 0xf0); + slave_data = slave_data | + ((timings[pio][0] << 2) | (timings[pio][1] + << (HWIF(drive)->index ? 4 : 0))); + } else { + master_data = master_data & 0xccf8; + if (pio > 1) + /* enable PPE, IE and TIME */ + master_data = master_data | 0x0007; + master_data = master_data | (timings[pio][0] << 12) | + (timings[pio][1] << 8); + } + save_flags(flags); + cli(); + pci_write_config_word(HWIF(drive)->pci_dev, master_port, master_data); + if (is_slave) + pci_write_config_byte(HWIF(drive)->pci_dev, slave_port, slave_data); + restore_flags(flags); +} + +#if defined(CONFIG_BLK_DEV_IDEDMA) && defined(CONFIG_IT8172_TUNING) +/* + * + */ +static byte it8172_dma_2_pio (byte xfer_rate) +{ + switch(xfer_rate) { + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + case XFER_MW_DMA_2: + case XFER_PIO_4: + return 4; + case XFER_MW_DMA_1: + case XFER_PIO_3: + return 3; + case XFER_SW_DMA_2: + case XFER_PIO_2: + return 2; + case XFER_MW_DMA_0: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + case XFER_PIO_1: + case XFER_PIO_0: + case XFER_PIO_SLOW: + default: + return 0; + } +} + +static int it8172_tune_chipset (ide_drive_t *drive, byte speed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + int a_speed = 3 << (drive->dn * 4); + int u_flag = 1 << drive->dn; + int u_speed = 0; + int err = 0; + byte reg48, reg4a; + + pci_read_config_byte(dev, 0x48, ®48); + pci_read_config_byte(dev, 0x4a, ®4a); + + switch(speed) { + case XFER_UDMA_4: + case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break; + case XFER_UDMA_5: + case XFER_UDMA_3: + case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break; + case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_SW_DMA_2: break; + default: return -1; + } + + if (speed >= XFER_UDMA_0) { + if (!(reg48 & u_flag)) + pci_write_config_byte(dev, 0x48, reg48|u_flag); + if (!(reg4a & u_speed)) { + pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed); + pci_write_config_byte(dev, 0x4a, reg4a|u_speed); + } + } + if (speed < XFER_UDMA_0) { + if (reg48 & u_flag) + pci_write_config_byte(dev, 0x48, reg48 & ~u_flag); + if (reg4a & a_speed) + pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed); + } + + it8172_tune_drive(drive, it8172_dma_2_pio(speed)); + +#if IT8172_DEBUG + printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive->dn); +#endif + if (!drive->init_speed) + drive->init_speed = speed; + err = ide_config_drive_speed(drive, speed); + drive->current_speed = speed; + return err; +} + +static int it8172_config_drive_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte speed; + + if (id->dma_ultra & 0x0010) { + speed = XFER_UDMA_2; + } else if (id->dma_ultra & 0x0008) { + speed = XFER_UDMA_1; + } else if (id->dma_ultra & 0x0004) { + speed = XFER_UDMA_2; + } else if (id->dma_ultra & 0x0002) { + speed = XFER_UDMA_1; + } else if (id->dma_ultra & 0x0001) { + speed = XFER_UDMA_0; + } else if (id->dma_mword & 0x0004) { + speed = XFER_MW_DMA_2; + } else if (id->dma_mword & 0x0002) { + speed = XFER_MW_DMA_1; + } else if (id->dma_1word & 0x0004) { + speed = XFER_SW_DMA_2; + } else { + speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL); + } + + (void) it8172_tune_chipset(drive, speed); + + return ((int)((id->dma_ultra >> 11) & 7) ? 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 : + ide_dma_off_quietly); +} + +static int it8172_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return ide_dmaproc((ide_dma_action_t)it8172_config_drive_for_dma(drive), + drive); + default : + break; + } + /* Other cases are done by generic IDE-DMA code. */ + return ide_dmaproc(func, drive); +} +#endif /* defined(CONFIG_BLK_DEV_IDEDMA) && (CONFIG_IT8172_TUNING) */ + +unsigned int __init pci_init_it8172 (struct pci_dev *dev, const char *name) +{ + unsigned char progif; + + /* + * Place both IDE interfaces into PCI "native" mode + */ + (void)pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); + (void)pci_write_config_byte(dev, PCI_CLASS_PROG, progif | 0x05); + + return IT8172_IDE_IRQ; +} + + +void __init ide_init_it8172 (ide_hwif_t *hwif) +{ + struct pci_dev* dev = hwif->pci_dev; + unsigned long cmdBase, ctrlBase; + + hwif->tuneproc = &it8172_tune_drive; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + + if (!hwif->dma_base) + return; + +#ifndef CONFIG_BLK_DEV_IDEDMA + hwif->autodma = 0; +#else /* CONFIG_BLK_DEV_IDEDMA */ +#ifdef CONFIG_IT8172_TUNING + hwif->autodma = 1; + hwif->dmaproc = &it8172_dmaproc; + hwif->speedproc = &it8172_tune_chipset; +#endif /* CONFIG_IT8172_TUNING */ +#endif /* !CONFIG_BLK_DEV_IDEDMA */ + + cmdBase = dev->resource[0].start; + ctrlBase = dev->resource[1].start; + + ide_init_hwif_ports(&hwif->hw, cmdBase, ctrlBase | 2, NULL); + memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); + hwif->noprobe = 0; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/piix.c linux.ac/drivers/ide/piix.c --- linux.vanilla/drivers/ide/piix.c Tue Apr 3 17:32:02 2001 +++ linux.ac/drivers/ide/piix.c Sun May 13 21:24:23 2001 @@ -108,7 +108,8 @@ c1 = inb_p((unsigned short)bibma + 0x0a); switch(bmide_dev->device) { - case PCI_DEVICE_ID_INTEL_82820FW_5: + case PCI_DEVICE_ID_INTEL_82801BA_8: + case PCI_DEVICE_ID_INTEL_82801BA_9: p += sprintf(p, "\n Intel PIIX4 Ultra 100 Chipset.\n"); break; case PCI_DEVICE_ID_INTEL_82372FB_1: @@ -358,7 +359,8 @@ byte speed; byte udma_66 = eighty_ninty_three(drive); - int ultra100 = ((dev->device == PCI_DEVICE_ID_INTEL_82820FW_5)) ? 1 : 0; + int ultra100 = ((dev->device == PCI_DEVICE_ID_INTEL_82801BA_8) || + (dev->device == PCI_DEVICE_ID_INTEL_82801BA_9)) ? 1 : 0; int ultra66 = ((ultra100) || (dev->device == PCI_DEVICE_ID_INTEL_82801AA_1) || (dev->device == PCI_DEVICE_ID_INTEL_82372FB_1)) ? 1 : 0; @@ -514,7 +516,8 @@ hwif->autodma = 0; #else /* CONFIG_BLK_DEV_IDEDMA */ #ifdef CONFIG_PIIX_TUNING - hwif->autodma = 1; + if (!noautodma) + hwif->autodma = 1; hwif->dmaproc = &piix_dmaproc; hwif->speedproc = &piix_tune_chipset; #endif /* CONFIG_PIIX_TUNING */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ide/via82cxxx.c linux.ac/drivers/ide/via82cxxx.c --- linux.vanilla/drivers/ide/via82cxxx.c Sat Feb 3 19:27:43 2001 +++ linux.ac/drivers/ide/via82cxxx.c Tue Apr 3 17:54:42 2001 @@ -1,5 +1,5 @@ /* - * $Id: via82cxxx.c,v 3.20 2001/01/27 10:13:60 vojtech Exp $ + * $Id: via82cxxx.c,v 3.23 2001/03/09 09:30:00 vojtech Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik * @@ -32,14 +32,9 @@ * PIO 0-5, MWDMA 0-2, SWDMA 0-2 and UDMA 0-5 * * (this includes UDMA33, 66 and 100) modes. UDMA66 and higher modes are - * autodetected only in case the BIOS has detected a 80 wire cable. To ignore - * the BIOS data, use 'ide0=ata66' or 'ide1=ata66' on the kernel command line. - * - * For correct operation it's needed to tell the driver the speed of the PCI - * bus. The default, and most common value is 33 MHz. Most likely, your system - * is using this value and you don't need to use any command line options. If - * you run your PCI bus speed at 25, 30, 37, 40 or 42 MHz, use the 'idebus=xx' - * option of the IDE driver. Note that this has nothing to do with UDMA66. + * autoenabled only in case the BIOS has detected a 80 wire cable. To ignore + * the BIOS data and assume the cable is present, use 'ide0=ata66' or + * 'ide1=ata66' on the kernel command line. */ /* @@ -84,15 +79,16 @@ #define VIA_ADDRESS_SETUP 0x4c #define VIA_UDMA_TIMING 0x50 -#define VIA_UDMA 0x07 -#define VIA_UDMA_NONE 0x00 -#define VIA_UDMA_33 0x01 -#define VIA_UDMA_66 0x02 -#define VIA_UDMA_100 0x03 -#define VIA_BAD_PREQ 0x10 -#define VIA_BAD_CLK66 0x20 -#define VIA_SET_FIFO 0x40 -#define VIA_SET_THRESH 0x80 +#define VIA_UDMA 0x007 +#define VIA_UDMA_NONE 0x000 +#define VIA_UDMA_33 0x001 +#define VIA_UDMA_66 0x002 +#define VIA_UDMA_100 0x003 +#define VIA_BAD_PREQ 0x010 /* Crashes if PREQ# till DDACK# set */ +#define VIA_BAD_CLK66 0x020 /* 66 MHz clock doesn't work correctly */ +#define VIA_SET_FIFO 0x040 /* Needs to have FIFO split set */ +#define VIA_SET_THRESH 0x080 /* Needs to have FIFO thresholds set */ +#define VIA_BAD_PIO 0x100 /* Always uses 26 PCICLK/xfer regardles of PIO mode */ /* * VIA SouthBridge chips. @@ -103,13 +99,15 @@ unsigned short id; unsigned char rev_min; unsigned char rev_max; - unsigned char flags; + unsigned short flags; } via_isa_bridges[] = { +#ifdef VIA_NEW_BRIDGES_TESTED { "vt8233", PCI_DEVICE_ID_VIA_8233_0, 0x00, 0x2f, VIA_UDMA_100 }, { "vt8231", PCI_DEVICE_ID_VIA_8231, 0x00, 0x2f, VIA_UDMA_66 }, - { "vt82c686b", PCI_DEVICE_ID_VIA_82C686, 0x40, 0x4f, VIA_UDMA_100 }, +#endif + { "vt82c686b", PCI_DEVICE_ID_VIA_82C686, 0x40, 0x4f, VIA_UDMA_100 | VIA_BAD_PIO }, { "vt82c686a", PCI_DEVICE_ID_VIA_82C686, 0x10, 0x2f, VIA_UDMA_66 }, - { "vt82c686", PCI_DEVICE_ID_VIA_82C686, 0x00, 0x0f, VIA_UDMA_66 }, + { "vt82c686", PCI_DEVICE_ID_VIA_82C686, 0x00, 0x0f, VIA_UDMA_33 | VIA_BAD_CLK66 }, { "vt82c596b", PCI_DEVICE_ID_VIA_82C596, 0x10, 0x2f, VIA_UDMA_66 }, { "vt82c596a", PCI_DEVICE_ID_VIA_82C596, 0x00, 0x0f, VIA_UDMA_33 | VIA_BAD_CLK66 }, { "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x40, 0x4f, VIA_UDMA_33 | VIA_SET_FIFO | VIA_BAD_PREQ }, @@ -123,6 +121,7 @@ static unsigned char via_enabled; static unsigned int via_80w; static unsigned int via_clock; +static char *via_dma[] = { "MWDMA16", "UDMA33", "UDMA66", "UDMA100" }; /* * VIA /proc entry. @@ -145,8 +144,8 @@ static int via_get_info(char *buffer, char **addr, off_t offset, int count) { - short speed[4], cycle[4], setup[4], active[4], - recover[4], uen[4], udma[4], active8b[4], recover8b[4]; + short speed[4], cycle[4], setup[4], active[4], recover[4], den[4], + uen[4], udma[4], umul[4], active8b[4], recover8b[4]; struct pci_dev *dev = bmide_dev; unsigned int v, u, i; unsigned short c, w; @@ -155,12 +154,13 @@ via_print("----------VIA BusMastering IDE Configuration----------------"); - via_print("Driver Version: 3.20"); + via_print("Driver Version: 3.23"); via_print("South Bridge: VIA %s", via_config->name); pci_read_config_byte(isa_dev, PCI_REVISION_ID, &t); pci_read_config_byte(dev, PCI_REVISION_ID, &x); via_print("Revision: ISA %#x IDE %#x", t, x); + via_print("Highest DMA rate: %s", via_dma[via_config->flags & VIA_UDMA]); via_print("BM-DMA base: %#x", via_base); via_print("PCI clock: %dMHz", via_clock); @@ -200,28 +200,43 @@ else u = 0; for (i = 0; i < 4; i++) { + setup[i] = ((t >> ((3 - i) << 1)) & 0x3) + 1; recover8b[i] = ((w >> ((1 - (i >> 1)) << 3)) & 0xf) + 1; active8b[i] = ((w >> (((1 - (i >> 1)) << 3) + 4)) & 0xf) + 1; active[i] = ((v >> (((3 - i) << 3) + 4)) & 0xf) + 1; recover[i] = ((v >> ((3 - i) << 3)) & 0xf) + 1; udma[i] = ((u >> ((3 - i) << 3)) & 0x7) + 2; + umul[i] = ((u >> (((3 - i) & 2) << 3)) & 0x8) ? 1 : 2; + uen[i] = ((u >> ((3 - i) << 3)) & 0x20); + den[i] = (c & ((i & 1) ? 0x40 : 0x20) << ((i & 2) << 2)); + + speed[i] = 20 * via_clock / (active[i] + recover[i]); + cycle[i] = 1000 / via_clock * (active[i] + recover[i]); - if ((via_config->flags & VIA_UDMA) == VIA_UDMA_100) { - speed[i] = 2000 / udma[i]; - cycle[i] = 10 * udma[i]; + if (!uen[i] || !den[i]) continue; - } - uen[i] = (u >> ((3 - i) << 3)) & 0x20; - udma[i] *= ((u >> (((3 - i) & 2) << 3)) & 0x8) ? 1 : 2; + switch (via_config->flags & VIA_UDMA) { + + case VIA_UDMA_100: + speed[i] = 2000 / udma[i]; + cycle[i] = 10 * udma[i]; + break; + + case VIA_UDMA_66: + speed[i] = 40 * via_clock / (udma[i] * umul[i]); + cycle[i] = 500 / via_clock * (udma[i] * umul[i]); + break; - speed[i] = 40 * via_clock / (uen[i] ? udma[i] : (active[i] + recover[i]) * 2); - cycle[i] = 1000 / via_clock * (uen[i] ? udma[i] : (active[i] + recover[i]) * 2) / 2; + case VIA_UDMA_33: + speed[i] = 20 * via_clock / udma[i]; + cycle[i] = 1000 / via_clock * udma[i]; + break; + } } - via_print_drive("Transfer Mode: ", "%10s", - (c & ((i & 1) ? 0x40 : 0x20) << ((i & 2) << 2)) ? (uen[i] ? "UDMA" : "DMA") : "PIO"); + via_print_drive("Transfer Mode: ", "%10s", den[i] ? (uen[i] ? "UDMA" : "DMA") : "PIO"); via_print_drive("Address Setup: ", "%8dns", (1000 / via_clock) * setup[i]); via_print_drive("Cmd Active: ", "%8dns", (1000 / via_clock) * active8b[i]); @@ -342,12 +357,13 @@ short speed = ide_find_best_mode(drive, XFER_PIO | XFER_EPIO | XFER_SWDMA | XFER_MWDMA | (via_config->flags & VIA_UDMA ? XFER_UDMA : 0) | - (w80 && (via_config->flags & VIA_UDMA) == VIA_UDMA_66 ? XFER_UDMA_66 : 0) | - (w80 && (via_config->flags & VIA_UDMA) == VIA_UDMA_100 ? XFER_UDMA_100 : 0)); - - func = ((speed & XFER_MODE) != XFER_PIO) ? ide_dma_on : ide_dma_off_quietly; + (w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_66 ? XFER_UDMA_66 : 0) | + (w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_100 ? XFER_UDMA_100 : 0)); via_set_drive(drive, speed); + + func = (HWIF(drive)->autodma && (speed & XFER_MODE) != XFER_PIO) + ? ide_dma_on : ide_dma_off_quietly; } return ide_dmaproc(func, drive); @@ -459,11 +475,7 @@ pci_read_config_byte(isa, PCI_REVISION_ID, &t); printk(KERN_INFO "VP_IDE: VIA %s (rev %02x) IDE %s controller on pci%s\n", - via_config->name, t, - (via_config->flags & VIA_UDMA) == VIA_UDMA_100 ? "UDMA100" : - (via_config->flags & VIA_UDMA) == VIA_UDMA_66 ? "UDMA66" : - (via_config->flags & VIA_UDMA) == VIA_UDMA_33 ? "UDMA33" : "MWDMA16", - dev->slot_name); + via_config->name, t, via_dma[via_config->flags & VIA_UDMA], dev->slot_name); /* * Setup /proc/ide/via entry. @@ -471,11 +483,11 @@ #ifdef CONFIG_PROC_FS if (!via_proc) { - via_proc = 1; via_base = pci_resource_start(dev, 4); bmide_dev = dev; isa_dev = isa; via_display_info = &via_get_info; + via_proc = 1; } #endif @@ -506,7 +518,8 @@ if (hwif->dma_base) { hwif->dmaproc = &via82cxxx_dmaproc; #ifdef CONFIG_IDEDMA_AUTO - hwif->autodma = 1; + if (!noautodma) + hwif->autodma = 1; #endif } #endif /* CONFIG_BLK_DEV_IDEDMA */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ieee1394/pcilynx.c linux.ac/drivers/ieee1394/pcilynx.c --- linux.vanilla/drivers/ieee1394/pcilynx.c Tue Apr 3 17:32:02 2001 +++ linux.ac/drivers/ieee1394/pcilynx.c Tue Apr 3 17:54:42 2001 @@ -1355,10 +1355,10 @@ lynx->id = num_of_cards-1; lynx->dev = dev; - lynx->lock = SPIN_LOCK_UNLOCKED; - lynx->phy_reg_lock = SPIN_LOCK_UNLOCKED; + lynx->lock = SPIN_LOCK_UNLOCKED; + lynx->phy_reg_lock = SPIN_LOCK_UNLOCKED; - if (pci_set_dma_mask(dev, 0xffffffff)) { + if (!pci_dma_supported(dev, 0xffffffff)) { FAIL("DMA address limits not supported for PCILynx hardware %d", lynx->id); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/ieee1394/video1394.c linux.ac/drivers/ieee1394/video1394.c --- linux.vanilla/drivers/ieee1394/video1394.c Sat May 26 16:53:04 2001 +++ linux.ac/drivers/ieee1394/video1394.c Sat May 26 17:04:25 2001 @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -130,8 +131,10 @@ quadlet_t isoXmitIntEvent); static struct video_card video_cards[MAX_OHCI1394_CARDS]; -static int num_of_video_cards = 0; -static struct video_template video_tmpl = { irq_handler }; +static int num_of_video_cards; +static struct video_template video_tmpl = { + irq_handler: irq_handler, +}; /* Taken from bttv.c */ /*******************************/ @@ -308,7 +311,6 @@ } memset(d, 0, sizeof(struct dma_iso_ctx)); - d->ohci = (void *)ohci; d->ctx = ctx; d->channel = channel; @@ -963,8 +965,10 @@ if (wait_event_interruptible(d->waitq, d->buffer_status[v.buffer] == VIDEO1394_BUFFER_READY) - == -ERESTARTSYS) + == -ERESTARTSYS) { + spin_unlock_irqrestore(&d->lock,flags); return -EINTR; + } #endif d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; break; @@ -1296,32 +1300,17 @@ } } -#ifdef MODULE - /* EXPORT_NO_SYMBOLS; */ MODULE_AUTHOR("Sebastien Rougeaux "); MODULE_DESCRIPTION("driver for digital video on OHCI board"); MODULE_SUPPORTED_DEVICE("video1394"); -void cleanup_module(void) -{ - int i; - unregister_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME); - - for (i=0; iprivate_data; + struct input_event event; + int retval = 0; + + while (retval < count) { + + if (copy_from_user(&event, buffer + retval, sizeof(struct input_event))) + return -EFAULT; + input_event(list->evdev->handle.dev, event.type, event.code, event.value); + retval += sizeof(struct input_event); + } + + return retval; } static ssize_t evdev_read(struct file * file, char * buffer, size_t count, loff_t *ppos) @@ -310,7 +322,7 @@ evdev->devfs = input_register_minor("event%d", minor, EVDEV_MINOR_BASE); - printk(KERN_INFO "event%d: Event device for input%d\n", minor, dev->number); +// printk(KERN_INFO "event%d: Event device for input%d\n", minor, dev->number); return &evdev->handle; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/input/keybdev.c linux.ac/drivers/input/keybdev.c --- linux.vanilla/drivers/input/keybdev.c Mon Apr 30 15:13:15 2001 +++ linux.ac/drivers/input/keybdev.c Tue May 1 08:20:14 2001 @@ -197,14 +197,14 @@ input_open_device(handle); - printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number); +// printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number); return handle; } static void keybdev_disconnect(struct input_handle *handle) { - printk(KERN_INFO "keybdev.c: Removing keyboard: input%d\n", handle->dev->number); +// printk(KERN_INFO "keybdev.c: Removing keyboard: input%d\n", handle->dev->number); input_close_device(handle); kfree(handle); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/input/mousedev.c linux.ac/drivers/input/mousedev.c --- linux.vanilla/drivers/input/mousedev.c Tue Apr 3 17:32:02 2001 +++ linux.ac/drivers/input/mousedev.c Tue Apr 3 17:54:42 2001 @@ -1,9 +1,9 @@ /* - * $Id: mousedev.c,v 1.15 2000/08/14 21:05:26 vojtech Exp $ + * $Id: mousedev.c,v 1.24 2000/11/15 10:57:45 vojtech Exp $ * * Copyright (c) 1999-2000 Vojtech Pavlik * - * Input driver to PS/2 or ImPS/2 device driver module. + * Input driver to ImExPS/2 device driver module. * * Sponsored by SuSE */ @@ -66,20 +66,22 @@ signed char ps2[6]; unsigned long buttons; unsigned char ready, buffer, bufsiz; - unsigned char mode, genseq, impseq; + unsigned char mode, imexseq, impsseq; }; -#define MOUSEDEV_GENIUS_LEN 5 -#define MOUSEDEV_IMPS_LEN 6 +#define MOUSEDEV_SEQ_LEN 6 -static unsigned char mousedev_genius_seq[] = { 0xe8, 3, 0xe6, 0xe6, 0xe6 }; static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 }; +static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; static struct input_handler mousedev_handler; static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; static struct mousedev mousedev_mix; +static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X; +static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y; + static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct mousedev *mousedevs[3] = { handle->private, &mousedev_mix, NULL }; @@ -99,12 +101,12 @@ switch (code) { case ABS_X: size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X]; - list->dx += (value * CONFIG_INPUT_MOUSEDEV_SCREEN_X - list->oldx) / size; + list->dx += (value * xres - list->oldx) / size; list->oldx += list->dx * size; break; case ABS_Y: size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y]; - list->dy -= (value * CONFIG_INPUT_MOUSEDEV_SCREEN_Y - list->oldy) / size; + list->dy -= (value * yres - list->oldy) / size; list->oldy -= list->dy * size; break; } @@ -124,12 +126,12 @@ case BTN_TOUCH: case BTN_LEFT: index = 0; break; case BTN_4: - case BTN_EXTRA: if (list->mode > 1) { index = 4; break; } + case BTN_EXTRA: if (list->mode == 2) { index = 4; break; } case BTN_STYLUS: case BTN_1: case BTN_RIGHT: index = 1; break; case BTN_3: - case BTN_SIDE: if (list->mode > 1) { index = 3; break; } + case BTN_SIDE: if (list->mode == 2) { index = 3; break; } case BTN_2: case BTN_STYLUS2: case BTN_MIDDLE: index = 2; break; @@ -257,14 +259,19 @@ list->dy -= list->ps2[off + 2]; list->bufsiz = off + 3; - if (list->mode > 1) - list->ps2[off] |= ((list->buttons & 0x18) << 3); + if (list->mode == 2) { + list->ps2[off + 3] = (list->dz > 7 ? 7 : (list->dz < -7 ? -7 : list->dz)); + list->dz -= list->ps2[off + 3]; + list->ps2[off + 3] = (list->ps2[off + 3] & 0x0f) | ((list->buttons & 0x18) << 1); + list->bufsiz++; + } - if (list->mode) { + if (list->mode == 1) { list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz)); - list->bufsiz++; list->dz -= list->ps2[off + 3]; + list->bufsiz++; } + if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0; list->buffer = list->bufsiz; } @@ -278,27 +285,26 @@ for (i = 0; i < count; i++) { - if (get_user(c, &buffer[i])) + if (get_user(c, buffer + i)) return -EFAULT; - if (c == mousedev_genius_seq[list->genseq]) { - if (++list->genseq == MOUSEDEV_GENIUS_LEN) { - list->genseq = 0; - list->ready = 1; + if (c == mousedev_imex_seq[list->imexseq]) { + if (++list->imexseq == MOUSEDEV_SEQ_LEN) { + list->imexseq = 0; list->mode = 2; } - } else list->genseq = 0; + } else list->imexseq = 0; - if (c == mousedev_imps_seq[list->impseq]) { - if (++list->impseq == MOUSEDEV_IMPS_LEN) { - list->impseq = 0; - list->ready = 1; + if (c == mousedev_imps_seq[list->impsseq]) { + if (++list->impsseq == MOUSEDEV_SEQ_LEN) { + list->impsseq = 0; list->mode = 1; } - } else list->impseq = 0; + } else list->impsseq = 0; list->ps2[0] = 0xfa; list->bufsiz = 1; + list->ready = 1; switch (c) { @@ -307,16 +313,16 @@ break; case 0xf2: /* Get ID */ - list->ps2[1] = (list->mode == 1) ? 3 : 0; + switch (list->mode) { + case 0: list->ps2[1] = 0; + case 1: list->ps2[1] = 3; + case 2: list->ps2[1] = 4; + } list->bufsiz = 2; break; case 0xe9: /* Get info */ - if (list->mode == 2) { - list->ps2[1] = 0x00; list->ps2[2] = 0x33; list->ps2[3] = 0x55; - } else { - list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200; - } + list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200; list->bufsiz = 4; break; } @@ -434,7 +440,7 @@ if (mousedev_mix.open) input_open_device(&mousedev->handle); - printk(KERN_INFO "mouse%d: PS/2 mouse device for input%d\n", minor, dev->number); +// printk(KERN_INFO "mouse%d: PS/2 mouse device for input%d\n", minor, dev->number); return &mousedev->handle; } @@ -491,3 +497,7 @@ MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Input driver to PS/2 or ImPS/2 device driver"); +MODULE_PARM(xres, "i"); +MODULE_PARM_DESC(xres, "Horizontal screen resolution"); +MODULE_PARM(yres, "i"); +MODULE_PARM_DESC(yres, "Vertical screen resolution"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/isdn/isdn_ppp.c linux.ac/drivers/isdn/isdn_ppp.c --- linux.vanilla/drivers/isdn/isdn_ppp.c Sat May 26 16:53:05 2001 +++ linux.ac/drivers/isdn/isdn_ppp.c Thu May 24 23:42:34 2001 @@ -785,7 +785,10 @@ } skb_reserve(skb, hl); if (copy_from_user(skb_put(skb, count), buf, count)) + { + kfree_skb(skb); return -EFAULT; + } if (is->debug & 0x40) { printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,lp->ppp_slot); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/macintosh/mac_hid.c linux.ac/drivers/macintosh/mac_hid.c --- linux.vanilla/drivers/macintosh/mac_hid.c Sun Sep 17 17:48:05 2000 +++ linux.ac/drivers/macintosh/mac_hid.c Tue Apr 3 17:54:45 2001 @@ -473,9 +473,19 @@ #ifdef CONFIG_MAC_ADBKEYCODES memcpy(pc_key_maps_save, key_maps, sizeof(key_maps)); - if (!keyboard_sends_linux_keycodes) + if (!keyboard_sends_linux_keycodes) { +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = mac_hid_kbd_sysrq_xlate; + SYSRQ_KEY = 0x69; +#endif memcpy(key_maps, mac_key_maps_save, sizeof(key_maps)); + } else { +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x54; #endif + } +#endif /* CONFIG_MAC_ADBKEYCODES */ #ifdef CONFIG_MAC_EMUMOUSEBTN emumousebtn_input_register(); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/macintosh/mac_keyb.c linux.ac/drivers/macintosh/mac_keyb.c --- linux.vanilla/drivers/macintosh/mac_keyb.c Mon Dec 4 01:48:19 2000 +++ linux.ac/drivers/macintosh/mac_keyb.c Tue Apr 3 17:54:45 2001 @@ -305,7 +305,7 @@ return 1; } -int mackbd_unexpected_up(unsigned char keycode) +char mackbd_unexpected_up(unsigned char keycode) { return 0x80; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/Makefile linux.ac/drivers/md/Makefile --- linux.vanilla/drivers/md/Makefile Fri Dec 29 22:07:22 2000 +++ linux.ac/drivers/md/Makefile Sat May 26 00:44:05 2001 @@ -6,7 +6,7 @@ export-objs := md.o xor.o list-multi := lvm-mod.o -lvm-mod-objs := lvm.o lvm-snap.o +lvm-mod-objs := lvm.o lvm-snap.o lvm-fs.o # Note: link order is important. All raid personalities # and xor.o must come before md.o, as they each initialise diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/lvm-fs.c linux.ac/drivers/md/lvm-fs.c --- linux.vanilla/drivers/md/lvm-fs.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/md/lvm-fs.c Sat May 26 00:44:05 2001 @@ -0,0 +1,596 @@ +/* + * kernel/lvm-fs.c + * + * Copyright (C) 2001 Sistina Software + * + * January,February 2001 + * + * LVM driver 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, or (at your option) + * any later version. + * + * LVM driver 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 GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/* + * Changelog + * + * 11/01/2001 - First version (Joe Thornber) + * + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "lvm-internal.h" + + +static int _proc_read_vg(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int _proc_read_lv(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int _proc_read_pv(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int _proc_read_global(char *page, char **start, off_t off, + int count, int *eof, void *data); + +static int _vg_info(vg_t *vg_ptr, char *buf); +static int _lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf); +static int _pv_info(pv_t *pv_ptr, char *buf); + +static void _show_uuid(const char *src, char *b, char *e); + +static devfs_handle_t lvm_devfs_handle; +static devfs_handle_t vg_devfs_handle[MAX_VG]; +static devfs_handle_t ch_devfs_handle[MAX_VG]; +static devfs_handle_t lv_devfs_handle[MAX_LV]; + +static struct proc_dir_entry *lvm_proc_dir = NULL; +static struct proc_dir_entry *lvm_proc_vg_subdir = NULL; + +/* inline functions */ + +/* public interface */ +void lvm_init_fs() { + struct proc_dir_entry *pde; + + lvm_devfs_handle = devfs_register( + 0 , "lvm", 0, 0, LVM_CHAR_MAJOR, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, + &lvm_chr_fops, NULL); + + lvm_proc_dir = create_proc_entry(LVM_DIR, S_IFDIR, &proc_root); + if (lvm_proc_dir) { + lvm_proc_vg_subdir = create_proc_entry(LVM_VG_SUBDIR, S_IFDIR, + lvm_proc_dir); + pde = create_proc_entry(LVM_GLOBAL, S_IFREG, lvm_proc_dir); + if ( pde != NULL) pde->read_proc = _proc_read_global; + } +} + +void lvm_fin_fs() { + devfs_unregister (lvm_devfs_handle); + + remove_proc_entry(LVM_GLOBAL, lvm_proc_dir); + remove_proc_entry(LVM_VG_SUBDIR, lvm_proc_dir); + remove_proc_entry(LVM_DIR, &proc_root); +} + +void lvm_fs_create_vg(vg_t *vg_ptr) { + struct proc_dir_entry *pde; + + vg_devfs_handle[vg_ptr->vg_number] = + devfs_mk_dir(0, vg_ptr->vg_name, NULL); + + ch_devfs_handle[vg_ptr->vg_number] = devfs_register( + vg_devfs_handle[vg_ptr->vg_number] , "group", + DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, + &lvm_chr_fops, NULL); + + vg_ptr->vg_dir_pde = create_proc_entry(vg_ptr->vg_name, S_IFDIR, + lvm_proc_vg_subdir); + + if((pde = create_proc_entry("group", S_IFREG, vg_ptr->vg_dir_pde))) { + pde->read_proc = _proc_read_vg; + pde->data = vg_ptr; + } + + vg_ptr->lv_subdir_pde = + create_proc_entry(LVM_LV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde); + + vg_ptr->pv_subdir_pde = + create_proc_entry(LVM_PV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde); +} + +void lvm_fs_remove_vg(vg_t *vg_ptr) { + int i; + + devfs_unregister(ch_devfs_handle[vg_ptr->vg_number]); + devfs_unregister(vg_devfs_handle[vg_ptr->vg_number]); + + /* remove lv's */ + for(i = 0; i < vg_ptr->lv_max; i++) + if(vg_ptr->lv[i]) lvm_fs_remove_lv(vg_ptr, vg_ptr->lv[i]); + + /* remove pv's */ + for(i = 0; i < vg_ptr->pv_max; i++) + if(vg_ptr->pv[i]) lvm_fs_remove_pv(vg_ptr, vg_ptr->pv[i]); + + if(vg_ptr->vg_dir_pde) { + remove_proc_entry(LVM_LV_SUBDIR, vg_ptr->vg_dir_pde); + remove_proc_entry(LVM_PV_SUBDIR, vg_ptr->vg_dir_pde); + remove_proc_entry("group", vg_ptr->vg_dir_pde); + remove_proc_entry(vg_ptr->vg_name, lvm_proc_vg_subdir); + } +} + + +static inline const char *_basename(const char *str) { + const char *name = strrchr(str, '/'); + name = name ? name + 1 : str; + return name; +} + +void lvm_fs_create_lv(vg_t *vg_ptr, lv_t *lv) { + struct proc_dir_entry *pde; + const char *name = _basename(lv->lv_name); + + lv_devfs_handle[MINOR(lv->lv_dev)] = devfs_register( + vg_devfs_handle[vg_ptr->vg_number], name, + DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, MINOR(lv->lv_dev), + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, + &lvm_blk_dops, NULL); + + if(vg_ptr->lv_subdir_pde && +(pde = create_proc_entry(name, S_IFREG, vg_ptr->lv_subdir_pde))) { + pde->read_proc = _proc_read_lv; + pde->data = lv; + } +} + +void lvm_fs_remove_lv(vg_t *vg_ptr, lv_t *lv) { + devfs_unregister(lv_devfs_handle[MINOR(lv->lv_dev)]); + + if(vg_ptr->lv_subdir_pde) { + const char *name = _basename(lv->lv_name); + remove_proc_entry(name, vg_ptr->lv_subdir_pde); + } +} + + +static inline void _make_pv_name(const char *src, char *b, char *e) { + int offset = strlen(LVM_DIR_PREFIX); + if(strncmp(src, LVM_DIR_PREFIX, offset)) + offset = 0; + + e--; + src += offset; + while(*src && (b != e)) { + *b++ = (*src == '/') ? '_' : *src; + src++; + } + *b = '\0'; +} + +void lvm_fs_create_pv(vg_t *vg_ptr, pv_t *pv) { + struct proc_dir_entry *pde; + char name[NAME_LEN]; + + if(!vg_ptr->pv_subdir_pde) + return; + + _make_pv_name(pv->pv_name, name, name + sizeof(name)); + if((pde = create_proc_entry(name, S_IFREG, vg_ptr->pv_subdir_pde))) { + pde->read_proc = _proc_read_pv; + pde->data = pv; + } +} + +void lvm_fs_remove_pv(vg_t *vg_ptr, pv_t *pv) { + char name[NAME_LEN]; + + if(!vg_ptr->pv_subdir_pde) + return; + + _make_pv_name(pv->pv_name, name, name + sizeof(name)); + remove_proc_entry(name, vg_ptr->pv_subdir_pde); +} + + +static int _proc_read_vg(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int sz = 0; + vg_t *vg_ptr = data; + char uuid[NAME_LEN]; + + sz += sprintf(page + sz, "name: %s\n", vg_ptr->vg_name); + sz += sprintf(page + sz, "size: %u\n", + vg_ptr->pe_total * vg_ptr->pe_size / 2); + sz += sprintf(page + sz, "access: %u\n", vg_ptr->vg_access); + sz += sprintf(page + sz, "status: %u\n", vg_ptr->vg_status); + sz += sprintf(page + sz, "number: %u\n", vg_ptr->vg_number); + sz += sprintf(page + sz, "LV max: %u\n", vg_ptr->lv_max); + sz += sprintf(page + sz, "LV current: %u\n", vg_ptr->lv_cur); + sz += sprintf(page + sz, "LV open: %u\n", vg_ptr->lv_open); + sz += sprintf(page + sz, "PV max: %u\n", vg_ptr->pv_max); + sz += sprintf(page + sz, "PV current: %u\n", vg_ptr->pv_cur); + sz += sprintf(page + sz, "PV active: %u\n", vg_ptr->pv_act); + sz += sprintf(page + sz, "PE size: %u\n", vg_ptr->pe_size / 2); + sz += sprintf(page + sz, "PE total: %u\n", vg_ptr->pe_total); + sz += sprintf(page + sz, "PE allocated: %u\n", vg_ptr->pe_allocated); + + _show_uuid(vg_ptr->vg_uuid, uuid, uuid + sizeof(uuid)); + sz += sprintf(page + sz, "uuid: %s\n", uuid); + + return sz; +} + +static int _proc_read_lv(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int sz = 0; + lv_t *lv = data; + + sz += sprintf(page + sz, "name: %s\n", lv->lv_name); + sz += sprintf(page + sz, "size: %u\n", lv->lv_size); + sz += sprintf(page + sz, "access: %u\n", lv->lv_access); + sz += sprintf(page + sz, "status: %u\n", lv->lv_status); + sz += sprintf(page + sz, "number: %u\n", lv->lv_number); + sz += sprintf(page + sz, "open: %u\n", lv->lv_open); + sz += sprintf(page + sz, "allocation: %u\n", lv->lv_allocation); + sz += sprintf(page + sz, "device: %02u:%02u\n", + MAJOR(lv->lv_dev), MINOR(lv->lv_dev)); + + return sz; +} + +static int _proc_read_pv(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int sz = 0; + pv_t *pv = data; + char uuid[NAME_LEN]; + + sz += sprintf(page + sz, "name: %s\n", pv->pv_name); + sz += sprintf(page + sz, "size: %u\n", pv->pv_size); + sz += sprintf(page + sz, "status: %u\n", pv->pv_status); + sz += sprintf(page + sz, "number: %u\n", pv->pv_number); + sz += sprintf(page + sz, "allocatable: %u\n", pv->pv_allocatable); + sz += sprintf(page + sz, "LV current: %u\n", pv->lv_cur); + sz += sprintf(page + sz, "PE size: %u\n", pv->pe_size / 2); + sz += sprintf(page + sz, "PE total: %u\n", pv->pe_total); + sz += sprintf(page + sz, "PE allocated: %u\n", pv->pe_allocated); + sz += sprintf(page + sz, "device: %02u:%02u\n", + MAJOR(pv->pv_dev), MINOR(pv->pv_dev)); + + _show_uuid(pv->pv_uuid, uuid, uuid + sizeof(uuid)); + sz += sprintf(page + sz, "uuid: %s\n", uuid); + + return sz; +} + +static int _proc_read_global(char *page, char **start, off_t pos, int count, + int *eof, void *data) { + +#define LVM_PROC_BUF ( i == 0 ? dummy_buf : &buf[sz]) + + int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter, + lv_open_total, pe_t_bytes, hash_table_bytes, lv_block_exception_t_bytes, seconds; + static off_t sz; + off_t sz_last; + static char *buf = NULL; + static char dummy_buf[160]; /* sized for 2 lines */ + vg_t *vg_ptr; + lv_t *lv_ptr; + pv_t *pv_ptr; + + +#ifdef DEBUG_LVM_PROC_GET_INFO + printk(KERN_DEBUG + "%s - lvm_proc_get_global_info CALLED pos: %lu count: %d whence: %d\n", + lvm_name, pos, count, whence); +#endif + + if(pos != 0 && buf != NULL) + goto out; + + sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \ + lv_open_total = pe_t_bytes = hash_table_bytes = \ + lv_block_exception_t_bytes = 0; + + /* get some statistics */ + for (v = 0; v < ABS_MAX_VG; v++) { + if ((vg_ptr = vg[v]) != NULL) { + vg_counter++; + pv_counter += vg_ptr->pv_cur; + lv_counter += vg_ptr->lv_cur; + if (vg_ptr->lv_cur > 0) { + for (l = 0; l < vg[v]->lv_max; l++) { + if ((lv_ptr = vg_ptr->lv[l]) != NULL) { + pe_t_bytes += lv_ptr->lv_allocated_le; + hash_table_bytes += lv_ptr->lv_snapshot_hash_table_size; + if (lv_ptr->lv_block_exception != NULL) + lv_block_exception_t_bytes += lv_ptr->lv_remap_end; + if (lv_ptr->lv_open > 0) { + lv_open_counter++; + lv_open_total += lv_ptr->lv_open; + } + } + } + } + } + } + + pe_t_bytes *= sizeof(pe_t); + lv_block_exception_t_bytes *= sizeof(lv_block_exception_t); + + if (buf != NULL) { + P_KFREE("%s -- vfree %d\n", lvm_name, __LINE__); + lock_kernel(); + vfree(buf); + unlock_kernel(); + buf = NULL; + } + /* 2 times: first to get size to allocate buffer, + 2nd to fill the malloced buffer */ + for (i = 0; i < 2; i++) { + sz = 0; + sz += sprintf(LVM_PROC_BUF, + "LVM " +#ifdef MODULE + "module" +#else + "driver" +#endif + " %s\n\n" + "Total: %d VG%s %d PV%s %d LV%s ", + lvm_short_version, + vg_counter, vg_counter == 1 ? "" : "s", + pv_counter, pv_counter == 1 ? "" : "s", + lv_counter, lv_counter == 1 ? "" : "s"); + sz += sprintf(LVM_PROC_BUF, + "(%d LV%s open", + lv_open_counter, + lv_open_counter == 1 ? "" : "s"); + if (lv_open_total > 0) + sz += sprintf(LVM_PROC_BUF, + " %d times)\n", + lv_open_total); + else + sz += sprintf(LVM_PROC_BUF, ")"); + sz += sprintf(LVM_PROC_BUF, + "\nGlobal: %lu bytes malloced IOP version: %d ", + vg_counter * sizeof(vg_t) + + pv_counter * sizeof(pv_t) + + lv_counter * sizeof(lv_t) + + pe_t_bytes + hash_table_bytes + lv_block_exception_t_bytes + sz_last, + lvm_iop_version); + + seconds = CURRENT_TIME - loadtime; + if (seconds < 0) + loadtime = CURRENT_TIME + seconds; + if (seconds / 86400 > 0) { + sz += sprintf(LVM_PROC_BUF, "%d day%s ", + seconds / 86400, + seconds / 86400 == 0 || + seconds / 86400 > 1 ? "s" : ""); + } + sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n", + (seconds % 86400) / 3600, + (seconds % 3600) / 60, + seconds % 60); + + if (vg_counter > 0) { + for (v = 0; v < ABS_MAX_VG; v++) { + /* volume group */ + if ((vg_ptr = vg[v]) != NULL) { + sz += _vg_info(vg_ptr, LVM_PROC_BUF); + + /* physical volumes */ + sz += sprintf(LVM_PROC_BUF, + "\n PV%s ", + vg_ptr->pv_cur == 1 ? ": " : "s:"); + c = 0; + for (p = 0; p < vg_ptr->pv_max; p++) { + if ((pv_ptr = vg_ptr->pv[p]) != NULL) { + sz += _pv_info(pv_ptr, LVM_PROC_BUF); + + c++; + if (c < vg_ptr->pv_cur) + sz += sprintf(LVM_PROC_BUF, + "\n "); + } + } + + /* logical volumes */ + sz += sprintf(LVM_PROC_BUF, + "\n LV%s ", + vg_ptr->lv_cur == 1 ? ": " : "s:"); + c = 0; + for (l = 0; l < vg_ptr->lv_max; l++) { + if ((lv_ptr = vg_ptr->lv[l]) != NULL) { + sz += _lv_info(vg_ptr, lv_ptr, LVM_PROC_BUF); + c++; + if (c < vg_ptr->lv_cur) + sz += sprintf(LVM_PROC_BUF, + "\n "); + } + } + if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none"); + sz += sprintf(LVM_PROC_BUF, "\n"); + } + } + } + if (buf == NULL) { + lock_kernel(); + buf = vmalloc(sz); + unlock_kernel(); + if (buf == NULL) { + sz = 0; + return sprintf(page, "%s - vmalloc error at line %d\n", + lvm_name, __LINE__); + } + } + sz_last = sz; + } + + out: + if (pos > sz - 1) { + lock_kernel(); + vfree(buf); + unlock_kernel(); + buf = NULL; + return 0; + } + *start = &buf[pos]; + if (sz - pos < count) + return sz - pos; + else + return count; + +#undef LVM_PROC_BUF +} + +/* + * provide VG info for proc filesystem use (global) + */ +static int _vg_info(vg_t *vg_ptr, char *buf) { + int sz = 0; + char inactive_flag = ' '; + + if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I'; + sz = sprintf(buf, + "\nVG: %c%s [%d PV, %d LV/%d open] " + " PE Size: %d KB\n" + " Usage [KB/PE]: %d /%d total " + "%d /%d used %d /%d free", + inactive_flag, + vg_ptr->vg_name, + vg_ptr->pv_cur, + vg_ptr->lv_cur, + vg_ptr->lv_open, + vg_ptr->pe_size >> 1, + vg_ptr->pe_size * vg_ptr->pe_total >> 1, + vg_ptr->pe_total, + vg_ptr->pe_allocated * vg_ptr->pe_size >> 1, + vg_ptr->pe_allocated, + (vg_ptr->pe_total - vg_ptr->pe_allocated) * + vg_ptr->pe_size >> 1, + vg_ptr->pe_total - vg_ptr->pe_allocated); + return sz; +} + + +/* + * provide LV info for proc filesystem use (global) + */ +static int _lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf) { + int sz = 0; + char inactive_flag = 'A', allocation_flag = ' ', + stripes_flag = ' ', rw_flag = ' ', *basename; + + if (!(lv_ptr->lv_status & LV_ACTIVE)) + inactive_flag = 'I'; + rw_flag = 'R'; + if (lv_ptr->lv_access & LV_WRITE) + rw_flag = 'W'; + allocation_flag = 'D'; + if (lv_ptr->lv_allocation & LV_CONTIGUOUS) + allocation_flag = 'C'; + stripes_flag = 'L'; + if (lv_ptr->lv_stripes > 1) + stripes_flag = 'S'; + sz += sprintf(buf+sz, + "[%c%c%c%c", + inactive_flag, + rw_flag, + allocation_flag, + stripes_flag); + if (lv_ptr->lv_stripes > 1) + sz += sprintf(buf+sz, "%-2d", + lv_ptr->lv_stripes); + else + sz += sprintf(buf+sz, " "); + + /* FIXME: use _basename */ + basename = strrchr(lv_ptr->lv_name, '/'); + if ( basename == 0) basename = lv_ptr->lv_name; + else basename++; + sz += sprintf(buf+sz, "] %-25s", basename); + if (strlen(basename) > 25) + sz += sprintf(buf+sz, + "\n "); + sz += sprintf(buf+sz, "%9d /%-6d ", + lv_ptr->lv_size >> 1, + lv_ptr->lv_size / vg_ptr->pe_size); + + if (lv_ptr->lv_open == 0) + sz += sprintf(buf+sz, "close"); + else + sz += sprintf(buf+sz, "%dx open", + lv_ptr->lv_open); + + return sz; +} + + +/* + * provide PV info for proc filesystem use (global) + */ +static int _pv_info(pv_t *pv, char *buf) { + int sz = 0; + char inactive_flag = 'A', allocation_flag = ' '; + char *pv_name = NULL; + + if (!(pv->pv_status & PV_ACTIVE)) + inactive_flag = 'I'; + allocation_flag = 'A'; + if (!(pv->pv_allocatable & PV_ALLOCATABLE)) + allocation_flag = 'N'; + pv_name = strrchr(pv->pv_name+1,'/'); + if ( pv_name == 0) pv_name = pv->pv_name; + else pv_name++; + sz = sprintf(buf, + "[%c%c] %-21s %8d /%-6d " + "%8d /%-6d %8d /%-6d", + inactive_flag, + allocation_flag, + pv_name, + pv->pe_total * pv->pe_size >> 1, + pv->pe_total, + pv->pe_allocated * pv->pe_size >> 1, + pv->pe_allocated, + (pv->pe_total - pv->pe_allocated) * + pv->pe_size >> 1, + pv->pe_total - pv->pe_allocated); + return sz; +} + +static void _show_uuid(const char *src, char *b, char *e) { + int i; + + e--; + for(i = 0; *src && (b != e); i++) { + if(i && !(i & 0x3)) + *b++ = '-'; + *b++ = *src++; + } + *b = '\0'; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/lvm-internal.h linux.ac/drivers/md/lvm-internal.h --- linux.vanilla/drivers/md/lvm-internal.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/md/lvm-internal.h Sat May 26 00:44:05 2001 @@ -0,0 +1,93 @@ +/* + * kernel/lvm-snap.h + * + * Copyright (C) 2001 Sistina Software + * + * + * LVM driver 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, or (at your option) + * any later version. + * + * LVM driver 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 GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/* + * Changelog + * + * 05/01/2001:Joe Thornber - Factored this file out of lvm.c + * + */ + +#ifndef LVM_SNAP_H +#define LVM_SNAP_H + + +/* global variables, defined in lvm.c */ +extern char *lvm_version; +extern char *lvm_short_version; +extern ushort lvm_iop_version; +extern int loadtime; +extern const char *const lvm_name; + + +extern vg_t *vg[]; +extern struct file_operations lvm_chr_fops; + +extern struct block_device_operations lvm_blk_dops; + + +/* debug macros */ +#ifdef DEBUG_IOCTL +#define P_IOCTL(fmt, args...) printk(KERN_DEBUG "lvm ioctl: " fmt, ## args) +#else +#define P_IOCTL(fmt, args...) +#endif + +#ifdef DEBUG_MAP +#define P_MAP(fmt, args...) printk(KERN_DEBUG "lvm map: " fmt, ## args) +#else +#define P_MAP(fmt, args...) +#endif + +#ifdef DEBUG_KFREE +#define P_KFREE(fmt, args...) printk(KERN_DEBUG "lvm kfree: " fmt, ## args) +#else +#define P_KFREE(fmt, args...) +#endif + + +/* lvm-snap.c */ +int lvm_get_blksize(kdev_t); +int lvm_snapshot_alloc(lv_t *); +void lvm_snapshot_fill_COW_page(vg_t *, lv_t *); +int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, lv_t *); +int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *); +void lvm_snapshot_release(lv_t *); +int lvm_write_COW_table_block(vg_t *, lv_t *); +void lvm_hash_link(lv_block_exception_t *, kdev_t, ulong, lv_t *); +int lvm_snapshot_alloc_hash_table(lv_t *); +void lvm_drop_snapshot(lv_t *, const char *); + + +/* lvm_fs.c */ +void lvm_init_fs(void); +void lvm_fin_fs(void); + +void lvm_fs_create_vg(vg_t *vg_ptr); +void lvm_fs_remove_vg(vg_t *vg_ptr); +void lvm_fs_create_lv(vg_t *vg_ptr, lv_t *lv); +void lvm_fs_remove_lv(vg_t *vg_ptr, lv_t *lv); +void lvm_fs_create_pv(vg_t *vg_ptr, pv_t *pv); +void lvm_fs_remove_pv(vg_t *vg_ptr, pv_t *pv); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/lvm-snap.c linux.ac/drivers/md/lvm-snap.c --- linux.vanilla/drivers/md/lvm-snap.c Mon Apr 30 15:13:15 2001 +++ linux.ac/drivers/md/lvm-snap.c Sat May 26 00:42:19 2001 @@ -26,6 +26,8 @@ * * 05/07/2000 - implemented persistent snapshot support * 23/11/2000 - used cpu_to_le64 rather than my own macro + * 25/01/2001 - Put LockPage back in + * 01/02/2001 - A dropped snapshot is now set as inactive * */ @@ -38,10 +40,11 @@ #include -#include "lvm-snap.h" +#include "lvm-internal.h" static char *lvm_snap_version __attribute__ ((unused)) = "LVM 0.9.1_beta2 snapshot code (18/01/2001)\n"; + extern const char *const lvm_name; extern int lvm_blocksizes[]; @@ -150,6 +153,7 @@ } lvm_snapshot_release(lv_snap); + lv_snap->lv_status &= ~LV_ACTIVE; printk(KERN_INFO "%s -- giving up to snapshot %s on %s due %s\n", @@ -463,7 +467,7 @@ goto out; err = -ENOMEM; - iobuf->locked = 0; + iobuf->locked = 1; iobuf->nr_pages = 0; for (i = 0; i < nr_pages; i++) { @@ -474,6 +478,7 @@ goto out; iobuf->maplist[i] = page; + LockPage(page); iobuf->nr_pages++; } iobuf->offset = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/lvm-snap.h linux.ac/drivers/md/lvm-snap.h --- linux.vanilla/drivers/md/lvm-snap.h Mon Jan 29 00:11:20 2001 +++ linux.ac/drivers/md/lvm-snap.h Thu Jan 1 01:00:00 1970 @@ -1,47 +0,0 @@ -/* - * kernel/lvm-snap.h - * - * Copyright (C) 2001 Sistina Software - * - * - * LVM driver 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, or (at your option) - * any later version. - * - * LVM driver 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 GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - */ - -/* - * Changelog - * - * 05/01/2001:Joe Thornber - Factored this file out of lvm.c - * - */ - -#ifndef LVM_SNAP_H -#define LVM_SNAP_H - -/* external snapshot calls */ -extern inline int lvm_get_blksize(kdev_t); -extern int lvm_snapshot_alloc(lv_t *); -extern void lvm_snapshot_fill_COW_page(vg_t *, lv_t *); -extern int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, lv_t *); -extern int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *); -extern void lvm_snapshot_release(lv_t *); -extern int lvm_write_COW_table_block(vg_t *, lv_t *); -extern inline void lvm_hash_link(lv_block_exception_t *, - kdev_t, ulong, lv_t *); -extern int lvm_snapshot_alloc_hash_table(lv_t *); -extern void lvm_drop_snapshot(lv_t *, const char *); - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/lvm.c linux.ac/drivers/md/lvm.c --- linux.vanilla/drivers/md/lvm.c Mon Apr 30 15:13:15 2001 +++ linux.ac/drivers/md/lvm.c Sat May 26 00:44:13 2001 @@ -147,7 +147,15 @@ * 08/01/2001 - Removed conditional compiles related to PROC_FS, * procfs is always supported now. (JT) * 12/01/2001 - avoided flushing logical volume in case of shrinking - * because of unecessary overhead in case of heavy updates + * because of unnecessary overhead in case of heavy updates + * 25/01/2001 - Allow RO open of an inactive LV so it can be reactivated. + * 31/01/2001 - If you try and BMAP a snapshot you now get an -EPERM + * 01/02/2001 - factored __remap_snapshot out of lvm_map + * 12/02/2001 - move devfs code to create VG before LVs + * 14/02/2001 - tidied device defines for blk.h + * - tidied debug statements + * - more lvm_map tidying + * 14/02/2001 - bug: vg[] member not set back to NULL if activation fails * 05/04/2001 - lvm_map bugs: don't use b_blocknr/b_dev in lvm_map, it * destroys stacking devices. call b_end_io on failed maps. * (Jens Axboe) @@ -155,17 +163,16 @@ */ -static char *lvm_version = "LVM version 0.9.1_beta2 by Heinz Mauelshagen (18/01/2001)\n"; -static char *lvm_short_version = "version 0.9.1_beta2 (18/01/2001)"; - -#define MAJOR_NR LVM_BLK_MAJOR -#define DEVICE_OFF(device) +#define MAJOR_NR LVM_BLK_MAJOR +#define DEVICE_OFF(device) +#define LOCAL_END_REQUEST /* lvm_do_lv_create calls fsync_dev_lockfs()/unlockfs() */ /* #define LVM_VFS_ENHANCEMENT */ #include #include + #include #include @@ -195,7 +202,7 @@ #include #include -#include "lvm-snap.h" +#include "lvm-internal.h" #define LVM_CORRECT_READ_AHEAD( a) \ if ( a < LVM_MIN_READ_AHEAD || \ @@ -205,24 +212,6 @@ # define WRITEA WRITE #endif -/* debug macros */ -#ifdef DEBUG_IOCTL -#define P_IOCTL(fmt, args...) printk(KERN_DEBUG "lvm ioctl: " fmt, ## args) -#else -#define P_IOCTL(fmt, args...) -#endif - -#ifdef DEBUG_MAP -#define P_MAP(fmt, args...) printk(KERN_DEBUG "lvm map: " fmt, ## args) -#else -#define P_MAP(fmt, args...) -#endif - -#ifdef DEBUG_KFREE -#define P_KFREE(fmt, args...) printk(KERN_DEBUG "lvm kfree: " fmt, ## args) -#else -#define P_KFREE(fmt, args...) -#endif /* * External function prototypes @@ -232,27 +221,13 @@ static int lvm_blk_ioctl(struct inode *, struct file *, uint, ulong); static int lvm_blk_open(struct inode *, struct file *); -static int lvm_chr_open(struct inode *, struct file *); - -static int lvm_chr_close(struct inode *, struct file *); static int lvm_blk_close(struct inode *, struct file *); static int lvm_user_bmap(struct inode *, struct lv_bmap *); +static int lvm_chr_open(struct inode *, struct file *); +static int lvm_chr_close(struct inode *, struct file *); static int lvm_chr_ioctl(struct inode *, struct file *, uint, ulong); -int lvm_proc_read_vg_info(char *, char **, off_t, int, int *, void *); -int lvm_proc_read_lv_info(char *, char **, off_t, int, int *, void *); -int lvm_proc_read_pv_info(char *, char **, off_t, int, int *, void *); -static int lvm_proc_get_global_info(char *, char **, off_t, int, int *, void *); - -void lvm_do_create_devfs_entry_of_vg ( vg_t *); - -void lvm_do_create_proc_entry_of_vg ( vg_t *); -void lvm_do_remove_proc_entry_of_vg ( vg_t *); -void lvm_do_create_proc_entry_of_lv ( vg_t *, lv_t *); -void lvm_do_remove_proc_entry_of_lv ( vg_t *, lv_t *); -void lvm_do_create_proc_entry_of_pv ( vg_t *, pv_t *); -void lvm_do_remove_proc_entry_of_pv ( vg_t *, pv_t *); /* End external function prototypes */ @@ -291,26 +266,28 @@ static int lvm_do_vg_rename(vg_t *, void *); static int lvm_do_vg_remove(int); static void lvm_geninit(struct gendisk *); -static char *lvm_show_uuid ( char *); #ifdef LVM_HD_NAME void lvm_hd_name(char *, int); #endif /* END Internal function prototypes */ -/* volume group descriptor area pointers */ -static vg_t *vg[ABS_MAX_VG]; +/* variables */ +char *lvm_version = "LVM version 0.9.1_beta3 by Heinz Mauelshagen " + "(25/01/2001)\n"; +char *lvm_short_version = "version 0.9.1_beta3 (25/01/2001)"; +ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION; +int loadtime = 0; +const char *const lvm_name = LVM_NAME; -static devfs_handle_t lvm_devfs_handle; -static devfs_handle_t vg_devfs_handle[MAX_VG]; -static devfs_handle_t ch_devfs_handle[MAX_VG]; -static devfs_handle_t lv_devfs_handle[MAX_LV]; + +/* volume group descriptor area pointers */ +vg_t *vg[ABS_MAX_VG]; static pv_t *pvp = NULL; static lv_t *lvp = NULL; static pe_t *pep = NULL; static pe_t *pep1 = NULL; -static char *basename = NULL; /* map from block minor number to VG and LV numbers */ @@ -335,32 +312,23 @@ static char pv_name[NAME_LEN]; /* static char rootvg[NAME_LEN] = { 0, }; */ -const char *const lvm_name = LVM_NAME; static int lock = 0; -static int loadtime = 0; static uint vg_count = 0; static long lvm_chr_open_count = 0; -static ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION; static DECLARE_WAIT_QUEUE_HEAD(lvm_wait); static DECLARE_WAIT_QUEUE_HEAD(lvm_map_wait); static spinlock_t lvm_lock = SPIN_LOCK_UNLOCKED; static spinlock_t lvm_snapshot_lock = SPIN_LOCK_UNLOCKED; -static struct proc_dir_entry *lvm_proc_dir = NULL; -static struct proc_dir_entry *lvm_proc_vg_subdir = NULL; -struct proc_dir_entry *pde = NULL; - -static struct file_operations lvm_chr_fops = -{ +struct file_operations lvm_chr_fops = { open: lvm_chr_open, release: lvm_chr_close, ioctl: lvm_chr_ioctl, }; - /* block device operations structure needed for 2.3.38? and above */ -static struct block_device_operations lvm_blk_dops = +struct block_device_operations lvm_blk_dops = { open: lvm_blk_open, release: lvm_blk_close, @@ -408,18 +376,7 @@ return -EIO; } - lvm_devfs_handle = devfs_register( - 0 , "lvm", 0, 0, LVM_CHAR_MAJOR, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, - &lvm_chr_fops, NULL); - - lvm_proc_dir = create_proc_entry (LVM_DIR, S_IFDIR, &proc_root); - if (lvm_proc_dir != NULL) { - lvm_proc_vg_subdir = create_proc_entry (LVM_VG_SUBDIR, S_IFDIR, lvm_proc_dir); - pde = create_proc_entry(LVM_GLOBAL, S_IFREG, lvm_proc_dir); - if ( pde != NULL) pde->read_proc = &lvm_proc_get_global_info; - } - + lvm_init_fs(); lvm_init_vars(); lvm_geninit(&lvm_gendisk); @@ -471,8 +428,6 @@ { struct gendisk *gendisk_ptr = NULL, *gendisk_ptr_prev = NULL; - devfs_unregister (lvm_devfs_handle); - if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) { printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name); } @@ -496,15 +451,14 @@ blksize_size[MAJOR_NR] = NULL; hardsect_size[MAJOR_NR] = NULL; - remove_proc_entry(LVM_GLOBAL, lvm_proc_dir); - remove_proc_entry(LVM_VG_SUBDIR, lvm_proc_dir); - remove_proc_entry(LVM_DIR, &proc_root); - #ifdef LVM_HD_NAME /* reference from linux/drivers/block/genhd.c */ lvm_hd_name_ptr = NULL; #endif + /* unregister with procfs and devfs */ + lvm_fin_fs(); + printk(KERN_INFO "%s -- Module successfully deactivated\n", lvm_name); return; @@ -550,8 +504,7 @@ /* * character device open routine */ -static int lvm_chr_open(struct inode *inode, - struct file *file) +int lvm_chr_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); @@ -582,8 +535,8 @@ * others will block. * */ -static int lvm_chr_ioctl(struct inode *inode, struct file *file, - uint command, ulong a) +int lvm_chr_ioctl(struct inode *inode, struct file *file, + uint command, ulong a) { int minor = MINOR(inode->i_rdev); uint extendable, l, v; @@ -790,7 +743,7 @@ /* * character device close routine */ -static int lvm_chr_close(struct inode *inode, struct file *file) +int lvm_chr_close(struct inode *inode, struct file *file) { #ifdef DEBUG int minor = MINOR(inode->i_rdev); @@ -854,8 +807,12 @@ if (lv_ptr->lv_status & LV_SPINDOWN) return -EPERM; /* Check inactive LV and open for read/write */ - if (!(lv_ptr->lv_status & LV_ACTIVE)) - return -EPERM; + /* We need to be able to "read" an inactive LV + to re-activate it again */ + if ((file->f_mode & FMODE_WRITE) && + (!(lv_ptr->lv_status & LV_ACTIVE))) + return -EPERM; + if (!(lv_ptr->lv_access & LV_WRITE) && (file->f_mode & FMODE_WRITE)) return -EACCES; @@ -892,7 +849,7 @@ void *arg = (void *) a; struct hd_geometry *hd = (struct hd_geometry *) a; - P_IOCTL("%s -- lvm_blk_ioctl MINOR: %d command: 0x%X arg: %X " + P_IOCTL("%s -- lvm_blk_ioctl MINOR: %d command: 0x%X arg: %lX " "VG#: %dl LV#: %d\n", lvm_name, minor, command, (ulong) arg, VG_BLK(minor), LV_BLK(minor)); @@ -922,8 +879,8 @@ /* set read ahead for block device */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; - P_IOCTL("%s -- lvm_blk_ioctl -- BLKRASET: %d sectors for %02X:%02X\n", - lvm_name, (long) arg, MAJOR(inode->i_rdev), minor); + P_IOCTL("%s -- lvm_blk_ioctl -- BLKRASET: %ld sectors for %s\n", + lvm_name, (long) arg, kdevname(inode->i_rdev)); if ((long) arg < LVM_MIN_READ_AHEAD || (long) arg > LVM_MAX_READ_AHEAD) @@ -960,10 +917,10 @@ copy_to_user((long *) &hd->start, &start, sizeof(start)) != 0) return -EFAULT; - } - P_IOCTL("%s -- lvm_blk_ioctl -- cylinders: %d\n", - lvm_name, lv_ptr->lv_size / heads / sectors); + P_IOCTL("%s -- lvm_blk_ioctl -- cylinders: %d\n", + lvm_name, cylinders); + } break; @@ -987,6 +944,11 @@ break; case LV_BMAP: + /* we shouldn't bmap a snapshot, since the mapping can + change */ + if(lv_ptr->lv_access & LV_SNAPSHOT) + return -EPERM; + /* turn logical block into (dev_t, block). non privileged. */ return lvm_user_bmap(inode, (struct lv_bmap *) arg); break; @@ -1075,8 +1037,8 @@ return -EFAULT; memset(&bh,0,sizeof bh); - bh.b_rsector = block; - bh.b_dev = bh.b_rdev = inode->i_dev; + bh.b_blocknr = block; + bh.b_dev = bh.b_rdev = inode->i_rdev; bh.b_size = lvm_get_blksize(bh.b_dev); if ((err=lvm_map(&bh, READ)) < 0) { printk("lvm map failed: %d\n", err); @@ -1089,402 +1051,25 @@ /* - * provide VG info for proc filesystem use (global) - */ -int lvm_vg_info(vg_t *vg_ptr, char *buf) { - int sz = 0; - char inactive_flag = ' '; - - if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I'; - sz = sprintf(buf, - "\nVG: %c%s [%d PV, %d LV/%d open] " - " PE Size: %d KB\n" - " Usage [KB/PE]: %d /%d total " - "%d /%d used %d /%d free", - inactive_flag, - vg_ptr->vg_name, - vg_ptr->pv_cur, - vg_ptr->lv_cur, - vg_ptr->lv_open, - vg_ptr->pe_size >> 1, - vg_ptr->pe_size * vg_ptr->pe_total >> 1, - vg_ptr->pe_total, - vg_ptr->pe_allocated * vg_ptr->pe_size >> 1, - vg_ptr->pe_allocated, - (vg_ptr->pe_total - vg_ptr->pe_allocated) * - vg_ptr->pe_size >> 1, - vg_ptr->pe_total - vg_ptr->pe_allocated); - return sz; -} - - -/* - * provide LV info for proc filesystem use (global) - */ -int lvm_lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf) { - int sz = 0; - char inactive_flag = 'A', allocation_flag = ' ', - stripes_flag = ' ', rw_flag = ' '; - - if (!(lv_ptr->lv_status & LV_ACTIVE)) - inactive_flag = 'I'; - rw_flag = 'R'; - if (lv_ptr->lv_access & LV_WRITE) - rw_flag = 'W'; - allocation_flag = 'D'; - if (lv_ptr->lv_allocation & LV_CONTIGUOUS) - allocation_flag = 'C'; - stripes_flag = 'L'; - if (lv_ptr->lv_stripes > 1) - stripes_flag = 'S'; - sz += sprintf(buf+sz, - "[%c%c%c%c", - inactive_flag, - rw_flag, - allocation_flag, - stripes_flag); - if (lv_ptr->lv_stripes > 1) - sz += sprintf(buf+sz, "%-2d", - lv_ptr->lv_stripes); - else - sz += sprintf(buf+sz, " "); - basename = strrchr(lv_ptr->lv_name, '/'); - if ( basename == 0) basename = lv_ptr->lv_name; - else basename++; - sz += sprintf(buf+sz, "] %-25s", basename); - if (strlen(basename) > 25) - sz += sprintf(buf+sz, - "\n "); - sz += sprintf(buf+sz, "%9d /%-6d ", - lv_ptr->lv_size >> 1, - lv_ptr->lv_size / vg_ptr->pe_size); - - if (lv_ptr->lv_open == 0) - sz += sprintf(buf+sz, "close"); - else - sz += sprintf(buf+sz, "%dx open", - lv_ptr->lv_open); - - return sz; -} - - -/* - * provide PV info for proc filesystem use (global) - */ -int lvm_pv_info(pv_t *pv_ptr, char *buf) { - int sz = 0; - char inactive_flag = 'A', allocation_flag = ' '; - char *pv_name = NULL; - - if (!(pv_ptr->pv_status & PV_ACTIVE)) - inactive_flag = 'I'; - allocation_flag = 'A'; - if (!(pv_ptr->pv_allocatable & PV_ALLOCATABLE)) - allocation_flag = 'N'; - pv_name = strrchr(pv_ptr->pv_name+1,'/'); - if ( pv_name == 0) pv_name = pv_ptr->pv_name; - else pv_name++; - sz = sprintf(buf, - "[%c%c] %-21s %8d /%-6d " - "%8d /%-6d %8d /%-6d", - inactive_flag, - allocation_flag, - pv_name, - pv_ptr->pe_total * - pv_ptr->pe_size >> 1, - pv_ptr->pe_total, - pv_ptr->pe_allocated * - pv_ptr->pe_size >> 1, - pv_ptr->pe_allocated, - (pv_ptr->pe_total - - pv_ptr->pe_allocated) * - pv_ptr->pe_size >> 1, - pv_ptr->pe_total - - pv_ptr->pe_allocated); - return sz; -} - - -/* - * Support functions /proc-Filesystem - */ - -#define LVM_PROC_BUF ( i == 0 ? dummy_buf : &buf[sz]) - -/* - * provide global LVM information - */ -static int lvm_proc_get_global_info(char *page, char **start, off_t pos, int count, int *eof, void *data) -{ - int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter, - lv_open_total, pe_t_bytes, hash_table_bytes, lv_block_exception_t_bytes, seconds; - static off_t sz; - off_t sz_last; - static char *buf = NULL; - static char dummy_buf[160]; /* sized for 2 lines */ - vg_t *vg_ptr; - lv_t *lv_ptr; - pv_t *pv_ptr; - - -#ifdef DEBUG_LVM_PROC_GET_INFO - printk(KERN_DEBUG - "%s - lvm_proc_get_global_info CALLED pos: %lu count: %d whence: %d\n", - lvm_name, pos, count, whence); -#endif - - MOD_INC_USE_COUNT; - - if (pos == 0 || buf == NULL) { - sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \ - lv_open_total = pe_t_bytes = hash_table_bytes = \ - lv_block_exception_t_bytes = 0; - - /* search for activity */ - for (v = 0; v < ABS_MAX_VG; v++) { - if ((vg_ptr = vg[v]) != NULL) { - vg_counter++; - pv_counter += vg_ptr->pv_cur; - lv_counter += vg_ptr->lv_cur; - if (vg_ptr->lv_cur > 0) { - for (l = 0; l < vg[v]->lv_max; l++) { - if ((lv_ptr = vg_ptr->lv[l]) != NULL) { - pe_t_bytes += lv_ptr->lv_allocated_le; - hash_table_bytes += lv_ptr->lv_snapshot_hash_table_size; - if (lv_ptr->lv_block_exception != NULL) - lv_block_exception_t_bytes += lv_ptr->lv_remap_end; - if (lv_ptr->lv_open > 0) { - lv_open_counter++; - lv_open_total += lv_ptr->lv_open; - } - } - } - } - } - } - pe_t_bytes *= sizeof(pe_t); - lv_block_exception_t_bytes *= sizeof(lv_block_exception_t); - - if (buf != NULL) { - P_KFREE("%s -- vfree %d\n", lvm_name, __LINE__); - lock_kernel(); - vfree(buf); - unlock_kernel(); - buf = NULL; - } - /* 2 times: first to get size to allocate buffer, - 2nd to fill the malloced buffer */ - for (i = 0; i < 2; i++) { - sz = 0; - sz += sprintf(LVM_PROC_BUF, - "LVM " -#ifdef MODULE - "module" -#else - "driver" -#endif - " %s\n\n" - "Total: %d VG%s %d PV%s %d LV%s ", - lvm_short_version, - vg_counter, vg_counter == 1 ? "" : "s", - pv_counter, pv_counter == 1 ? "" : "s", - lv_counter, lv_counter == 1 ? "" : "s"); - sz += sprintf(LVM_PROC_BUF, - "(%d LV%s open", - lv_open_counter, - lv_open_counter == 1 ? "" : "s"); - if (lv_open_total > 0) - sz += sprintf(LVM_PROC_BUF, - " %d times)\n", - lv_open_total); - else - sz += sprintf(LVM_PROC_BUF, ")"); - sz += sprintf(LVM_PROC_BUF, - "\nGlobal: %lu bytes malloced IOP version: %d ", - vg_counter * sizeof(vg_t) + - pv_counter * sizeof(pv_t) + - lv_counter * sizeof(lv_t) + - pe_t_bytes + hash_table_bytes + lv_block_exception_t_bytes + sz_last, - lvm_iop_version); - - seconds = CURRENT_TIME - loadtime; - if (seconds < 0) - loadtime = CURRENT_TIME + seconds; - if (seconds / 86400 > 0) { - sz += sprintf(LVM_PROC_BUF, "%d day%s ", - seconds / 86400, - seconds / 86400 == 0 || - seconds / 86400 > 1 ? "s" : ""); - } - sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n", - (seconds % 86400) / 3600, - (seconds % 3600) / 60, - seconds % 60); - - if (vg_counter > 0) { - for (v = 0; v < ABS_MAX_VG; v++) { - /* volume group */ - if ((vg_ptr = vg[v]) != NULL) { - sz += lvm_vg_info(vg_ptr, LVM_PROC_BUF); - - /* physical volumes */ - sz += sprintf(LVM_PROC_BUF, - "\n PV%s ", - vg_ptr->pv_cur == 1 ? ": " : "s:"); - c = 0; - for (p = 0; p < vg_ptr->pv_max; p++) { - if ((pv_ptr = vg_ptr->pv[p]) != NULL) { - sz += lvm_pv_info(pv_ptr, LVM_PROC_BUF); - - c++; - if (c < vg_ptr->pv_cur) - sz += sprintf(LVM_PROC_BUF, - "\n "); - } - } - - /* logical volumes */ - sz += sprintf(LVM_PROC_BUF, - "\n LV%s ", - vg_ptr->lv_cur == 1 ? ": " : "s:"); - c = 0; - for (l = 0; l < vg_ptr->lv_max; l++) { - if ((lv_ptr = vg_ptr->lv[l]) != NULL) { - sz += lvm_lv_info(vg_ptr, lv_ptr, LVM_PROC_BUF); - c++; - if (c < vg_ptr->lv_cur) - sz += sprintf(LVM_PROC_BUF, - "\n "); - } - } - if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none"); - sz += sprintf(LVM_PROC_BUF, "\n"); - } - } - } - if (buf == NULL) { - lock_kernel(); - buf = vmalloc(sz); - unlock_kernel(); - if (buf == NULL) { - sz = 0; - MOD_DEC_USE_COUNT; - return sprintf(page, "%s - vmalloc error at line %d\n", - lvm_name, __LINE__); - } - } - sz_last = sz; - } - } - MOD_DEC_USE_COUNT; - if (pos > sz - 1) { - lock_kernel(); - vfree(buf); - unlock_kernel(); - buf = NULL; - return 0; - } - *start = &buf[pos]; - if (sz - pos < count) - return sz - pos; - else - return count; -} /* lvm_proc_get_global_info() */ - - -/* - * provide VG information - */ -int lvm_proc_read_vg_info(char *page, char **start, off_t off, - int count, int *eof, void *data) { - int sz = 0; - vg_t *vg = data; - - sz += sprintf ( page+sz, "name: %s\n", vg->vg_name); - sz += sprintf ( page+sz, "size: %u\n", - vg->pe_total * vg->pe_size / 2); - sz += sprintf ( page+sz, "access: %u\n", vg->vg_access); - sz += sprintf ( page+sz, "status: %u\n", vg->vg_status); - sz += sprintf ( page+sz, "number: %u\n", vg->vg_number); - sz += sprintf ( page+sz, "LV max: %u\n", vg->lv_max); - sz += sprintf ( page+sz, "LV current: %u\n", vg->lv_cur); - sz += sprintf ( page+sz, "LV open: %u\n", vg->lv_open); - sz += sprintf ( page+sz, "PV max: %u\n", vg->pv_max); - sz += sprintf ( page+sz, "PV current: %u\n", vg->pv_cur); - sz += sprintf ( page+sz, "PV active: %u\n", vg->pv_act); - sz += sprintf ( page+sz, "PE size: %u\n", vg->pe_size / 2); - sz += sprintf ( page+sz, "PE total: %u\n", vg->pe_total); - sz += sprintf ( page+sz, "PE allocated: %u\n", vg->pe_allocated); - sz += sprintf ( page+sz, "uuid: %s\n", lvm_show_uuid(vg->vg_uuid)); - - return sz; -} - - -/* - * provide LV information - */ -int lvm_proc_read_lv_info(char *page, char **start, off_t off, - int count, int *eof, void *data) { - int sz = 0; - lv_t *lv = data; - - sz += sprintf ( page+sz, "name: %s\n", lv->lv_name); - sz += sprintf ( page+sz, "size: %u\n", lv->lv_size); - sz += sprintf ( page+sz, "access: %u\n", lv->lv_access); - sz += sprintf ( page+sz, "status: %u\n", lv->lv_status); - sz += sprintf ( page+sz, "number: %u\n", lv->lv_number); - sz += sprintf ( page+sz, "open: %u\n", lv->lv_open); - sz += sprintf ( page+sz, "allocation: %u\n", lv->lv_allocation); - sz += sprintf ( page+sz, "device: %02u:%02u\n", - MAJOR(lv->lv_dev), MINOR(lv->lv_dev)); - - return sz; -} - - -/* - * provide PV information - */ -int lvm_proc_read_pv_info(char *page, char **start, off_t off, - int count, int *eof, void *data) { - int sz = 0; - pv_t *pv = data; - - sz += sprintf ( page+sz, "name: %s\n", pv->pv_name); - sz += sprintf ( page+sz, "size: %u\n", pv->pv_size); - sz += sprintf ( page+sz, "status: %u\n", pv->pv_status); - sz += sprintf ( page+sz, "number: %u\n", pv->pv_number); - sz += sprintf ( page+sz, "allocatable: %u\n", pv->pv_allocatable); - sz += sprintf ( page+sz, "LV current: %u\n", pv->lv_cur); - sz += sprintf ( page+sz, "PE size: %u\n", pv->pe_size / 2); - sz += sprintf ( page+sz, "PE total: %u\n", pv->pe_total); - sz += sprintf ( page+sz, "PE allocated: %u\n", pv->pe_allocated); - sz += sprintf ( page+sz, "device: %02u:%02u\n", - MAJOR(pv->pv_dev), MINOR(pv->pv_dev)); - sz += sprintf ( page+sz, "uuid: %s\n", lvm_show_uuid(pv->pv_uuid)); - - - return sz; -} - - -/* * block device support function for /usr/src/linux/drivers/block/ll_rw_blk.c * (see init_module/lvm_init) */ +static inline void __remap_snapshot(kdev_t rdev, ulong rsector, + ulong pe_start, lv_t *lv, vg_t *vg) { + if (!lvm_snapshot_remap_block(&rdev, &rsector, pe_start, lv) && + !lvm_snapshot_COW(rdev, rsector, pe_start, rsector, lv)) + lvm_write_COW_table_block(vg, lv); +} + static int lvm_map(struct buffer_head *bh, int rw) { int minor = MINOR(bh->b_rdev); - int ret = 0; ulong index; ulong pe_start; ulong size = bh->b_size >> 9; - ulong rsector_tmp = bh->b_rsector; - ulong rsector_sav; - kdev_t rdev_tmp = bh->b_rdev; - kdev_t rdev_sav; + ulong rsector_org = bh->b_blocknr * size; + ulong rsector_map; + kdev_t rdev_map; vg_t *vg_this = vg[VG_BLK(minor)]; lv_t *lv = vg_this->lv[LV_BLK(minor)]; @@ -1493,153 +1078,130 @@ printk(KERN_ALERT "%s - lvm_map: ll_rw_blk for inactive LV %s\n", lvm_name, lv->lv_name); - return -1; + goto bad; } if ((rw == WRITE || rw == WRITEA) && !(lv->lv_access & LV_WRITE)) { printk(KERN_CRIT - "%s - lvm_map: ll_rw_blk write for readonly LV %s\n", + "%s - lvm_map: ll_rw_blk write for readonly LV %s\n", lvm_name, lv->lv_name); - return -1; + goto bad; } - P_MAP("%s - lvm_map minor:%d *rdev: %02d:%02d *rsector: %lu " - "size:%lu\n", + P_MAP("%s - lvm_map minor: %d *rdev: %s *rsector: %lu size:%lu\n", lvm_name, minor, - MAJOR(rdev_tmp), - MINOR(rdev_tmp), - rsector_tmp, size); + kdevname(bh->b_dev), + rsector_org, size); - if (rsector_tmp + size > lv->lv_size) { + if (rsector_org + size > lv->lv_size) { printk(KERN_ALERT "%s - lvm_map access beyond end of device; *rsector: " "%lu or size: %lu wrong for minor: %2d\n", - lvm_name, rsector_tmp, size, minor); - return -1; + lvm_name, rsector_org, size, minor); + goto bad; } - rsector_sav = rsector_tmp; - rdev_sav = rdev_tmp; -lvm_second_remap: - /* linear mapping */ - if (lv->lv_stripes < 2) { +remap: + if (lv->lv_stripes < 2) { /* linear mapping */ /* get the index */ - index = rsector_tmp / vg_this->pe_size; + index = rsector_org / vg_this->pe_size; pe_start = lv->lv_current_pe[index].pe; - rsector_tmp = lv->lv_current_pe[index].pe + - (rsector_tmp % vg_this->pe_size); - rdev_tmp = lv->lv_current_pe[index].dev; - - P_MAP("lv_current_pe[%ld].pe: %ld rdev: %02d:%02d " - "rsector:%ld\n", - index, - lv->lv_current_pe[index].pe, - MAJOR(rdev_tmp), - MINOR(rdev_tmp), - rsector_tmp); + rsector_map = lv->lv_current_pe[index].pe + + (rsector_org % vg_this->pe_size); + rdev_map = lv->lv_current_pe[index].dev; + + P_MAP("lv_current_pe[%ld].pe: %d rdev: %s rsector:%ld\n", + index, lv->lv_current_pe[index].pe, + kdevname(rdev_map), rsector_map); - /* striped mapping */ - } else { + } else { /* striped mapping */ ulong stripe_index; ulong stripe_length; stripe_length = vg_this->pe_size * lv->lv_stripes; - stripe_index = (rsector_tmp % stripe_length) / lv->lv_stripesize; - index = rsector_tmp / stripe_length + - (stripe_index % lv->lv_stripes) * - (lv->lv_allocated_le / lv->lv_stripes); + stripe_index = (rsector_org % stripe_length) / + lv->lv_stripesize; + index = rsector_org / stripe_length + + (stripe_index % lv->lv_stripes) * + (lv->lv_allocated_le / lv->lv_stripes); pe_start = lv->lv_current_pe[index].pe; - rsector_tmp = lv->lv_current_pe[index].pe + - (rsector_tmp % stripe_length) - - (stripe_index % lv->lv_stripes) * lv->lv_stripesize - - stripe_index / lv->lv_stripes * - (lv->lv_stripes - 1) * lv->lv_stripesize; - rdev_tmp = lv->lv_current_pe[index].dev; - } - - P_MAP("lv_current_pe[%ld].pe: %ld rdev: %02d:%02d rsector:%ld\n" - "stripe_length: %ld stripe_index: %ld\n", - index, - lv->lv_current_pe[index].pe, - MAJOR(rdev_tmp), - MINOR(rdev_tmp), - rsector_tmp, - stripe_length, - stripe_index); + rsector_map = lv->lv_current_pe[index].pe + + (rsector_org % stripe_length) - + (stripe_index % lv->lv_stripes) * lv->lv_stripesize - + stripe_index / lv->lv_stripes * + (lv->lv_stripes - 1) * lv->lv_stripesize; + rdev_map = lv->lv_current_pe[index].dev; + + P_MAP("lv_current_pe[%ld].pe: %d rdev: %s rsector:%ld\n" + "stripe_length: %ld stripe_index: %ld\n", + index, lv->lv_current_pe[index].pe, kdevname(rdev_tmp), + rsector_tmp, stripe_length, stripe_index); + } /* handle physical extents on the move */ if (pe_lock_req.lock == LOCK_PE) { - if (rdev_tmp == pe_lock_req.data.pv_dev && - rsector_tmp >= pe_lock_req.data.pv_offset && - rsector_tmp < (pe_lock_req.data.pv_offset + + if (rdev_map == pe_lock_req.data.pv_dev && + rsector_map >= pe_lock_req.data.pv_offset && + rsector_map < (pe_lock_req.data.pv_offset + vg_this->pe_size)) { sleep_on(&lvm_map_wait); - rsector_tmp = rsector_sav; - rdev_tmp = rdev_sav; - goto lvm_second_remap; + goto remap; } } + /* statistic */ if (rw == WRITE || rw == WRITEA) lv->lv_current_pe[index].writes++; else lv->lv_current_pe[index].reads++; - /* snapshot volume exception handling on physical device address base */ - if (lv->lv_access & (LV_SNAPSHOT|LV_SNAPSHOT_ORG)) { - /* original logical volume */ - if (lv->lv_access & LV_SNAPSHOT_ORG) { - /* Serializes the access to the lv_snapshot_next list */ - down(&lv->lv_snapshot_sem); - if (rw == WRITE || rw == WRITEA) - { - lv_t *lv_ptr; - - /* start with first snapshot and loop thrugh all of them */ - for (lv_ptr = lv->lv_snapshot_next; - lv_ptr != NULL; - lv_ptr = lv_ptr->lv_snapshot_next) { - /* Check for inactive snapshot */ - if (!(lv_ptr->lv_status & LV_ACTIVE)) continue; - /* Serializes the COW with the accesses to the snapshot device */ - down(&lv_ptr->lv_snapshot_sem); - /* do we still have exception storage for this snapshot free? */ - if (lv_ptr->lv_block_exception != NULL) { - rdev_sav = rdev_tmp; - rsector_sav = rsector_tmp; - if (!lvm_snapshot_remap_block(&rdev_tmp, - &rsector_tmp, - pe_start, - lv_ptr)) { - /* create a new mapping */ - if (!(ret = lvm_snapshot_COW(rdev_tmp, - rsector_tmp, - pe_start, - rsector_sav, - lv_ptr))) - ret = lvm_write_COW_table_block(vg_this, - lv_ptr); - } - rdev_tmp = rdev_sav; - rsector_tmp = rsector_sav; - } - up(&lv_ptr->lv_snapshot_sem); - } - } - up(&lv->lv_snapshot_sem); - } else { - /* remap snapshot logical volume */ - down(&lv->lv_snapshot_sem); - if (lv->lv_block_exception != NULL) - lvm_snapshot_remap_block(&rdev_tmp, &rsector_tmp, pe_start, lv); + /* snapshot volume exception handling on physical device + address base */ + if (!(lv->lv_access & (LV_SNAPSHOT|LV_SNAPSHOT_ORG))) + goto out; + + if (lv->lv_access & LV_SNAPSHOT) { /* remap snapshot */ + down(&lv->lv_snapshot_sem); + if (lv->lv_block_exception) + lvm_snapshot_remap_block(&rdev_map, &rsector_map, + pe_start, lv); + else { up(&lv->lv_snapshot_sem); + goto bad; } - } - bh->b_rdev = rdev_tmp; - bh->b_rsector = rsector_tmp; + up(&lv->lv_snapshot_sem); - return ret; + } else if(rw == WRITE || rw == WRITEA) { /* snapshot origin */ + lv_t *snap; + + /* Serializes the access to the lv_snapshot_next list */ + down(&lv->lv_snapshot_sem); + /* start with first snapshot and loop through all of + them */ + for (snap = lv->lv_snapshot_next; snap; + snap = snap->lv_snapshot_next) { + /* Check for inactive snapshot */ + if (!(snap->lv_status & LV_ACTIVE)) + continue; + + /* Serializes the COW with the accesses to the + snapshot device */ + down(&snap->lv_snapshot_sem); + __remap_snapshot(rdev_map, rsector_map, + pe_start, snap, vg_this); + up(&snap->lv_snapshot_sem); + } + up(&lv->lv_snapshot_sem); + } + + out: + bh->b_rdev = rdev_map; + bh->b_rsector = rsector_map; + return 1; + + bad: + return -1; } /* lvm_map() */ @@ -1818,26 +1380,29 @@ /* we are not that active so far... */ vg_ptr->vg_status &= ~VG_ACTIVE; - vg[VG_CHR(minor)] = vg_ptr; - vg[VG_CHR(minor)]->pe_allocated = 0; + vg_ptr->pe_allocated = 0; if (vg_ptr->pv_max > ABS_MAX_PV) { printk(KERN_WARNING "%s -- Can't activate VG: ABS_MAX_PV too small\n", lvm_name); kfree(vg_ptr); - vg[VG_CHR(minor)] = NULL; return -EPERM; } + if (vg_ptr->lv_max > ABS_MAX_LV) { printk(KERN_WARNING "%s -- Can't activate VG: ABS_MAX_LV too small for %u\n", lvm_name, vg_ptr->lv_max); kfree(vg_ptr); - vg_ptr = NULL; return -EPERM; } + /* create devfs and procfs entries */ + lvm_fs_create_vg(vg_ptr); + + vg[VG_CHR(minor)] = vg_ptr; + /* get the physical volume structures */ vg_ptr->pv_act = vg_ptr->pv_cur = 0; for (p = 0; p < vg_ptr->pv_max; p++) { @@ -1885,8 +1450,6 @@ } } - lvm_do_create_devfs_entry_of_vg ( vg_ptr); - /* Second path to correct snapshot logical volumes which are not in place during first path above */ for (l = 0; l < ls; l++) { @@ -1901,8 +1464,6 @@ } } - lvm_do_create_proc_entry_of_vg ( vg_ptr); - vfree(snap_lv_ptr); vg_count++; @@ -1934,7 +1495,6 @@ if ( ret != 0) return ret; pv_ptr = vg_ptr->pv[p]; vg_ptr->pe_total += pv_ptr->pe_total; - lvm_do_create_proc_entry_of_pv(vg_ptr, pv_ptr); return 0; } } @@ -1984,10 +1544,12 @@ lv_t *lv_ptr = NULL; pv_t *pv_ptr = NULL; + if (vg_ptr == NULL) return -ENXIO; + if (copy_from_user(vg_name, arg, sizeof(vg_name)) != 0) return -EFAULT; - lvm_do_remove_proc_entry_of_vg ( vg_ptr); + lvm_fs_remove_vg(vg_ptr); strncpy ( vg_ptr->vg_name, vg_name, sizeof ( vg_name)-1); for ( l = 0; l < vg_ptr->lv_max; l++) @@ -2009,7 +1571,7 @@ strncpy(pv_ptr->vg_name, vg_name, NAME_LEN); } - lvm_do_create_proc_entry_of_vg ( vg_ptr); + lvm_fs_create_vg(vg_ptr); return 0; } /* lvm_do_vg_rename */ @@ -2036,6 +1598,9 @@ /* let's go inactive */ vg_ptr->vg_status &= ~VG_ACTIVE; + /* remove from procfs and devfs */ + lvm_fs_remove_vg(vg_ptr); + /* free LVs */ /* first free snapshot logical volumes */ for (i = 0; i < vg_ptr->lv_max; i++) { @@ -2063,11 +1628,6 @@ } } - devfs_unregister (ch_devfs_handle[vg_ptr->vg_number]); - devfs_unregister (vg_devfs_handle[vg_ptr->vg_number]); - - lvm_do_remove_proc_entry_of_vg ( vg_ptr); - P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__); kfree(vg_ptr); vg[VG_CHR(minor)] = NULL; @@ -2103,6 +1663,7 @@ pv_ptr->pv_status = PV_ACTIVE; vg_ptr->pv_act++; vg_ptr->pv_cur++; + lvm_fs_create_pv(vg_ptr, pv_ptr); return 0; } /* lvm_do_pv_create() */ @@ -2114,7 +1675,8 @@ static int lvm_do_pv_remove(vg_t *vg_ptr, ulong p) { pv_t *pv_ptr = vg_ptr->pv[p]; - lvm_do_remove_proc_entry_of_pv ( vg_ptr, pv_ptr); + lvm_fs_remove_pv(vg_ptr, pv_ptr); + vg_ptr->pe_total -= pv_ptr->pe_total; vg_ptr->pv_cur--; vg_ptr->pv_act--; @@ -2274,7 +1836,6 @@ lvm_snapshot_fill_COW_page(vg_ptr, lv_ptr); init_waitqueue_head(&lv_ptr->lv_snapshot_wait); } else { - vfree(lv_ptr->lv_block_exception); kfree(lv_ptr); vg_ptr->lv[l] = NULL; return -EFAULT; @@ -2296,22 +1857,6 @@ vg_ptr->lv_cur++; lv_ptr->lv_status = lv_status_save; - { - char *lv_tmp, *lv_buf = lv->lv_name; - - strtok(lv->lv_name, "/"); /* /dev */ - while((lv_tmp = strtok(NULL, "/")) != NULL) - lv_buf = lv_tmp; - - lv_devfs_handle[lv->lv_number] = devfs_register( - vg_devfs_handle[vg_ptr->vg_number], lv_buf, - DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, lv->lv_number, - S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, - &lvm_blk_dops, NULL); - } - - lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr); - /* optionally add our new snapshot LV */ if (lv_ptr->lv_access & LV_SNAPSHOT) { lv_t *org = lv_ptr->lv_snapshot_org, *last; @@ -2350,6 +1895,8 @@ lv_ptr->vg = vg_ptr; + lvm_fs_create_lv(vg_ptr, lv_ptr); + return 0; } /* lvm_do_lv_create() */ @@ -2387,6 +1934,8 @@ lv_ptr->lv_snapshot_next != NULL) return -EPERM; + lvm_fs_remove_lv(vg_ptr, lv_ptr); + if (lv_ptr->lv_access & LV_SNAPSHOT) { /* * Atomically make the the snapshot invisible @@ -2448,10 +1997,6 @@ vfree(lv_ptr->lv_current_pe); } - devfs_unregister(lv_devfs_handle[lv_ptr->lv_number]); - - lvm_do_remove_proc_entry_of_lv ( vg_ptr, lv_ptr); - P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__); kfree(lv_ptr); vg_ptr->lv[l] = NULL; @@ -2787,11 +2332,11 @@ if ( (lv_ptr = vg_ptr->lv[l]) == NULL) continue; if (lv_ptr->lv_dev == lv->lv_dev) { - lvm_do_remove_proc_entry_of_lv ( vg_ptr, lv_ptr); + lvm_fs_remove_lv(vg_ptr, lv_ptr); strncpy(lv_ptr->lv_name, lv_req->lv_name, NAME_LEN); - lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr); + lvm_fs_create_lv(vg_ptr, lv_ptr); break; } } @@ -2871,156 +2416,6 @@ } /* lvm_do_pv_status() */ - -/* - * create a devfs entry for a volume group - */ -void lvm_do_create_devfs_entry_of_vg ( vg_t *vg_ptr) { - vg_devfs_handle[vg_ptr->vg_number] = devfs_mk_dir(0, vg_ptr->vg_name, NULL); - ch_devfs_handle[vg_ptr->vg_number] = devfs_register( - vg_devfs_handle[vg_ptr->vg_number] , "group", - DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, - &lvm_chr_fops, NULL); -} - - -/* - * create a /proc entry for a logical volume - */ -void lvm_do_create_proc_entry_of_lv ( vg_t *vg_ptr, lv_t *lv_ptr) { - char *basename; - - if ( vg_ptr->lv_subdir_pde != NULL) { - basename = strrchr(lv_ptr->lv_name, '/'); - if (basename == NULL) basename = lv_ptr->lv_name; - else basename++; - pde = create_proc_entry(basename, S_IFREG, - vg_ptr->lv_subdir_pde); - if ( pde != NULL) { - pde->read_proc = lvm_proc_read_lv_info; - pde->data = lv_ptr; - } - } -} - - -/* - * remove a /proc entry for a logical volume - */ -void lvm_do_remove_proc_entry_of_lv ( vg_t *vg_ptr, lv_t *lv_ptr) { - char *basename; - - if ( vg_ptr->lv_subdir_pde != NULL) { - basename = strrchr(lv_ptr->lv_name, '/'); - if (basename == NULL) basename = lv_ptr->lv_name; - else basename++; - remove_proc_entry(basename, vg_ptr->lv_subdir_pde); - } -} - - -/* - * create a /proc entry for a physical volume - */ -void lvm_do_create_proc_entry_of_pv ( vg_t *vg_ptr, pv_t *pv_ptr) { - int offset = 0; - char *basename; - char buffer[NAME_LEN]; - - basename = pv_ptr->pv_name; - if (strncmp(basename, "/dev/", 5) == 0) offset = 5; - strncpy(buffer, basename + offset, sizeof(buffer)); - basename = buffer; - while ( ( basename = strchr ( basename, '/')) != NULL) *basename = '_'; - pde = create_proc_entry(buffer, S_IFREG, vg_ptr->pv_subdir_pde); - if ( pde != NULL) { - pde->read_proc = lvm_proc_read_pv_info; - pde->data = pv_ptr; - } -} - - -/* - * remove a /proc entry for a physical volume - */ -void lvm_do_remove_proc_entry_of_pv ( vg_t *vg_ptr, pv_t *pv_ptr) { - char *basename; - - basename = strrchr(pv_ptr->pv_name, '/'); - if ( vg_ptr->pv_subdir_pde != NULL) { - basename = strrchr(pv_ptr->pv_name, '/'); - if (basename == NULL) basename = pv_ptr->pv_name; - else basename++; - remove_proc_entry(basename, vg_ptr->pv_subdir_pde); - } -} - - -/* - * create a /proc entry for a volume group - */ -void lvm_do_create_proc_entry_of_vg ( vg_t *vg_ptr) { - int l, p; - pv_t *pv_ptr; - lv_t *lv_ptr; - - pde = create_proc_entry(vg_ptr->vg_name, S_IFDIR, - lvm_proc_vg_subdir); - if ( pde != NULL) { - vg_ptr->vg_dir_pde = pde; - pde = create_proc_entry("group", S_IFREG, - vg_ptr->vg_dir_pde); - if ( pde != NULL) { - pde->read_proc = lvm_proc_read_vg_info; - pde->data = vg_ptr; - } - pde = create_proc_entry(LVM_LV_SUBDIR, S_IFDIR, - vg_ptr->vg_dir_pde); - if ( pde != NULL) { - vg_ptr->lv_subdir_pde = pde; - for ( l = 0; l < vg_ptr->lv_max; l++) { - if ( ( lv_ptr = vg_ptr->lv[l]) == NULL) continue; - lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr); - } - } - pde = create_proc_entry(LVM_PV_SUBDIR, S_IFDIR, - vg_ptr->vg_dir_pde); - if ( pde != NULL) { - vg_ptr->pv_subdir_pde = pde; - for ( p = 0; p < vg_ptr->pv_max; p++) { - if ( ( pv_ptr = vg_ptr->pv[p]) == NULL) continue; - lvm_do_create_proc_entry_of_pv ( vg_ptr, pv_ptr); - } - } - } -} - -/* - * remove a /proc entry for a volume group - */ -void lvm_do_remove_proc_entry_of_vg ( vg_t *vg_ptr) { - int l, p; - lv_t *lv_ptr; - pv_t *pv_ptr; - - for ( l = 0; l < vg_ptr->lv_max; l++) { - if ( ( lv_ptr = vg_ptr->lv[l]) == NULL) continue; - lvm_do_remove_proc_entry_of_lv ( vg_ptr, vg_ptr->lv[l]); - } - for ( p = 0; p < vg_ptr->pv_max; p++) { - if ( ( pv_ptr = vg_ptr->pv[p]) == NULL) continue; - lvm_do_remove_proc_entry_of_pv ( vg_ptr, vg_ptr->pv[p]); - } - if ( vg_ptr->vg_dir_pde != NULL) { - remove_proc_entry(LVM_LV_SUBDIR, vg_ptr->vg_dir_pde); - remove_proc_entry(LVM_PV_SUBDIR, vg_ptr->vg_dir_pde); - remove_proc_entry("group", vg_ptr->vg_dir_pde); - remove_proc_entry(vg_ptr->vg_name, lvm_proc_vg_subdir); - } -} - - /* * support function initialize gendisk variables */ @@ -3044,32 +2439,6 @@ return; } /* lvm_gen_init() */ - - -/* - * return a pointer to a '-' padded uuid - */ -static char *lvm_show_uuid ( char *uuidstr) { - int i, j; - static char uuid[NAME_LEN] = { 0, }; - - memset ( uuid, 0, NAME_LEN); - - i = 6; - memcpy ( uuid, uuidstr, i); - uuidstr += i; - - for ( j = 0; j < 6; j++) { - uuid[i++] = '-'; - memcpy ( &uuid[i], uuidstr, 4); - uuidstr += 4; - i += 4; - } - - memcpy ( &uuid[i], uuidstr, 2 ); - - return uuid; -} module_init(lvm_init); module_exit(lvm_cleanup); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/md/md.c linux.ac/drivers/md/md.c --- linux.vanilla/drivers/md/md.c Sat May 26 16:53:05 2001 +++ linux.ac/drivers/md/md.c Sat May 26 17:05:24 2001 @@ -3912,4 +3912,3 @@ MD_EXPORT_SYMBOL(md_interrupt_thread); MD_EXPORT_SYMBOL(mddev_map); MD_EXPORT_SYMBOL(md_check_ordering); - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/Config.in linux.ac/drivers/media/video/Config.in --- linux.vanilla/drivers/media/video/Config.in Sat Feb 17 00:02:36 2001 +++ linux.ac/drivers/media/video/Config.in Tue Apr 3 23:52:23 2001 @@ -21,6 +21,13 @@ dep_tristate ' QuickCam Colour Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT fi fi +if [ "$CONFIG_PARPORT" != "n" ]; then + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_PARPORT_1284" != "n" ]; then + dep_tristate ' Winbond W9966CF Webcam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_W9966 $CONFIG_VIDEO_DEV $CONFIG_PARPORT + fi + fi +fi dep_tristate ' CPiA Video For Linux' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV if [ "$CONFIG_VIDEO_CPIA" != "n" ]; then if [ "$CONFIG_PARPORT_1284" != "n" ]; then @@ -39,7 +46,6 @@ dep_tristate ' Stradis 4:2:2 MPEG-2 video driver (EXPERIMENTAL)' CONFIG_VIDEO_STRADIS $CONFIG_VIDEO_DEV $CONFIG_PCI fi dep_tristate ' Zoran ZR36057/36060 Video For Linux' CONFIG_VIDEO_ZORAN $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_I2C -dep_tristate ' Include support for Iomega Buz' CONFIG_VIDEO_BUZ $CONFIG_VIDEO_ZORAN dep_tristate ' Zoran ZR36120/36125 Video For Linux' CONFIG_VIDEO_ZR36120 $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_I2C endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/Makefile linux.ac/drivers/media/video/Makefile --- linux.vanilla/drivers/media/video/Makefile Fri Dec 29 22:07:22 2000 +++ linux.ac/drivers/media/video/Makefile Sun Apr 15 15:12:50 2001 @@ -38,12 +38,13 @@ tda7432.o tda9875.o tuner.o obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o -obj-$(CONFIG_VIDEO_ZR36120) += zoran.o i2c-old.o tuner.o saa7110.o saa7111.o saa7185.o +obj-$(CONFIG_VIDEO_ZR36120) += zoran.o obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o i2c-old.o obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o i2c-old.o obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o -obj-$(CONFIG_VIDEO_ZORAN) += buz.o i2c-old.o saa7110.o saa7111.o saa7185.o +obj-$(CONFIG_VIDEO_W9966) += w9966.o +obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o i2c-old.o saa7110.o saa7111.o saa7185.o adv7175.o bt819.o bt856.o obj-$(CONFIG_VIDEO_LML33) += bt856.o bt819.o obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_PLANB) += planb.o diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/buz.c linux.ac/drivers/media/video/buz.c --- linux.vanilla/drivers/media/video/buz.c Tue Apr 3 17:32:08 2001 +++ linux.ac/drivers/media/video/buz.c Thu Jan 1 01:00:00 1970 @@ -1,3463 +0,0 @@ -/* - buz - Iomega Buz driver version 1.0 - - Copyright (C) 1999 Rainer Johanni - - based on - - buz.0.0.3 Copyright (C) 1998 Dave Perks - - and - - bttv - Bt848 frame grabber driver - - Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) - & Marcus Metzler (mocm@thp.uni-koeln.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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include "buz.h" -#include -#include - -#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | /* ZR36057_ISR_GIRQ1 | ZR36057_ISR_CodRepIRQ | */ ZR36057_ISR_JPEGRepIRQ ) -#define GPIO_MASK 0xdf - -/* - - BUZ - - GPIO0 = 1, take board out of reset - GPIO1 = 1, take JPEG codec out of sleep mode - GPIO3 = 1, deassert FRAME# to 36060 - - - GIRQ0 signals a vertical sync of the video signal - GIRQ1 signals that ZR36060's DATERR# line is asserted. - - SAA7111A - - In their infinite wisdom, the Iomega engineers decided to - use the same input line for composite and S-Video Color, - although there are two entries not connected at all! - Through this ingenious strike, it is not possible to - keep two running video sources connected at the same time - to Composite and S-VHS input! - - mode 0 - N/C - mode 1 - S-Video Y - mode 2 - noise or something I don't know - mode 3 - Composite and S-Video C - mode 4 - N/C - mode 5 - S-Video (gain C independently selectable of gain Y) - mode 6 - N/C - mode 7 - S-Video (gain C adapted to gain Y) - */ - -#define MAJOR_VERSION 1 /* driver major version */ -#define MINOR_VERSION 0 /* driver minor version */ - -#define BUZ_NAME "Iomega BUZ V-1.0" /* name of the driver */ - -#define DEBUG(x) /* Debug driver */ -#define IDEBUG(x) /* Debug interrupt handler */ -#define IOCTL_DEBUG(x) - - -/* The parameters for this driver */ - -/* - The video mem address of the video card. - The driver has a little database for some videocards - to determine it from there. If your video card is not in there - you have either to give it to the driver as a parameter - or set in in a VIDIOCSFBUF ioctl - */ - -static unsigned long vidmem; /* Video memory base address (default 0) */ - -/* Special purposes only: */ - -static int triton; /* 0=no (default), 1=yes */ -static int natoma; /* 0=no (default), 1=yes */ - -/* - Number and size of grab buffers for Video 4 Linux - The vast majority of applications should not need more than 2, - the very popular BTTV driver actually does ONLY have 2. - Time sensitive applications might need more, the maximum - is VIDEO_MAX_FRAME (defined in ). - - The size is set so that the maximum possible request - can be satisfied. Decrease it, if bigphys_area alloc'd - memory is low. If you don't have the bigphys_area patch, - set it to 128 KB. Will you allow only to grab small - images with V4L, but that's better than nothing. - - v4l_bufsize has to be given in KB ! - - */ - -static int v4l_nbufs = 2; -static int v4l_bufsize = 128; /* Everybody should be able to work with this setting */ - -/* - Default input and video norm at startup of the driver. - */ - -static int default_input; /* 0=Composite (default), 1=S-VHS */ -static int default_norm; /* 0=PAL (default), 1=NTSC */ - -MODULE_PARM(vidmem, "i"); -MODULE_PARM(triton, "i"); -MODULE_PARM(natoma, "i"); -MODULE_PARM(v4l_nbufs, "i"); -MODULE_PARM(v4l_bufsize, "i"); -MODULE_PARM(default_input, "i"); -MODULE_PARM(default_norm, "i"); - -/* Anybody who uses more than four? */ -#define BUZ_MAX 4 - -static int zoran_num; /* number of Buzs in use */ -static struct zoran zoran[BUZ_MAX]; - -/* forward references */ - -static void v4l_fbuffer_free(struct zoran *zr); -static void jpg_fbuffer_free(struct zoran *zr); -static void zoran_feed_stat_com(struct zoran *zr); - - - -/* - * Allocate the V4L grab buffers - * - * These have to be pysically contiguous. - * If v4l_bufsize <= KMALLOC_MAXSIZE we use kmalloc - */ - -static int v4l_fbuffer_alloc(struct zoran *zr) -{ - int i, off; - unsigned char *mem; - - for (i = 0; i < v4l_nbufs; i++) { - if (zr->v4l_gbuf[i].fbuffer) - printk(KERN_WARNING "%s: v4l_fbuffer_alloc: buffer %d allready allocated ?\n", zr->name, i); - - if (v4l_bufsize <= KMALLOC_MAXSIZE) { - /* Use kmalloc */ - - mem = (unsigned char *) kmalloc(v4l_bufsize, GFP_KERNEL); - if (mem == 0) { - printk(KERN_ERR "%s: kmalloc for V4L bufs failed\n", zr->name); - v4l_fbuffer_free(zr); - return -ENOBUFS; - } - zr->v4l_gbuf[i].fbuffer = mem; - zr->v4l_gbuf[i].fbuffer_phys = virt_to_phys(mem); - zr->v4l_gbuf[i].fbuffer_bus = virt_to_bus(mem); - for (off = 0; off < v4l_bufsize; off += PAGE_SIZE) - mem_map_reserve(virt_to_page(mem + off)); - DEBUG(printk(BUZ_INFO ": V4L frame %d mem 0x%x (bus: 0x%x=%d)\n", i, mem, virt_to_bus(mem), virt_to_bus(mem))); - } else { - v4l_fbuffer_free(zr); - return -ENOBUFS; - } - } - - return 0; -} - -/* free the V4L grab buffers */ -static void v4l_fbuffer_free(struct zoran *zr) -{ - int i, off; - unsigned char *mem; - - for (i = 0; i < v4l_nbufs; i++) { - if (!zr->v4l_gbuf[i].fbuffer) - continue; - - mem = zr->v4l_gbuf[i].fbuffer; - for (off = 0; off < v4l_bufsize; off += PAGE_SIZE) - mem_map_unreserve(virt_to_page(mem + off)); - kfree((void *) zr->v4l_gbuf[i].fbuffer); - zr->v4l_gbuf[i].fbuffer = NULL; - } -} - -/* - * Allocate the MJPEG grab buffers. - * - * If the requested buffer size is smaller than KMALLOC_MAXSIZE, - * kmalloc is used to request a physically contiguous area, - * else we allocate the memory in framgents with get_free_page. - * - * If a Natoma chipset is present and this is a revision 1 zr36057, - * each MJPEG buffer needs to be physically contiguous. - * (RJ: This statement is from Dave Perks' original driver, - * I could never check it because I have a zr36067) - * The driver cares about this because it reduces the buffer - * size to KMALLOC_MAXSIZE in that case (which forces contiguous allocation). - * - * RJ: The contents grab buffers needs never be accessed in the driver. - * Therefore there is no need to allocate them with vmalloc in order - * to get a contiguous virtual memory space. - * I don't understand why many other drivers first allocate them with - * vmalloc (which uses internally also get_free_page, but delivers you - * virtual addresses) and then again have to make a lot of efforts - * to get the physical address. - * - */ - -static int jpg_fbuffer_alloc(struct zoran *zr) -{ - int i, j, off, alloc_contig; - unsigned long mem; - - /* Decide if we should alloc contiguous or fragmented memory */ - /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */ - - alloc_contig = (zr->jpg_bufsize < KMALLOC_MAXSIZE); - - for (i = 0; i < zr->jpg_nbufs; i++) { - if (zr->jpg_gbuf[i].frag_tab) - printk(KERN_WARNING "%s: jpg_fbuffer_alloc: buffer %d allready allocated ???\n", zr->name, i); - - /* Allocate fragment table for this buffer */ - - mem = get_free_page(GFP_KERNEL); - if (mem == 0) { - printk(KERN_ERR "%s: jpg_fbuffer_alloc: get_free_page (frag_tab) failed for buffer %d\n", zr->name, i); - jpg_fbuffer_free(zr); - return -ENOBUFS; - } - memset((void *) mem, 0, PAGE_SIZE); - zr->jpg_gbuf[i].frag_tab = (u32 *) mem; - zr->jpg_gbuf[i].frag_tab_bus = virt_to_bus((void *) mem); - - if (alloc_contig) { - mem = (unsigned long) kmalloc(zr->jpg_bufsize, GFP_KERNEL); - if (mem == 0) { - jpg_fbuffer_free(zr); - return -ENOBUFS; - } - zr->jpg_gbuf[i].frag_tab[0] = virt_to_bus((void *) mem); - zr->jpg_gbuf[i].frag_tab[1] = ((zr->jpg_bufsize / 4) << 1) | 1; - for (off = 0; off < zr->jpg_bufsize; off += PAGE_SIZE) - mem_map_reserve(virt_to_page(mem + off)); - } else { - /* jpg_bufsize is alreay page aligned */ - for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) { - mem = get_free_page(GFP_KERNEL); - if (mem == 0) { - jpg_fbuffer_free(zr); - return -ENOBUFS; - } - zr->jpg_gbuf[i].frag_tab[2 * j] = virt_to_bus((void *) mem); - zr->jpg_gbuf[i].frag_tab[2 * j + 1] = (PAGE_SIZE / 4) << 1; - mem_map_reserve(virt_to_page(mem)); - } - - zr->jpg_gbuf[i].frag_tab[2 * j - 1] |= 1; - } - } - - DEBUG(printk("jpg_fbuffer_alloc: %d KB allocated\n", - (zr->jpg_nbufs * zr->jpg_bufsize) >> 10)); - zr->jpg_buffers_allocated = 1; - return 0; -} - -/* free the MJPEG grab buffers */ -static void jpg_fbuffer_free(struct zoran *zr) -{ - int i, j, off, alloc_contig; - unsigned char *mem; - - /* Decide if we should alloc contiguous or fragmented memory */ - /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */ - - alloc_contig = (zr->jpg_bufsize < KMALLOC_MAXSIZE); - - for (i = 0; i < zr->jpg_nbufs; i++) { - if (!zr->jpg_gbuf[i].frag_tab) - continue; - - if (alloc_contig) { - if (zr->jpg_gbuf[i].frag_tab[0]) { - mem = (unsigned char *) bus_to_virt(zr->jpg_gbuf[i].frag_tab[0]); - for (off = 0; off < zr->jpg_bufsize; off += PAGE_SIZE) - mem_map_unreserve(virt_to_page(mem + off)); - kfree((void *) mem); - zr->jpg_gbuf[i].frag_tab[0] = 0; - zr->jpg_gbuf[i].frag_tab[1] = 0; - } - } else { - for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) { - if (!zr->jpg_gbuf[i].frag_tab[2 * j]) - break; - mem_map_unreserve(virt_to_page(bus_to_virt(zr->jpg_gbuf[i].frag_tab[2 * j]))); - free_page((unsigned long) bus_to_virt(zr->jpg_gbuf[i].frag_tab[2 * j])); - zr->jpg_gbuf[i].frag_tab[2 * j] = 0; - zr->jpg_gbuf[i].frag_tab[2 * j + 1] = 0; - } - } - - free_page((unsigned long) zr->jpg_gbuf[i].frag_tab); - zr->jpg_gbuf[i].frag_tab = NULL; - } - zr->jpg_buffers_allocated = 0; -} - - -/* ----------------------------------------------------------------------- */ - -/* I2C functions */ - -#define I2C_DELAY 10 - - -/* software I2C functions */ - -static void i2c_setlines(struct i2c_bus *bus, int ctrl, int data) -{ - struct zoran *zr = (struct zoran *) bus->data; - btwrite((data << 1) | ctrl, ZR36057_I2CBR); - btread(ZR36057_I2CBR); - udelay(I2C_DELAY); -} - -static int i2c_getdataline(struct i2c_bus *bus) -{ - struct zoran *zr = (struct zoran *) bus->data; - return (btread(ZR36057_I2CBR) >> 1) & 1; -} - -static void attach_inform(struct i2c_bus *bus, int id) -{ - DEBUG(struct zoran *zr = (struct zoran *) bus->data); - DEBUG(printk(BUZ_DEBUG "-%u: i2c attach %02x\n", zr->id, id)); -} - -static void detach_inform(struct i2c_bus *bus, int id) -{ - DEBUG(struct zoran *zr = (struct zoran *) bus->data); - DEBUG(printk(BUZ_DEBUG "-%u: i2c detach %02x\n", zr->id, id)); -} - -static struct i2c_bus zoran_i2c_bus_template = { - name: "zr36057", - id: I2C_BUSID_BT848, - bus_lock: SPIN_LOCK_UNLOCKED, - - attach_inform: attach_inform, - detach_inform: detach_inform, - - i2c_setlines: i2c_setlines, - i2c_getdataline: i2c_getdataline, -}; - - -/* ----------------------------------------------------------------------- */ - -static void GPIO(struct zoran *zr, unsigned bit, unsigned value) -{ - u32 reg; - u32 mask; - - mask = 1 << (24 + bit); - reg = btread(ZR36057_GPPGCR1) & ~mask; - if (value) { - reg |= mask; - } - btwrite(reg, ZR36057_GPPGCR1); - /* Stop any PCI posting on the GPIO bus */ - btread(ZR36057_I2CBR); -} - - -/* - * Set the registers for the size we have specified. Don't bother - * trying to understand this without the ZR36057 manual in front of - * you [AC]. - * - * PS: The manual is free for download in .pdf format from - * www.zoran.com - nicely done those folks. - */ - -struct tvnorm { - u16 Wt, Wa, Ht, Ha, HStart, VStart; -}; - -static struct tvnorm tvnorms[] = -{ - /* PAL-BDGHI */ - {864, 720, 625, 576, 31, 16}, - /* NTSC */ - {858, 720, 525, 480, 21, 8}, -}; -#define TVNORMS (sizeof(tvnorms) / sizeof(tvnorm)) - -static int format2bpp(int format) -{ - int bpp; - - /* Determine the number of bytes per pixel for the video format requested */ - - switch (format) { - - case VIDEO_PALETTE_YUV422: - bpp = 2; - break; - - case VIDEO_PALETTE_RGB555: - bpp = 2; - break; - - case VIDEO_PALETTE_RGB565: - bpp = 2; - break; - - case VIDEO_PALETTE_RGB24: - bpp = 3; - break; - - case VIDEO_PALETTE_RGB32: - bpp = 4; - break; - - default: - bpp = 0; - } - - return bpp; -} - -/* - * set geometry - */ -static void zr36057_set_vfe(struct zoran *zr, int video_width, int video_height, - unsigned int video_format) -{ - struct tvnorm *tvn; - unsigned HStart, HEnd, VStart, VEnd; - unsigned DispMode; - unsigned VidWinWid, VidWinHt; - unsigned hcrop1, hcrop2, vcrop1, vcrop2; - unsigned Wa, We, Ha, He; - unsigned X, Y, HorDcm, VerDcm; - u32 reg; - unsigned mask_line_size; - - if (zr->params.norm < 0 || zr->params.norm > 1) { - printk(KERN_ERR "%s: set_vfe: video_norm = %d not valid\n", zr->name, zr->params.norm); - return; - } - if (video_width < BUZ_MIN_WIDTH || video_height < BUZ_MIN_HEIGHT) { - printk(KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n", zr->name, video_width, video_height); - return; - } - tvn = &tvnorms[zr->params.norm]; - - Wa = tvn->Wa; - Ha = tvn->Ha; - - /* if window has more than half of active height, - switch on interlacing - we want the full information */ - - zr->video_interlace = (video_height > Ha / 2); - -/**** zr36057 ****/ - - /* horizontal */ - VidWinWid = video_width; - X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa; - We = (VidWinWid * 64) / X; - HorDcm = 64 - X; - hcrop1 = 2 * ((tvn->Wa - We) / 4); - hcrop2 = tvn->Wa - We - hcrop1; - HStart = tvn->HStart | 1; - HEnd = HStart + tvn->Wa - 1; - HStart += hcrop1; - HEnd -= hcrop2; - reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart) - | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd); - reg |= ZR36057_VFEHCR_HSPol; - btwrite(reg, ZR36057_VFEHCR); - - /* Vertical */ - DispMode = !zr->video_interlace; - VidWinHt = DispMode ? video_height : video_height / 2; - Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha; - He = (VidWinHt * 64) / Y; - VerDcm = 64 - Y; - vcrop1 = (tvn->Ha / 2 - He) / 2; - vcrop2 = tvn->Ha / 2 - He - vcrop1; - VStart = tvn->VStart; - VEnd = VStart + tvn->Ha / 2 - 1; - VStart += vcrop1; - VEnd -= vcrop2; - reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart) - | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd); - reg |= ZR36057_VFEVCR_VSPol; - btwrite(reg, ZR36057_VFEVCR); - - /* scaler and pixel format */ - reg = 0 // ZR36057_VFESPFR_ExtFl /* Trying to live without ExtFl */ - | (HorDcm << ZR36057_VFESPFR_HorDcm) - | (VerDcm << ZR36057_VFESPFR_VerDcm) - | (DispMode << ZR36057_VFESPFR_DispMode) - | ZR36057_VFESPFR_LittleEndian; - /* RJ: I don't know, why the following has to be the opposite - of the corresponding ZR36060 setting, but only this way - we get the correct colors when uncompressing to the screen */ - reg |= ZR36057_VFESPFR_VCLKPol; - /* RJ: Don't know if that is needed for NTSC also */ - reg |= ZR36057_VFESPFR_TopField; - switch (video_format) { - - case VIDEO_PALETTE_YUV422: - reg |= ZR36057_VFESPFR_YUV422; - break; - - case VIDEO_PALETTE_RGB555: - reg |= ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ErrDif; - break; - - case VIDEO_PALETTE_RGB565: - reg |= ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ErrDif; - break; - - case VIDEO_PALETTE_RGB24: - reg |= ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_Pack24; - break; - - case VIDEO_PALETTE_RGB32: - reg |= ZR36057_VFESPFR_RGB888; - break; - - default: - printk(KERN_INFO "%s: Unknown color_fmt=%x\n", zr->name, video_format); - return; - - } - if (HorDcm >= 48) { - reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */ - } else if (HorDcm >= 32) { - reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */ - } else if (HorDcm >= 16) { - reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */ - } - btwrite(reg, ZR36057_VFESPFR); - - /* display configuration */ - - reg = (16 << ZR36057_VDCR_MinPix) - | (VidWinHt << ZR36057_VDCR_VidWinHt) - | (VidWinWid << ZR36057_VDCR_VidWinWid); - if (triton) - reg &= ~ZR36057_VDCR_Triton; - else - reg |= ZR36057_VDCR_Triton; - btwrite(reg, ZR36057_VDCR); - - /* Write overlay clipping mask data, but don't enable overlay clipping */ - /* RJ: since this makes only sense on the screen, we use - zr->window.width instead of video_width */ - - mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; - reg = virt_to_bus(zr->overlay_mask); - btwrite(reg, ZR36057_MMTR); - reg = virt_to_bus(zr->overlay_mask + mask_line_size); - btwrite(reg, ZR36057_MMBR); - reg = mask_line_size - (zr->window.width + 31) / 32; - if (DispMode == 0) - reg += mask_line_size; - reg <<= ZR36057_OCR_MaskStride; - btwrite(reg, ZR36057_OCR); - -} - -/* - * Switch overlay on or off - */ - -static void zr36057_overlay(struct zoran *zr, int on) -{ - int fmt, bpp; - u32 reg; - - if (on) { - /* do the necessary settings ... */ - - btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */ - - switch (zr->buffer.depth) { - case 15: - fmt = VIDEO_PALETTE_RGB555; - bpp = 2; - break; - case 16: - fmt = VIDEO_PALETTE_RGB565; - bpp = 2; - break; - case 24: - fmt = VIDEO_PALETTE_RGB24; - bpp = 3; - break; - case 32: - fmt = VIDEO_PALETTE_RGB32; - bpp = 4; - break; - default: - fmt = 0; - bpp = 0; - } - - zr36057_set_vfe(zr, zr->window.width, zr->window.height, fmt); - - /* Start and length of each line MUST be 4-byte aligned. - This should be allready checked before the call to this routine. - All error messages are internal driver checking only! */ - - /* video display top and bottom registers */ - - reg = (u32) zr->buffer.base - + zr->window.x * bpp - + zr->window.y * zr->buffer.bytesperline; - btwrite(reg, ZR36057_VDTR); - if (reg & 3) - printk(KERN_ERR "%s: zr36057_overlay: video_address not aligned\n", zr->name); - if (zr->video_interlace) - reg += zr->buffer.bytesperline; - btwrite(reg, ZR36057_VDBR); - - /* video stride, status, and frame grab register */ - - reg = zr->buffer.bytesperline - zr->window.width * bpp; - if (zr->video_interlace) - reg += zr->buffer.bytesperline; - if (reg & 3) - printk(KERN_ERR "%s: zr36057_overlay: video_stride not aligned\n", zr->name); - reg = (reg << ZR36057_VSSFGR_DispStride); - reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */ - btwrite(reg, ZR36057_VSSFGR); - - /* Set overlay clipping */ - - if (zr->window.clipcount) - btor(ZR36057_OCR_OvlEnable, ZR36057_OCR); - - /* ... and switch it on */ - - btor(ZR36057_VDCR_VidEn, ZR36057_VDCR); - } else { - /* Switch it off */ - - btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); - } -} - -/* - * The overlay mask has one bit for each pixel on a scan line, - * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels. - */ -static void write_overlay_mask(struct zoran *zr, struct video_clip *vp, int count) -{ - unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; - u32 *mask; - int x, y, width, height; - unsigned i, j, k; - u32 reg; - - /* fill mask with one bits */ - memset(zr->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT); - reg = 0; - - for (i = 0; i < count; ++i) { - /* pick up local copy of clip */ - x = vp[i].x; - y = vp[i].y; - width = vp[i].width; - height = vp[i].height; - - /* trim clips that extend beyond the window */ - if (x < 0) { - width += x; - x = 0; - } - if (y < 0) { - height += y; - y = 0; - } - if (x + width > zr->window.width) { - width = zr->window.width - x; - } - if (y + height > zr->window.height) { - height = zr->window.height - y; - } - /* ignore degenerate clips */ - if (height <= 0) { - continue; - } - if (width <= 0) { - continue; - } - /* apply clip for each scan line */ - for (j = 0; j < height; ++j) { - /* reset bit for each pixel */ - /* this can be optimized later if need be */ - mask = zr->overlay_mask + (y + j) * mask_line_size; - for (k = 0; k < width; ++k) { - mask[(x + k) / 32] &= ~((u32) 1 << (x + k) % 32); - } - } - } -} - -/* Enable/Disable uncompressed memory grabbing of the 36057 */ - -static void zr36057_set_memgrab(struct zoran *zr, int mode) -{ - if (mode) { - if (btread(ZR36057_VSSFGR) & (ZR36057_VSSFGR_SnapShot | ZR36057_VSSFGR_FrameGrab)) - printk(KERN_WARNING "%s: zr36057_set_memgrab_on with SnapShot or FrameGrab on ???\n", zr->name); - - /* switch on VSync interrupts */ - - btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts - - btor(ZR36057_ICR_GIRQ0, ZR36057_ICR); - - /* enable SnapShot */ - - btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); - - /* Set zr36057 video front end and enable video */ - -#ifdef XAWTV_HACK - zr36057_set_vfe(zr, zr->gwidth > 720 ? 720 : zr->gwidth, zr->gheight, zr->gformat); -#else - zr36057_set_vfe(zr, zr->gwidth, zr->gheight, zr->gformat); -#endif - - zr->v4l_memgrab_active = 1; - } else { - zr->v4l_memgrab_active = 0; - - /* switch off VSync interrupts */ - - btand(~ZR36057_ICR_GIRQ0, ZR36057_ICR); - - /* reenable grabbing to screen if it was running */ - - if (zr->v4l_overlay_active) { - zr36057_overlay(zr, 1); - } else { - btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); - btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); - } - } -} - -static int wait_grab_pending(struct zoran *zr) -{ - unsigned long flags; - - /* wait until all pending grabs are finished */ - - if (!zr->v4l_memgrab_active) - return 0; - - while (zr->v4l_pend_tail != zr->v4l_pend_head) { - interruptible_sleep_on(&zr->v4l_capq); - if (signal_pending(current)) - return -ERESTARTSYS; - } - - spin_lock_irqsave(&zr->lock, flags); - zr36057_set_memgrab(zr, 0); - spin_unlock_irqrestore(&zr->lock, flags); - - return 0; -} - -/* - * V4L Buffer grabbing - */ - -static int v4l_grab(struct zoran *zr, struct video_mmap *mp) -{ - unsigned long flags; - int res, bpp; - - /* - * There is a long list of limitations to what is allowed to be grabbed - * We don't output error messages her, since some programs (e.g. xawtv) - * just try several settings to find out what is valid or not. - */ - - /* No grabbing outside the buffer range! */ - - if (mp->frame >= v4l_nbufs || mp->frame < 0) - return -EINVAL; - - /* Check size and format of the grab wanted */ - - if (mp->height < BUZ_MIN_HEIGHT || mp->width < BUZ_MIN_WIDTH) - return -EINVAL; - if (mp->height > BUZ_MAX_HEIGHT || mp->width > BUZ_MAX_WIDTH) - return -EINVAL; - - bpp = format2bpp(mp->format); - if (bpp == 0) - return -EINVAL; - - /* Check against available buffer size */ - - if (mp->height * mp->width * bpp > v4l_bufsize) - return -EINVAL; - - /* The video front end needs 4-byte alinged line sizes */ - - if ((bpp == 2 && (mp->width & 1)) || (bpp == 3 && (mp->width & 3))) - return -EINVAL; - - /* - * To minimize the time spent in the IRQ routine, we avoid setting up - * the video front end there. - * If this grab has different parameters from a running streaming capture - * we stop the streaming capture and start it over again. - */ - - if (zr->v4l_memgrab_active && - (zr->gwidth != mp->width || zr->gheight != mp->height || zr->gformat != mp->format)) { - res = wait_grab_pending(zr); - if (res) - return res; - } - zr->gwidth = mp->width; - zr->gheight = mp->height; - zr->gformat = mp->format; - zr->gbpl = bpp * zr->gwidth; - - - spin_lock_irqsave(&zr->lock, flags); - - /* make sure a grab isn't going on currently with this buffer */ - - switch (zr->v4l_gbuf[mp->frame].state) { - - default: - case BUZ_STATE_PEND: - res = -EBUSY; /* what are you doing? */ - break; - - case BUZ_STATE_USER: - case BUZ_STATE_DONE: - /* since there is at least one unused buffer there's room for at least one more pend[] entry */ - zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = mp->frame; - zr->v4l_gbuf[mp->frame].state = BUZ_STATE_PEND; - res = 0; - break; - - } - - /* put the 36057 into frame grabbing mode */ - - if (!res && !zr->v4l_memgrab_active) - zr36057_set_memgrab(zr, 1); - - spin_unlock_irqrestore(&zr->lock, flags); - - return res; -} - -/* - * Sync on a V4L buffer - */ - -static int v4l_sync(struct zoran *zr, int frame) -{ - unsigned long flags; - - - /* check passed-in frame number */ - if (frame >= v4l_nbufs || frame < 0) { - printk(KERN_ERR "%s: v4l_sync: frame %d is invalid\n", zr->name, frame); - return -EINVAL; - } - /* Check if is buffer was queued at all */ - - if (zr->v4l_gbuf[frame].state == BUZ_STATE_USER) { -// printk(KERN_ERR "%s: v4l_sync: Trying to sync on a buffer which was not queued?\n", zr->name); - return -EINVAL; - } - /* wait on this buffer to get ready */ - - while (zr->v4l_gbuf[frame].state == BUZ_STATE_PEND) { - interruptible_sleep_on(&zr->v4l_capq); - if (signal_pending(current)) - return -ERESTARTSYS; - } - - /* buffer should now be in BUZ_STATE_DONE */ - - if (zr->v4l_gbuf[frame].state != BUZ_STATE_DONE) - printk(KERN_ERR "%s: v4l_sync - internal error\n", zr->name); - - /* Check if streaming capture has finished */ - - spin_lock_irqsave(&zr->lock, flags); - - if (zr->v4l_pend_tail == zr->v4l_pend_head) - zr36057_set_memgrab(zr, 0); - - spin_unlock_irqrestore(&zr->lock, flags); - - return 0; -} -/***************************************************************************** - * * - * Set up the Buz-specific MJPEG part * - * * - *****************************************************************************/ - -/* - * Wait til post office is no longer busy - */ - -static int post_office_wait(struct zoran *zr) -{ - u32 por; - u32 ct=0; - - while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) { - ct++; - if(ct>100000) - { - printk(KERN_ERR "%s: timeout on post office.\n", zr->name); - return -1; - } - /* wait for something to happen */ - } - if ((por & ZR36057_POR_POPen) != 0) { - printk(KERN_WARNING "%s: pop pending %08x\n", zr->name, por); - return -1; - } - if ((por & (ZR36057_POR_POTime | ZR36057_POR_POPen)) != 0) { - printk(KERN_WARNING "%s: pop timeout %08x\n", zr->name, por); - return -1; - } - return 0; -} - -static int post_office_write(struct zoran *zr, unsigned guest, unsigned reg, unsigned value) -{ - u32 por; - - post_office_wait(zr); - por = ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16) | (value & 0xFF); - btwrite(por, ZR36057_POR); - return post_office_wait(zr); -} - -static int post_office_read(struct zoran *zr, unsigned guest, unsigned reg) -{ - u32 por; - - post_office_wait(zr); - por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16); - btwrite(por, ZR36057_POR); - if (post_office_wait(zr) < 0) { - return -1; - } - return btread(ZR36057_POR) & 0xFF; -} - -static int zr36060_write_8(struct zoran *zr, unsigned reg, unsigned val) -{ - if (post_office_wait(zr) - || post_office_write(zr, 0, 1, reg >> 8) - || post_office_write(zr, 0, 2, reg)) { - return -1; - } - return post_office_write(zr, 0, 3, val); -} - -static int zr36060_write_16(struct zoran *zr, unsigned reg, unsigned val) -{ - if (zr36060_write_8(zr, reg + 0, val >> 8)) { - return -1; - } - return zr36060_write_8(zr, reg + 1, val >> 0); -} - -static int zr36060_write_24(struct zoran *zr, unsigned reg, unsigned val) -{ - if (zr36060_write_8(zr, reg + 0, val >> 16)) { - return -1; - } - return zr36060_write_16(zr, reg + 1, val >> 0); -} - -static int zr36060_write_32(struct zoran *zr, unsigned reg, unsigned val) -{ - if (zr36060_write_16(zr, reg + 0, val >> 16)) { - return -1; - } - return zr36060_write_16(zr, reg + 2, val >> 0); -} - -static u32 zr36060_read_8(struct zoran *zr, unsigned reg) -{ - if (post_office_wait(zr) - || post_office_write(zr, 0, 1, reg >> 8) - || post_office_write(zr, 0, 2, reg)) { - return -1; - } - return post_office_read(zr, 0, 3) & 0xFF; -} - -static int zr36060_reset(struct zoran *zr) -{ - return post_office_write(zr, 3, 0, 0); -} - -static void zr36060_sleep(struct zoran *zr, int sleep) -{ - GPIO(zr, 1, !sleep); -} - - -static void zr36060_set_jpg(struct zoran *zr, enum zoran_codec_mode mode) -{ - struct tvnorm *tvn; - u32 reg; - int size; - - reg = (1 << 0) /* CodeMstr */ - |(0 << 2) /* CFIS=0 */ - |(0 << 6) /* Endian=0 */ - |(0 << 7); /* Code16=0 */ - zr36060_write_8(zr, 0x002, reg); - - switch (mode) { - - case BUZ_MODE_MOTION_DECOMPRESS: - case BUZ_MODE_STILL_DECOMPRESS: - reg = 0x00; /* Codec mode = decompression */ - break; - - case BUZ_MODE_MOTION_COMPRESS: - case BUZ_MODE_STILL_COMPRESS: - default: - reg = 0xa4; /* Codec mode = compression with variable scale factor */ - break; - - } - zr36060_write_8(zr, 0x003, reg); - - reg = 0x00; /* reserved, mbz */ - zr36060_write_8(zr, 0x004, reg); - - reg = 0xff; /* 510 bits/block */ - zr36060_write_8(zr, 0x005, reg); - - /* JPEG markers */ - reg = (zr->params.jpeg_markers) & 0x38; /* DRI, DQT, DHT */ - if (zr->params.COM_len) - reg |= JPEG_MARKER_COM; - if (zr->params.APP_len) - reg |= JPEG_MARKER_APP; - zr36060_write_8(zr, 0x006, reg); - - reg = (0 << 3) /* DATERR=0 */ - |(0 << 2) /* END=0 */ - |(0 << 1) /* EOI=0 */ - |(0 << 0); /* EOAV=0 */ - zr36060_write_8(zr, 0x007, reg); - - /* code volume */ - - /* Target field size in pixels: */ - tvn = &tvnorms[zr->params.norm]; - size = (tvn->Ha / 2) * (tvn->Wa) / (zr->params.HorDcm) / (zr->params.VerDcm); - - /* Target compressed field size in bits: */ - size = size * 16; /* uncompressed size in bits */ - size = size * zr->params.quality / 400; /* quality = 100 is a compression ratio 1:4 */ - - /* Lower limit (arbitrary, 1 KB) */ - if (size < 8192) - size = 8192; - - /* Upper limit: 7/8 of the code buffers */ - if (size * zr->params.field_per_buff > zr->jpg_bufsize * 7) - size = zr->jpg_bufsize * 7 / zr->params.field_per_buff; - - reg = size; - zr36060_write_32(zr, 0x009, reg); - - /* how do we set initial SF as a function of quality parameter? */ - reg = 0x0100; /* SF=1.0 */ - zr36060_write_16(zr, 0x011, reg); - - reg = 0x00ffffff; /* AF=max */ - zr36060_write_24(zr, 0x013, reg); - - reg = 0x0000; /* test */ - zr36060_write_16(zr, 0x024, reg); -} - -static void zr36060_set_video(struct zoran *zr, enum zoran_codec_mode mode) -{ - struct tvnorm *tvn; - u32 reg; - - reg = (0 << 7) /* Video8=0 */ - |(0 << 6) /* Range=0 */ - |(0 << 3) /* FlDet=0 */ - |(1 << 2) /* FlVedge=1 */ - |(0 << 1) /* FlExt=0 */ - |(0 << 0); /* SyncMstr=0 */ - - /* According to ZR36067 documentation, FlDet should correspond - to the odd_even flag of the ZR36067 */ - if (zr->params.odd_even) - reg |= (1 << 3); - - if (mode != BUZ_MODE_STILL_DECOMPRESS) { - /* limit pixels to range 16..235 as per CCIR-601 */ - reg |= (1 << 6); /* Range=1 */ - } - zr36060_write_8(zr, 0x030, reg); - - reg = (0 << 7) /* VCLKPol=0 */ - |(0 << 6) /* PValPol=0 */ - |(1 << 5) /* PoePol=1 */ - |(0 << 4) /* SImgPol=0 */ - |(0 << 3) /* BLPol=0 */ - |(0 << 2) /* FlPol=0 */ - |(0 << 1) /* HSPol=0, sync on falling edge */ - |(1 << 0); /* VSPol=1 */ - zr36060_write_8(zr, 0x031, reg); - - switch (zr->params.HorDcm) { - default: - case 1: - reg = (0 << 0); - break; /* HScale = 0 */ - - case 2: - reg = (1 << 0); - break; /* HScale = 1 */ - - case 4: - reg = (2 << 0); - break; /* HScale = 2 */ - } - if (zr->params.VerDcm == 2) - reg |= (1 << 2); - zr36060_write_8(zr, 0x032, reg); - - reg = 0x80; /* BackY */ - zr36060_write_8(zr, 0x033, reg); - - reg = 0xe0; /* BackU */ - zr36060_write_8(zr, 0x034, reg); - - reg = 0xe0; /* BackV */ - zr36060_write_8(zr, 0x035, reg); - - /* sync generator */ - - tvn = &tvnorms[zr->params.norm]; - - reg = tvn->Ht - 1; /* Vtotal */ - zr36060_write_16(zr, 0x036, reg); - - reg = tvn->Wt - 1; /* Htotal */ - zr36060_write_16(zr, 0x038, reg); - - reg = 6 - 1; /* VsyncSize */ - zr36060_write_8(zr, 0x03a, reg); - - reg = 100 - 1; /* HsyncSize */ - zr36060_write_8(zr, 0x03b, reg); - - reg = tvn->VStart - 1; /* BVstart */ - zr36060_write_8(zr, 0x03c, reg); - - reg += tvn->Ha / 2; /* BVend */ - zr36060_write_16(zr, 0x03e, reg); - - reg = tvn->HStart - 1; /* BHstart */ - zr36060_write_8(zr, 0x03d, reg); - - reg += tvn->Wa; /* BHend */ - zr36060_write_16(zr, 0x040, reg); - - /* active area */ - reg = zr->params.img_y + tvn->VStart; /* Vstart */ - zr36060_write_16(zr, 0x042, reg); - - reg += zr->params.img_height; /* Vend */ - zr36060_write_16(zr, 0x044, reg); - - reg = zr->params.img_x + tvn->HStart; /* Hstart */ - zr36060_write_16(zr, 0x046, reg); - - reg += zr->params.img_width; /* Hend */ - zr36060_write_16(zr, 0x048, reg); - - /* subimage area */ - reg = zr->params.img_y + tvn->VStart; /* SVstart */ - zr36060_write_16(zr, 0x04a, reg); - - reg += zr->params.img_height; /* SVend */ - zr36060_write_16(zr, 0x04c, reg); - - reg = zr->params.img_x + tvn->HStart; /* SHstart */ - zr36060_write_16(zr, 0x04e, reg); - - reg += zr->params.img_width; /* SHend */ - zr36060_write_16(zr, 0x050, reg); -} - -static void zr36060_set_jpg_SOF(struct zoran *zr) -{ - u32 reg; - - - reg = 0xffc0; /* SOF marker */ - zr36060_write_16(zr, 0x060, reg); - - reg = 17; /* SOF length */ - zr36060_write_16(zr, 0x062, reg); - - reg = 8; /* precision 8 bits */ - zr36060_write_8(zr, 0x064, reg); - - reg = zr->params.img_height / zr->params.VerDcm; /* image height */ - zr36060_write_16(zr, 0x065, reg); - - reg = zr->params.img_width / zr->params.HorDcm; /* image width */ - zr36060_write_16(zr, 0x067, reg); - - reg = 3; /* 3 color components */ - zr36060_write_8(zr, 0x069, reg); - - reg = 0x002100; /* Y component */ - zr36060_write_24(zr, 0x06a, reg); - - reg = 0x011101; /* U component */ - zr36060_write_24(zr, 0x06d, reg); - - reg = 0x021101; /* V component */ - zr36060_write_24(zr, 0x070, reg); -} - -static void zr36060_set_jpg_SOS(struct zoran *zr) -{ - u32 reg; - - - reg = 0xffda; /* SOS marker */ - zr36060_write_16(zr, 0x07a, reg); - - reg = 12; /* SOS length */ - zr36060_write_16(zr, 0x07c, reg); - - reg = 3; /* 3 color components */ - zr36060_write_8(zr, 0x07e, reg); - - reg = 0x0000; /* Y component */ - zr36060_write_16(zr, 0x07f, reg); - - reg = 0x0111; /* U component */ - zr36060_write_16(zr, 0x081, reg); - - reg = 0x0211; /* V component */ - zr36060_write_16(zr, 0x083, reg); - - reg = 0x003f00; /* Start, end spectral scans */ - zr36060_write_24(zr, 0x085, reg); -} - -static void zr36060_set_jpg_DRI(struct zoran *zr) -{ - u32 reg; - - - reg = 0xffdd; /* DRI marker */ - zr36060_write_16(zr, 0x0c0, reg); - - reg = 4; /* DRI length */ - zr36060_write_16(zr, 0x0c2, reg); - - reg = 8; /* length in MCUs */ - zr36060_write_16(zr, 0x0c4, reg); -} - -static void zr36060_set_jpg_DQT(struct zoran *zr) -{ - unsigned i; - unsigned adr; - static const u8 dqt[] = - { - 0xff, 0xdb, /* DHT marker */ - 0x00, 0x84, /* DHT length */ - 0x00, /* table ID 0 */ - 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, - 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, - 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, - 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, - 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, - 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, - 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, - 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, - 0x01, /* table ID 1 */ - 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, - 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 - }; - - /* write fixed quantitization tables */ - adr = 0x0cc; - for (i = 0; i < sizeof(dqt); ++i) { - zr36060_write_8(zr, adr++, dqt[i]); - } -} - -static void zr36060_set_jpg_DHT(struct zoran *zr) -{ - unsigned i; - unsigned adr; - static const u8 dht[] = - { - 0xff, 0xc4, /* DHT marker */ - 0x01, 0xa2, /* DHT length */ - 0x00, /* table class 0, ID 0 */ - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, /* # codes of length 1..8 */ - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* # codes of length 8..16 */ - 0x00, /* values for codes of length 2 */ - 0x01, 0x02, 0x03, 0x04, 0x05, /* values for codes of length 3 */ - 0x06, /* values for codes of length 4 */ - 0x07, /* values for codes of length 5 */ - 0x08, /* values for codes of length 6 */ - 0x09, /* values for codes of length 7 */ - 0x0a, /* values for codes of length 8 */ - 0x0b, /* values for codes of length 9 */ - 0x01, /* table class 0, ID 1 */ - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* # codes of length 1..8 */ - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, /* # codes of length 9..16 */ - 0x00, 0x01, 0x02, /* values for codes of length 2 */ - 0x03, /* values for codes of length 3 */ - 0x04, /* values for codes of length 4 */ - 0x05, /* values for codes of length 5 */ - 0x06, /* values for codes of length 6 */ - 0x07, /* values for codes of length 7 */ - 0x08, /* values for codes of length 8 */ - 0x09, /* values for codes of length 9 */ - 0x0a, /* values for codes of length 10 */ - 0x0b, /* values for codes of length 11 */ - 0x10, - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, - 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, - 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, - 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, - 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, - 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, - 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, - 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, - 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, - 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, - 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, - 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, - 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, - 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, - 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, - 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, - 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, - 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, - 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, - 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, - 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, - 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, - 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, - 0xf6, 0xf7, 0xf8, 0xf9, 0xfa - }; - - /* write fixed Huffman tables */ - adr = 0x1d4; - for (i = 0; i < sizeof(dht); ++i) { - zr36060_write_8(zr, adr++, dht[i]); - } -} - -static void zr36060_set_jpg_APP(struct zoran *zr) -{ - unsigned adr; - int len, i; - u32 reg; - - - len = zr->params.APP_len; - if (len < 0) - len = 0; - if (len > 60) - len = 60; - - i = zr->params.APPn; - if (i < 0) - i = 0; - if (i > 15) - i = 15; - - reg = 0xffe0 + i; /* APPn marker */ - zr36060_write_16(zr, 0x380, reg); - - reg = len + 2; /* APPn len */ - zr36060_write_16(zr, 0x382, reg); - - /* write APPn data */ - adr = 0x384; - for (i = 0; i < 60; i++) { - zr36060_write_8(zr, adr++, (i < len ? zr->params.APP_data[i] : 0)); - } -} - -static void zr36060_set_jpg_COM(struct zoran *zr) -{ - unsigned adr; - int len, i; - u32 reg; - - - len = zr->params.COM_len; - if (len < 0) - len = 0; - if (len > 60) - len = 60; - - reg = 0xfffe; /* COM marker */ - zr36060_write_16(zr, 0x3c0, reg); - - reg = len + 2; /* COM len */ - zr36060_write_16(zr, 0x3c2, reg); - - /* write COM data */ - adr = 0x3c4; - for (i = 0; i < 60; i++) { - zr36060_write_8(zr, adr++, (i < len ? zr->params.COM_data[i] : 0)); - } -} - -static void zr36060_set_cap(struct zoran *zr, enum zoran_codec_mode mode) -{ - unsigned i; - u32 reg; - - zr36060_reset(zr); - mdelay(10); - - reg = (0 << 7) /* Load=0 */ - |(1 << 0); /* SynRst=1 */ - zr36060_write_8(zr, 0x000, reg); - - zr36060_set_jpg(zr, mode); - zr36060_set_video(zr, mode); - zr36060_set_jpg_SOF(zr); - zr36060_set_jpg_SOS(zr); - zr36060_set_jpg_DRI(zr); - zr36060_set_jpg_DQT(zr); - zr36060_set_jpg_DHT(zr); - zr36060_set_jpg_APP(zr); - zr36060_set_jpg_COM(zr); - - reg = (1 << 7) /* Load=1 */ - |(0 << 0); /* SynRst=0 */ - zr36060_write_8(zr, 0x000, reg); - - /* wait for codec to unbusy */ - for (i = 0; i < 1000; ++i) { - reg = zr36060_read_8(zr, 0x001); - if ((reg & (1 << 7)) == 0) { - DEBUG(printk(KERN_DEBUG "060: loaded, loops=%u\n", i)); - return; - } - udelay(1000); - } - printk(KERN_INFO "060: stuck busy, statux=%02x\n", reg); -} - -static void zr36057_set_jpg(struct zoran *zr, enum zoran_codec_mode mode) -{ - struct tvnorm *tvn; - u32 reg; - int i; - - tvn = &tvnorms[zr->params.norm]; - - /* assert P_Reset */ - btwrite(0, ZR36057_JPC); - - /* re-initialize DMA ring stuff */ - zr->jpg_que_head = 0; - zr->jpg_dma_head = 0; - zr->jpg_dma_tail = 0; - zr->jpg_que_tail = 0; - zr->jpg_seq_num = 0; - for (i = 0; i < BUZ_NUM_STAT_COM; ++i) { - zr->stat_com[i] = 1; /* mark as unavailable to zr36057 */ - } - for (i = 0; i < zr->jpg_nbufs; i++) { - zr->jpg_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */ - } - - /* MJPEG compression mode */ - switch (mode) { - - case BUZ_MODE_MOTION_COMPRESS: - default: - reg = ZR36057_JMC_MJPGCmpMode; - break; - - case BUZ_MODE_MOTION_DECOMPRESS: - reg = ZR36057_JMC_MJPGExpMode; - reg |= ZR36057_JMC_SyncMstr; - /* RJ: The following is experimental - improves the output to screen */ - if (zr->params.VFIFO_FB) - reg |= ZR36057_JMC_VFIFO_FB; - break; - - case BUZ_MODE_STILL_COMPRESS: - reg = ZR36057_JMC_JPGCmpMode; - break; - - case BUZ_MODE_STILL_DECOMPRESS: - reg = ZR36057_JMC_JPGExpMode; - break; - - } - reg |= ZR36057_JMC_JPG; - if (zr->params.field_per_buff == 1) - reg |= ZR36057_JMC_Fld_per_buff; - btwrite(reg, ZR36057_JMC); - - /* vertical */ - btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR); - reg = (6 << ZR36057_VSP_VsyncSize) | (tvn->Ht << ZR36057_VSP_FrmTot); - btwrite(reg, ZR36057_VSP); - reg = ((zr->params.img_y + tvn->VStart) << ZR36057_FVAP_NAY) - | (zr->params.img_height << ZR36057_FVAP_PAY); - btwrite(reg, ZR36057_FVAP); - - /* horizontal */ - btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR); - reg = ((tvn->Wt - 100) << ZR36057_HSP_HsyncStart) | (tvn->Wt << ZR36057_HSP_LineTot); - btwrite(reg, ZR36057_HSP); - reg = ((zr->params.img_x + tvn->HStart) << ZR36057_FHAP_NAX) - | (zr->params.img_width << ZR36057_FHAP_PAX); - btwrite(reg, ZR36057_FHAP); - - /* field process parameters */ - if (zr->params.odd_even) - reg = ZR36057_FPP_Odd_Even; - else - reg = 0; - btwrite(reg, ZR36057_FPP); - - /* Set proper VCLK Polarity, else colors will be wrong during playback */ - btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR); - - /* code base address and FIFO threshold */ - reg = virt_to_bus(zr->stat_com); - btwrite(reg, ZR36057_JCBA); - reg = 0x50; - btwrite(reg, ZR36057_JCFT); - - /* JPEG codec guest ID */ - reg = (1 << ZR36057_JCGI_JPEGuestID) | (0 << ZR36057_JCGI_JPEGuestReg); - btwrite(reg, ZR36057_JCGI); - - /* Code transfer guest ID */ - reg = (0 << ZR36057_MCTCR_CodGuestID) | (3 << ZR36057_MCTCR_CodGuestReg); - reg |= ZR36057_MCTCR_CFlush; - btwrite(reg, ZR36057_MCTCR); - - /* deassert P_Reset */ - btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC); -} - -static void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode) -{ - static int zero = 0; - static int one = 1; - - switch (mode) { - - case BUZ_MODE_MOTION_COMPRESS: - zr36060_set_cap(zr, mode); - zr36057_set_jpg(zr, mode); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &one); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &zero); - - /* deassert P_Reset, assert Code transfer enable */ - btwrite(IRQ_MASK, ZR36057_ISR); - btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR); - break; - - case BUZ_MODE_MOTION_DECOMPRESS: - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &zero); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &one); - zr36060_set_cap(zr, mode); - zr36057_set_jpg(zr, mode); - - /* deassert P_Reset, assert Code transfer enable */ - btwrite(IRQ_MASK, ZR36057_ISR); - btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR); - break; - - case BUZ_MODE_IDLE: - default: - /* shut down processing */ - btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); - btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC); - btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC); - btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC); - btand(~ZR36057_JMC_Go_en, ZR36057_JMC); - btwrite(0, ZR36057_ISR); - zr36060_reset(zr); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &one); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &zero); - break; - - } - zr->codec_mode = mode; -} - -/* - * Queue a MJPEG buffer for capture/playback - */ - -static int jpg_qbuf(struct zoran *zr, int frame, enum zoran_codec_mode mode) -{ - unsigned long flags; - int res; - - /* Check if buffers are allocated */ - - if (!zr->jpg_buffers_allocated) { - printk(KERN_ERR "%s: jpg_qbuf: buffers not yet allocated\n", zr->name); - return -ENOMEM; - } - /* Does the user want to stop streaming? */ - - if (frame < 0) { - if (zr->codec_mode == mode) { - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - return 0; - } else { - printk(KERN_ERR "%s: jpg_qbuf - stop streaming but not in streaming mode\n", zr->name); - return -EINVAL; - } - } - /* No grabbing outside the buffer range! */ - - if (frame >= zr->jpg_nbufs) { - printk(KERN_ERR "%s: jpg_qbuf: buffer %d out of range\n", zr->name, frame); - return -EINVAL; - } - /* what is the codec mode right now? */ - - if (zr->codec_mode == BUZ_MODE_IDLE) { - /* Ok load up the zr36060 and go */ - zr36057_enable_jpg(zr, mode); - } else if (zr->codec_mode != mode) { - /* wrong codec mode active - invalid */ - printk(KERN_ERR "%s: jpg_qbuf - codec in wrong mode\n", zr->name); - return -EINVAL; - } - spin_lock_irqsave(&zr->lock, flags); - - /* make sure a grab isn't going on currently with this buffer */ - - switch (zr->jpg_gbuf[frame].state) { - - default: - case BUZ_STATE_DMA: - case BUZ_STATE_PEND: - case BUZ_STATE_DONE: - res = -EBUSY; /* what are you doing? */ - break; - - case BUZ_STATE_USER: - /* since there is at least one unused buffer there's room for at least one more pend[] entry */ - zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = frame; - zr->jpg_gbuf[frame].state = BUZ_STATE_PEND; - zoran_feed_stat_com(zr); - res = 0; - break; - - } - - spin_unlock_irqrestore(&zr->lock, flags); - - /* Start the zr36060 when the first frame is queued */ - if (zr->jpg_que_head == 1) { - btor(ZR36057_JMC_Go_en, ZR36057_JMC); - btwrite(ZR36057_JPC_P_Reset | ZR36057_JPC_CodTrnsEn | ZR36057_JPC_Active, ZR36057_JPC); - } - return res; -} - -/* - * Sync on a MJPEG buffer - */ - -static int jpg_sync(struct zoran *zr, struct zoran_sync *bs) -{ - unsigned long flags; - int frame; - - if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS && - zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { - return -EINVAL; - } - while (zr->jpg_que_tail == zr->jpg_dma_tail) { - interruptible_sleep_on(&zr->jpg_capq); - if (signal_pending(current)) - return -ERESTARTSYS; - } - - spin_lock_irqsave(&zr->lock, flags); - - frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME]; - - /* buffer should now be in BUZ_STATE_DONE */ - - if (zr->jpg_gbuf[frame].state != BUZ_STATE_DONE) - printk(KERN_ERR "%s: jpg_sync - internal error\n", zr->name); - - *bs = zr->jpg_gbuf[frame].bs; - zr->jpg_gbuf[frame].state = BUZ_STATE_USER; - - spin_unlock_irqrestore(&zr->lock, flags); - - return 0; -} - -/* when this is called the spinlock must be held */ -static void zoran_feed_stat_com(struct zoran *zr) -{ - /* move frames from pending queue to DMA */ - - int frame, i, max_stat_com; - - max_stat_com = (zr->params.TmpDcm == 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1); - - while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com - && zr->jpg_dma_head != zr->jpg_que_head) { - - frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME]; - if (zr->params.TmpDcm == 1) { - /* fill 1 stat_com entry */ - i = zr->jpg_dma_head & BUZ_MASK_STAT_COM; - zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus; - } else { - /* fill 2 stat_com entries */ - i = (zr->jpg_dma_head & 1) * 2; - zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus; - zr->stat_com[i + 1] = zr->jpg_gbuf[frame].frag_tab_bus; - } - zr->jpg_gbuf[frame].state = BUZ_STATE_DMA; - zr->jpg_dma_head++; - - } -} - -/* when this is called the spinlock must be held */ -static void zoran_reap_stat_com(struct zoran *zr) -{ - /* move frames from DMA queue to done queue */ - - int i; - u32 stat_com; - unsigned int seq; - unsigned int dif; - int frame; - struct zoran_gbuffer *gbuf; - - /* In motion decompress we don't have a hardware frame counter, - we just count the interrupts here */ - - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) - zr->jpg_seq_num++; - - while (zr->jpg_dma_tail != zr->jpg_dma_head) { - if (zr->params.TmpDcm == 1) - i = zr->jpg_dma_tail & BUZ_MASK_STAT_COM; - else - i = (zr->jpg_dma_tail & 1) * 2 + 1; - - stat_com = zr->stat_com[i]; - - if ((stat_com & 1) == 0) { - return; - } - frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; - gbuf = &zr->jpg_gbuf[frame]; - get_fast_time(&gbuf->bs.timestamp); - - if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { - gbuf->bs.length = (stat_com & 0x7fffff) >> 1; - - /* update sequence number with the help of the counter in stat_com */ - - seq = stat_com >> 24; - dif = (seq - zr->jpg_seq_num) & 0xff; - zr->jpg_seq_num += dif; - } else { - gbuf->bs.length = 0; - } - gbuf->bs.seq = zr->params.TmpDcm == 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num; - gbuf->state = BUZ_STATE_DONE; - - zr->jpg_dma_tail++; - } -} - -static void zoran_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - u32 stat, astat; - int count; - struct zoran *zr; - unsigned long flags; - - zr = (struct zoran *) dev_id; - count = 0; - - spin_lock_irqsave(&zr->lock, flags); - while (1) { - /* get/clear interrupt status bits */ - stat = btread(ZR36057_ISR); - astat = stat & IRQ_MASK; - if (!astat) { - break; - } - btwrite(astat, ZR36057_ISR); - IDEBUG(printk(BUZ_DEBUG "-%u: astat %08x stat %08x\n", zr->id, astat, stat)); - -#if (IRQ_MASK & ZR36057_ISR_GIRQ0) - if (astat & ZR36057_ISR_GIRQ0) { - - /* Interrupts may still happen when zr->v4l_memgrab_active is switched off. - We simply ignore them */ - - if (zr->v4l_memgrab_active) { - -/* A lot more checks should be here ... */ - if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) == 0) - printk(KERN_WARNING "%s: BuzIRQ with SnapShot off ???\n", zr->name); - - if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) { - /* There is a grab on a frame going on, check if it has finished */ - - if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FrameGrab) == 0) { - /* it is finished, notify the user */ - - zr->v4l_gbuf[zr->v4l_grab_frame].state = BUZ_STATE_DONE; - zr->v4l_grab_frame = NO_GRAB_ACTIVE; - zr->v4l_grab_seq++; - zr->v4l_pend_tail++; - } - } - if (zr->v4l_grab_frame == NO_GRAB_ACTIVE) - wake_up_interruptible(&zr->v4l_capq); - - /* Check if there is another grab queued */ - - if (zr->v4l_grab_frame == NO_GRAB_ACTIVE && - zr->v4l_pend_tail != zr->v4l_pend_head) { - - int frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME]; - u32 reg; - - zr->v4l_grab_frame = frame; - - /* Set zr36057 video front end and enable video */ - - /* Buffer address */ - - reg = zr->v4l_gbuf[frame].fbuffer_bus; - btwrite(reg, ZR36057_VDTR); - if (zr->video_interlace) - reg += zr->gbpl; - btwrite(reg, ZR36057_VDBR); - - /* video stride, status, and frame grab register */ - -#ifdef XAWTV_HACK - reg = (zr->gwidth > 720) ? ((zr->gwidth & ~3) - 720) * zr->gbpl / zr->gwidth : 0; -#else - reg = 0; -#endif - if (zr->video_interlace) - reg += zr->gbpl; - reg = (reg << ZR36057_VSSFGR_DispStride); - reg |= ZR36057_VSSFGR_VidOvf; - reg |= ZR36057_VSSFGR_SnapShot; - reg |= ZR36057_VSSFGR_FrameGrab; - btwrite(reg, ZR36057_VSSFGR); - - btor(ZR36057_VDCR_VidEn, ZR36057_VDCR); - } - } - } -#endif /* (IRQ_MASK & ZR36057_ISR_GIRQ0) */ - -#if (IRQ_MASK & ZR36057_ISR_GIRQ1) - if (astat & ZR36057_ISR_GIRQ1) { - unsigned csr = zr36060_read_8(zr, 0x001); - unsigned isr = zr36060_read_8(zr, 0x008); - - IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_GIRQ1 60_code=%02x 60_intr=%02x\n", - zr->name, csr, isr)); - - btand(~ZR36057_ICR_GIRQ1, ZR36057_ICR); - zoran_reap_stat_com(zr); - zoran_feed_stat_com(zr); - } -#endif /* (IRQ_MASK & ZR36057_ISR_GIRQ1) */ - -#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ) - if (astat & ZR36057_ISR_CodRepIRQ) { - IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n", zr->name)); - btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR); - } -#endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */ - -#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) - if ((astat & ZR36057_ISR_JPEGRepIRQ) && - (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || - zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) { - zoran_reap_stat_com(zr); - zoran_feed_stat_com(zr); - wake_up_interruptible(&zr->jpg_capq); - } -#endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */ - - count++; - if (count > 10) { - printk(KERN_WARNING "%s: irq loop %d\n", zr->name, count); - if (count > 20) { - btwrite(0, ZR36057_ICR); - printk(KERN_ERR "%s: IRQ lockup, cleared int mask\n", zr->name); - break; - } - } - } - spin_unlock_irqrestore(&zr->lock, flags); -} - -/* Check a zoran_params struct for correctness, insert default params */ - -static int zoran_check_params(struct zoran *zr, struct zoran_params *params) -{ - int err = 0, err0 = 0; - - /* insert constant params */ - - params->major_version = MAJOR_VERSION; - params->minor_version = MINOR_VERSION; - - /* Check input and norm */ - - if (params->input != 0 && params->input != 1) { - err++; - } - if (params->norm != VIDEO_MODE_PAL && params->norm != VIDEO_MODE_NTSC) { - err++; - } - /* Check decimation, set default values for decimation = 1, 2, 4 */ - - switch (params->decimation) { - case 1: - - params->HorDcm = 1; - params->VerDcm = 1; - params->TmpDcm = 1; - params->field_per_buff = 2; - - params->img_x = 0; - params->img_y = 0; - params->img_width = 720; - params->img_height = tvnorms[params->norm].Ha / 2; - break; - - case 2: - - params->HorDcm = 2; - params->VerDcm = 1; - params->TmpDcm = 2; - params->field_per_buff = 1; - - params->img_x = 8; - params->img_y = 0; - params->img_width = 704; - params->img_height = tvnorms[params->norm].Ha / 2; - break; - - case 4: - - params->HorDcm = 4; - params->VerDcm = 2; - params->TmpDcm = 2; - params->field_per_buff = 1; - - params->img_x = 8; - params->img_y = 0; - params->img_width = 704; - params->img_height = tvnorms[params->norm].Ha / 2; - break; - - case 0: - - /* We have to check the data the user has set */ - - if (params->HorDcm != 1 && params->HorDcm != 2 && params->HorDcm != 4) - err0++; - if (params->VerDcm != 1 && params->VerDcm != 2) - err0++; - if (params->TmpDcm != 1 && params->TmpDcm != 2) - err0++; - if (params->field_per_buff != 1 && params->field_per_buff != 2) - err0++; - - if (params->img_x < 0) - err0++; - if (params->img_y < 0) - err0++; - if (params->img_width < 0) - err0++; - if (params->img_height < 0) - err0++; - if (params->img_x + params->img_width > 720) - err0++; - if (params->img_y + params->img_height > tvnorms[params->norm].Ha / 2) - err0++; - if (params->img_width % (16 * params->HorDcm) != 0) - err0++; - if (params->img_height % (8 * params->VerDcm) != 0) - err0++; - - if (err0) { - err++; - } - break; - - default: - err++; - break; - } - - if (params->quality > 100) - params->quality = 100; - if (params->quality < 5) - params->quality = 5; - - if (params->APPn < 0) - params->APPn = 0; - if (params->APPn > 15) - params->APPn = 15; - if (params->APP_len < 0) - params->APP_len = 0; - if (params->APP_len > 60) - params->APP_len = 60; - if (params->COM_len < 0) - params->COM_len = 0; - if (params->COM_len > 60) - params->COM_len = 60; - - if (err) - return -EINVAL; - - return 0; - -} -static void zoran_open_init_params(struct zoran *zr) -{ - int i; - - /* Per default, map the V4L Buffers */ - - zr->map_mjpeg_buffers = 0; - - /* User must explicitly set a window */ - - zr->window_set = 0; - - zr->window.x = 0; - zr->window.y = 0; - zr->window.width = 0; - zr->window.height = 0; - zr->window.chromakey = 0; - zr->window.flags = 0; - zr->window.clips = NULL; - zr->window.clipcount = 0; - - zr->video_interlace = 0; - - zr->v4l_memgrab_active = 0; - zr->v4l_overlay_active = 0; - - zr->v4l_grab_frame = NO_GRAB_ACTIVE; - zr->v4l_grab_seq = 0; - - zr->gwidth = 0; - zr->gheight = 0; - zr->gformat = 0; - zr->gbpl = 0; - - /* DMA ring stuff for V4L */ - - zr->v4l_pend_tail = 0; - zr->v4l_pend_head = 0; - for (i = 0; i < v4l_nbufs; i++) { - zr->v4l_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */ - } - - /* Set necessary params and call zoran_check_params to set the defaults */ - - zr->params.decimation = 1; - - zr->params.quality = 50; /* default compression factor 8 */ - zr->params.odd_even = 1; - - zr->params.APPn = 0; - zr->params.APP_len = 0; /* No APPn marker */ - for (i = 0; i < 60; i++) - zr->params.APP_data[i] = 0; - - zr->params.COM_len = 0; /* No COM marker */ - for (i = 0; i < 60; i++) - zr->params.COM_data[i] = 0; - - zr->params.VFIFO_FB = 0; - - memset(zr->params.reserved, 0, sizeof(zr->params.reserved)); - - zr->params.jpeg_markers = JPEG_MARKER_DHT | JPEG_MARKER_DQT; - - i = zoran_check_params(zr, &zr->params); - if (i) - printk(KERN_ERR "%s: zoran_open_init_params internal error\n", zr->name); -} - -/* - * Open a buz card. Right now the flags stuff is just playing - */ - -static int zoran_open(struct video_device *dev, int flags) -{ - struct zoran *zr = (struct zoran *) dev; - - DEBUG(printk(KERN_INFO ": zoran_open\n")); - - switch (flags) { - - case 0: - if (zr->user) - return -EBUSY; - zr->user++; - - if (v4l_fbuffer_alloc(zr) < 0) { - zr->user--; - return -ENOMEM; - } - /* default setup */ - - zoran_open_init_params(zr); - - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - - btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts - - btor(ZR36057_ICR_IntPinEn, ZR36057_ICR); - - break; - - default: - return -EBUSY; - - } - return 0; -} - -static void zoran_close(struct video_device *dev) -{ - struct zoran *zr = (struct zoran *) dev; - - DEBUG(printk(KERN_INFO ": zoran_close\n")); - - /* disable interrupts */ - btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); - - /* wake up sleeping beauties */ - wake_up_interruptible(&zr->v4l_capq); - wake_up_interruptible(&zr->jpg_capq); - - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - zr36057_set_memgrab(zr, 0); - if (zr->v4l_overlay_active) - zr36057_overlay(zr, 0); - - zr->user--; - - v4l_fbuffer_free(zr); - jpg_fbuffer_free(zr); - zr->jpg_nbufs = 0; - - DEBUG(printk(KERN_INFO ": zoran_close done\n")); -} - - -static long zoran_read(struct video_device *dev, char *buf, unsigned long count, int nonblock) -{ - return -EINVAL; -} - -static long zoran_write(struct video_device *dev, const char *buf, unsigned long count, int nonblock) -{ - return -EINVAL; -} - -/* - * ioctl routine - */ - - -static int zoran_ioctl(struct video_device *dev, unsigned int cmd, void *arg) -{ - struct zoran *zr = (struct zoran *) dev; - - switch (cmd) { - - case VIDIOCGCAP: - { - struct video_capability b; - IOCTL_DEBUG(printk("buz ioctl VIDIOCGCAP\n")); - strncpy(b.name, zr->video_dev.name, sizeof(b.name)); - b.type = VID_TYPE_CAPTURE | - VID_TYPE_OVERLAY | - VID_TYPE_CLIPPING | - VID_TYPE_FRAMERAM | - VID_TYPE_SCALES; - /* theoretically we could also flag VID_TYPE_SUBCAPTURE - but this is not even implemented in the BTTV driver */ - - b.channels = 2; /* composite, svhs */ - b.audios = 0; - b.maxwidth = BUZ_MAX_WIDTH; - b.maxheight = BUZ_MAX_HEIGHT; - b.minwidth = BUZ_MIN_WIDTH; - b.minheight = BUZ_MIN_HEIGHT; - 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; - } - IOCTL_DEBUG(printk("buz ioctl VIDIOCGCHAN for channel %d\n", v.channel)); - switch (v.channel) { - case 0: - strcpy(v.name, "Composite"); - break; - case 1: - strcpy(v.name, "SVHS"); - break; - default: - return -EINVAL; - } - v.tuners = 0; - v.flags = 0; - v.type = VIDEO_TYPE_CAMERA; - v.norm = zr->params.norm; - if (copy_to_user(arg, &v, sizeof(v))) { - return -EFAULT; - } - return 0; - } - - /* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says: - - * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input." - * ^^^^^^^ - * The famos BTTV driver has it implemented with a struct video_channel argument - * and we follow it for compatibility reasons - * - * BTW: this is the only way the user can set the norm! - */ - - case VIDIOCSCHAN: - { - struct video_channel v; - int input; - int on, res; - - if (copy_from_user(&v, arg, sizeof(v))) { - return -EFAULT; - } - IOCTL_DEBUG(printk("buz ioctl VIDIOCSCHAN: channel=%d, norm=%d\n", v.channel, v.norm)); - switch (v.channel) { - case 0: - input = 3; - break; - case 1: - input = 7; - break; - default: - return -EINVAL; - } - - if (v.norm != VIDEO_MODE_PAL - && v.norm != VIDEO_MODE_NTSC) { - return -EINVAL; - } - zr->params.norm = v.norm; - zr->params.input = v.channel; - - /* We switch overlay off and on since a change in the norm - needs different VFE settings */ - - on = zr->v4l_overlay_active && !zr->v4l_memgrab_active; - if (on) - zr36057_overlay(zr, 0); - - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm); - - if (on) - zr36057_overlay(zr, 1); - - /* Make sure the changes come into effect */ - res = wait_grab_pending(zr); - if (res) - return res; - - return 0; - } - - case VIDIOCGTUNER: - case VIDIOCSTUNER: - return -EINVAL; - - case VIDIOCGPICT: - { - struct video_picture p = zr->picture; - - IOCTL_DEBUG(printk("buz ioctl VIDIOCGPICT\n")); - p.depth = zr->buffer.depth; - switch (zr->buffer.depth) { - case 15: - p.palette = VIDEO_PALETTE_RGB555; - break; - - case 16: - p.palette = VIDEO_PALETTE_RGB565; - break; - - case 24: - p.palette = VIDEO_PALETTE_RGB24; - break; - - case 32: - p.palette = VIDEO_PALETTE_RGB32; - break; - } - - 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; - } - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_PICTURE, &p); - IOCTL_DEBUG(printk("buz ioctl VIDIOCSPICT bri=%d hue=%d col=%d con=%d dep=%d pal=%d\n", - p.brightness, p.hue, p.colour, p.contrast, p.depth, p.palette)); - /* The depth and palette values have no meaning to us, - should we return -EINVAL if they don't fit ? */ - zr->picture = p; - return 0; - } - - case VIDIOCCAPTURE: - { - int v, res; - - if (copy_from_user(&v, arg, sizeof(v))) { - return -EFAULT; - } - IOCTL_DEBUG(printk("buz ioctl VIDIOCCAPTURE: %d\n", v)); - /* If there is nothing to do, return immediatly */ - - if ((v && zr->v4l_overlay_active) || (!v && !zr->v4l_overlay_active)) - return 0; - - if (v == 0) { - zr->v4l_overlay_active = 0; - if (!zr->v4l_memgrab_active) - zr36057_overlay(zr, 0); - /* When a grab is running, the video simply won't be switched on any more */ - } else { - if (!zr->buffer_set || !zr->window_set) { - return -EINVAL; - } - zr->v4l_overlay_active = 1; - if (!zr->v4l_memgrab_active) - zr36057_overlay(zr, 1); - /* When a grab is running, the video will be switched on when grab is finished */ - } - /* Make sure the changes come into effect */ - res = wait_grab_pending(zr); - if (res) - return res; - return 0; - } - - case VIDIOCGWIN: - { - IOCTL_DEBUG(printk("buz ioctl VIDIOCGWIN\n")); - if (copy_to_user(arg, &zr->window, sizeof(zr->window))) { - return -EFAULT; - } - return 0; - } - - case VIDIOCSWIN: - { - struct video_clip *vcp; - struct video_window vw; - int on, end, res; - - if (copy_from_user(&vw, arg, sizeof(vw))) { - return -EFAULT; - } - IOCTL_DEBUG(printk("buz ioctl VIDIOCSWIN: x=%d y=%d w=%d h=%d clipcount=%d\n", vw.x, vw.y, vw.width, vw.height, vw.clipcount)); - if (!zr->buffer_set) { - return -EINVAL; - } - /* - * The video front end needs 4-byte alinged line sizes, we correct that - * silently here if necessary - */ - - if (zr->buffer.depth == 15 || zr->buffer.depth == 16) { - end = (vw.x + vw.width) & ~1; /* round down */ - vw.x = (vw.x + 1) & ~1; /* round up */ - vw.width = end - vw.x; - } - if (zr->buffer.depth == 24) { - end = (vw.x + vw.width) & ~3; /* round down */ - vw.x = (vw.x + 3) & ~3; /* round up */ - vw.width = end - vw.x; - } -#if 0 - // At least xawtv seems to care about the following - just leave it away - /* - * Also corrected silently (as long as window fits at all): - * video not fitting the screen - */ -#if 0 - if (vw.x < 0 || vw.y < 0 || vw.x + vw.width > zr->buffer.width || - vw.y + vw.height > zr->buffer.height) { - printk(BUZ_ERR ": VIDIOCSWIN: window does not fit frame buffer: %dx%d+%d*%d\n", - vw.width, vw.height, vw.x, vw.y); - return -EINVAL; - } -#else - if (vw.x < 0) - vw.x = 0; - if (vw.y < 0) - vw.y = 0; - if (vw.x + vw.width > zr->buffer.width) - vw.width = zr->buffer.width - vw.x; - if (vw.y + vw.height > zr->buffer.height) - vw.height = zr->buffer.height - vw.y; -#endif -#endif - - /* Check for valid parameters */ - if (vw.width < BUZ_MIN_WIDTH || vw.height < BUZ_MIN_HEIGHT || - vw.width > BUZ_MAX_WIDTH || vw.height > BUZ_MAX_HEIGHT) { - return -EINVAL; - } -#ifdef XAWTV_HACK - if (vw.width > 720) - vw.width = 720; -#endif - - zr->window.x = vw.x; - zr->window.y = vw.y; - zr->window.width = vw.width; - zr->window.height = vw.height; - zr->window.chromakey = 0; - zr->window.flags = 0; // RJ: Is this intended for interlace on/off ? - - zr->window.clips = NULL; - zr->window.clipcount = vw.clipcount; - - /* - * If an overlay is running, we have to switch it off - * and switch it on again in order to get the new settings in effect. - * - * We also want to avoid that the overlay mask is written - * when an overlay is running. - */ - - on = zr->v4l_overlay_active && !zr->v4l_memgrab_active; - if (on) - zr36057_overlay(zr, 0); - - /* - * Write the overlay mask if clips are wanted. - */ - if (vw.clipcount) { - vcp = vmalloc(sizeof(struct video_clip) * (vw.clipcount + 4)); - if (vcp == NULL) { - return -ENOMEM; - } - if (copy_from_user(vcp, vw.clips, sizeof(struct video_clip) * vw.clipcount)) { - vfree(vcp); - return -EFAULT; - } - write_overlay_mask(zr, vcp, vw.clipcount); - vfree(vcp); - } - if (on) - zr36057_overlay(zr, 1); - zr->window_set = 1; - - /* Make sure the changes come into effect */ - res = wait_grab_pending(zr); - if (res) - return res; - - return 0; - } - - case VIDIOCGFBUF: - { - IOCTL_DEBUG(printk("buz ioctl VIDIOCGFBUF\n")); - if (copy_to_user(arg, &zr->buffer, sizeof(zr->buffer))) { - return -EFAULT; - } - return 0; - } - - 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; - - IOCTL_DEBUG(printk("buz ioctl VIDIOCSFBUF: base=0x%x w=%d h=%d depth=%d bpl=%d\n", (u32) v.base, v.width, v.height, v.depth, v.bytesperline)); - if (zr->v4l_overlay_active) { - /* Has the user gotten crazy ... ? */ - return -EINVAL; - } - if (v.depth != 15 - && v.depth != 16 - && v.depth != 24 - && v.depth != 32) { - return -EINVAL; - } - if (v.height <= 0 || v.width <= 0 || v.bytesperline <= 0) { - return -EINVAL; - } - if (v.bytesperline & 3) { - return -EINVAL; - } - if (v.base) { - zr->buffer.base = (void *) ((unsigned long) v.base & ~3); - } - zr->buffer.height = v.height; - zr->buffer.width = v.width; - zr->buffer.depth = v.depth; - zr->buffer.bytesperline = v.bytesperline; - - if (zr->buffer.base) - zr->buffer_set = 1; - zr->window_set = 0; /* The user should set new window parameters */ - return 0; - } - - /* RJ: what is VIDIOCKEY intended to do ??? */ - - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - - case VIDIOCSYNC: - { - int v; - - if (copy_from_user(&v, arg, sizeof(v))) { - return -EFAULT; - } - IOCTL_DEBUG(printk("buz ioctl VIDIOCSYNC %d\n", v)); - return v4l_sync(zr, v); - } - - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - - if (copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) { - return -EFAULT; - } - IOCTL_DEBUG(printk("buz ioctl VIDIOCMCAPTURE frame=%d geom=%dx%d fmt=%d\n", - vm.frame, vm.height, vm.width, vm.format)); - return v4l_grab(zr, &vm); - } - - case VIDIOCGMBUF: - { - struct video_mbuf vm; - int i; - - IOCTL_DEBUG(printk("buz ioctl VIDIOCGMBUF\n")); - - vm.size = v4l_nbufs * v4l_bufsize; - vm.frames = v4l_nbufs; - for (i = 0; i < v4l_nbufs; i++) { - vm.offsets[i] = i * v4l_bufsize; - } - - /* The next mmap will map the V4L buffers */ - zr->map_mjpeg_buffers = 0; - - if (copy_to_user(arg, &vm, sizeof(vm))) { - return -EFAULT; - } - return 0; - } - - case VIDIOCGUNIT: - { - struct video_unit vu; - - IOCTL_DEBUG(printk("buz ioctl VIDIOCGUNIT\n")); - vu.video = zr->video_dev.minor; - vu.vbi = VIDEO_NO_UNIT; - vu.radio = VIDEO_NO_UNIT; - vu.audio = VIDEO_NO_UNIT; - vu.teletext = VIDEO_NO_UNIT; - if (copy_to_user(arg, &vu, sizeof(vu))) - return -EFAULT; - return 0; - } - - /* - * RJ: In principal we could support subcaptures for V4L grabbing. - * Not even the famous BTTV driver has them, however. - * If there should be a strong demand, one could consider - * to implement them. - */ - case VIDIOCGCAPTURE: - case VIDIOCSCAPTURE: - return -EINVAL; - - case BUZIOC_G_PARAMS: - { - IOCTL_DEBUG(printk("buz ioctl BUZIOC_G_PARAMS\n")); - if (copy_to_user(arg, &(zr->params), sizeof(zr->params))) - return -EFAULT; - return 0; - } - - case BUZIOC_S_PARAMS: - { - struct zoran_params bp; - int input, on; - - if (zr->codec_mode != BUZ_MODE_IDLE) { - return -EINVAL; - } - if (copy_from_user(&bp, arg, sizeof(bp))) { - return -EFAULT; - } - IOCTL_DEBUG(printk("buz ioctl BUZIOC_S_PARAMS\n")); - - /* Check the params first before overwriting our internal values */ - - if (zoran_check_params(zr, &bp)) - return -EINVAL; - - zr->params = bp; - - /* Make changes of input and norm go into effect immediatly */ - - /* We switch overlay off and on since a change in the norm - needs different VFE settings */ - - on = zr->v4l_overlay_active && !zr->v4l_memgrab_active; - if (on) - zr36057_overlay(zr, 0); - - input = zr->params.input == 0 ? 3 : 7; - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm); - - if (on) - zr36057_overlay(zr, 1); - - if (copy_to_user(arg, &bp, sizeof(bp))) { - return -EFAULT; - } - return 0; - } - - case BUZIOC_REQBUFS: - { - struct zoran_requestbuffers br; - - if (zr->jpg_buffers_allocated) { - return -EINVAL; - } - if (copy_from_user(&br, arg, sizeof(br))) { - return -EFAULT; - } - IOCTL_DEBUG(printk("buz ioctl BUZIOC_REQBUFS count = %lu size=%lu\n", - br.count, br.size)); - /* Enforce reasonable lower and upper limits */ - if (br.count < 4) - br.count = 4; /* Could be choosen smaller */ - if (br.count > BUZ_MAX_FRAME) - br.count = BUZ_MAX_FRAME; - br.size = PAGE_ALIGN(br.size); - if (br.size < 8192) - br.size = 8192; /* Arbitrary */ - /* br.size is limited by 1 page for the stat_com tables to a Maximum of 2 MB */ - if (br.size > (512 * 1024)) - br.size = (512 * 1024); /* 512 K should be enough */ - if (zr->need_contiguous && br.size > KMALLOC_MAXSIZE) - br.size = KMALLOC_MAXSIZE; - - zr->jpg_nbufs = br.count; - zr->jpg_bufsize = br.size; - - if (jpg_fbuffer_alloc(zr)) - return -ENOMEM; - - /* The next mmap will map the MJPEG buffers */ - zr->map_mjpeg_buffers = 1; - - if (copy_to_user(arg, &br, sizeof(br))) { - return -EFAULT; - } - return 0; - } - - case BUZIOC_QBUF_CAPT: - { - int nb; - - if (copy_from_user((void *) &nb, (void *) arg, sizeof(int))) { - return -EFAULT; - } - IOCTL_DEBUG(printk("buz ioctl BUZIOC_QBUF_CAPT %d\n", nb)); - return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_COMPRESS); - } - - case BUZIOC_QBUF_PLAY: - { - int nb; - - if (copy_from_user((void *) &nb, (void *) arg, sizeof(int))) { - return -EFAULT; - } - IOCTL_DEBUG(printk("buz ioctl BUZIOC_QBUF_PLAY %d\n", nb)); - return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_DECOMPRESS); - } - - case BUZIOC_SYNC: - { - struct zoran_sync bs; - int res; - - IOCTL_DEBUG(printk("buz ioctl BUZIOC_SYNC\n")); - res = jpg_sync(zr, &bs); - if (copy_to_user(arg, &bs, sizeof(bs))) { - return -EFAULT; - } - return res; - } - - case BUZIOC_G_STATUS: - { - struct zoran_status bs; - int norm, input, status; - - if (zr->codec_mode != BUZ_MODE_IDLE) { - return -EINVAL; - } - if (copy_from_user(&bs, arg, sizeof(bs))) { - return -EFAULT; - } - IOCTL_DEBUG(printk("buz ioctl BUZIOC_G_STATUS\n")); - switch (bs.input) { - case 0: - input = 3; - break; - case 1: - input = 7; - break; - default: - return -EINVAL; - } - - /* Set video norm to VIDEO_MODE_AUTO */ - - norm = VIDEO_MODE_AUTO; - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &norm); - - /* sleep 1 second */ - - schedule_timeout(HZ); - - /* Get status of video decoder */ - - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_GET_STATUS, &status); - bs.signal = (status & DECODER_STATUS_GOOD) ? 1 : 0; - bs.norm = (status & DECODER_STATUS_NTSC) ? VIDEO_MODE_NTSC : VIDEO_MODE_PAL; - bs.color = (status & DECODER_STATUS_COLOR) ? 1 : 0; - - /* restore previous input and norm */ - input = zr->params.input == 0 ? 3 : 7; - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); - - if (copy_to_user(arg, &bs, sizeof(bs))) { - return -EFAULT; - } - return 0; - } - - default: - return -ENOIOCTLCMD; - - } - return 0; -} - - -/* - * This maps the buffers to user space. - * - * Depending on the state of zr->map_mjpeg_buffers - * the V4L or the MJPEG buffers are mapped - * - */ - -static int zoran_mmap(struct video_device *dev, const char *adr, unsigned long size) -{ - struct zoran *zr = (struct zoran *) dev; - unsigned long start = (unsigned long) adr; - unsigned long page, pos, todo, fraglen; - int i, j; - - if (zr->map_mjpeg_buffers) { - /* Map the MJPEG buffers */ - - if (!zr->jpg_buffers_allocated) { - return -ENOMEM; - } - if (size > zr->jpg_nbufs * zr->jpg_bufsize) { - return -EINVAL; - } - - for (i = 0; i < zr->jpg_nbufs; i++) { - for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) { - fraglen = (zr->jpg_gbuf[i].frag_tab[2 * j + 1] & ~1) << 1; - todo = size; - if (todo > fraglen) - todo = fraglen; - pos = (unsigned long) zr->jpg_gbuf[i].frag_tab[2 * j]; - page = virt_to_phys(bus_to_virt(pos)); /* should just be pos on i386 */ - if (remap_page_range(start, page, todo, PAGE_SHARED)) { - printk(KERN_ERR "%s: zoran_mmap(V4L): remap_page_range failed\n", zr->name); - return -EAGAIN; - } - size -= todo; - start += todo; - if (size == 0) - break; - if (zr->jpg_gbuf[i].frag_tab[2 * j + 1] & 1) - break; /* was last fragment */ - } - if (size == 0) - break; - } - } else { - /* Map the V4L buffers */ - - if (size > v4l_nbufs * v4l_bufsize) { - return -EINVAL; - } - - for (i = 0; i < v4l_nbufs; i++) { - todo = size; - if (todo > v4l_bufsize) - todo = v4l_bufsize; - page = zr->v4l_gbuf[i].fbuffer_phys; - DEBUG(printk("V4L remap page range %d 0x%x %d to 0x%x\n", i, page, todo, start)); - if (remap_page_range(start, page, todo, PAGE_SHARED)) { - printk(KERN_ERR "%s: zoran_mmap(V4L): remap_page_range failed\n", zr->name); - return -EAGAIN; - } - size -= todo; - start += todo; - if (size == 0) - break; - } - } - return 0; -} - -static struct video_device zoran_template = -{ - owner: THIS_MODULE, - name: BUZ_NAME, - type: VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM | - VID_TYPE_SCALES | VID_TYPE_SUBCAPTURE, - hardware: VID_HARDWARE_ZR36067, - open: zoran_open, - close: zoran_close, - read: zoran_read, - write: zoran_write, - ioctl: zoran_ioctl, - mmap: zoran_mmap, -}; - -static int zr36057_init(int i) -{ - struct zoran *zr = &zoran[i]; - unsigned long mem; - unsigned mem_needed; - int j; - int rev; - - /* reset zr36057 */ - btwrite(0, ZR36057_SPGPPCR); - mdelay(10); - - /* default setup of all parameters which will persist beetween opens */ - - zr->user = 0; - - init_waitqueue_head(&zr->v4l_capq); - init_waitqueue_head(&zr->jpg_capq); - - zr->map_mjpeg_buffers = 0; /* Map V4L buffers by default */ - - zr->jpg_nbufs = 0; - zr->jpg_bufsize = 0; - zr->jpg_buffers_allocated = 0; - - zr->buffer_set = 0; /* Flag if frame buffer has been set */ - zr->buffer.base = (void *) vidmem; - zr->buffer.width = 0; - zr->buffer.height = 0; - zr->buffer.depth = 0; - zr->buffer.bytesperline = 0; - - zr->params.norm = default_norm ? 1 : 0; /* Avoid nonsense settings from user */ - zr->params.input = default_input ? 1 : 0; /* Avoid nonsense settings from user */ - zr->video_interlace = 0; - - /* Should the following be reset at every open ? */ - - zr->picture.colour = 32768; - zr->picture.brightness = 32768; - zr->picture.hue = 32768; - zr->picture.contrast = 32768; - zr->picture.whiteness = 0; - zr->picture.depth = 0; - zr->picture.palette = 0; - - for (j = 0; j < VIDEO_MAX_FRAME; j++) { - zr->v4l_gbuf[i].fbuffer = 0; - zr->v4l_gbuf[i].fbuffer_phys = 0; - zr->v4l_gbuf[i].fbuffer_bus = 0; - } - - zr->stat_com = 0; - - /* default setup (will be repeated at every open) */ - - zoran_open_init_params(zr); - - /* allocate memory *before* doing anything to the hardware in case allocation fails */ - - /* STAT_COM table and overlay mask */ - - mem_needed = (BUZ_NUM_STAT_COM + ((BUZ_MAX_WIDTH + 31) / 32) * BUZ_MAX_HEIGHT) * 4; - mem = (unsigned long) kmalloc(mem_needed, GFP_KERNEL); - if (!mem) { - return -ENOMEM; - } - memset((void *) mem, 0, mem_needed); - - zr->stat_com = (u32 *) mem; - for (j = 0; j < BUZ_NUM_STAT_COM; j++) { - zr->stat_com[j] = 1; /* mark as unavailable to zr36057 */ - } - zr->overlay_mask = (u32 *) (mem + BUZ_NUM_STAT_COM * 4); - - /* Initialize zr->jpg_gbuf */ - - for (j = 0; j < BUZ_MAX_FRAME; j++) { - zr->jpg_gbuf[j].frag_tab = 0; - zr->jpg_gbuf[j].frag_tab_bus = 0; - zr->jpg_gbuf[j].state = BUZ_STATE_USER; - zr->jpg_gbuf[j].bs.frame = j; - } - - /* take zr36057 out of reset now */ - btwrite(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR); - mdelay(10); - - /* stop all DMA processes */ - btwrite(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); - btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); - /* assert P_Reset */ - btwrite(0, ZR36057_JPC); - - switch(zr->board) - { - case BOARD_BUZ: - - /* set up GPIO direction */ - btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR); - - /* Set up guest bus timing - Guests 0..3 Tdur=12, Trec=3 */ - btwrite((GPIO_MASK << 24) | 0x8888, ZR36057_GPPGCR1); - mdelay(10); - - /* reset video decoder */ - - GPIO(zr, 0, 0); - mdelay(10); - GPIO(zr, 0, 1); - mdelay(10); - - /* reset JPEG codec */ - zr36060_sleep(zr, 0); - mdelay(10); - zr36060_reset(zr); - mdelay(10); - - /* display codec revision */ - if ((rev=zr36060_read_8(zr, 0x022)) == 0x33) { - printk(KERN_INFO "%s: Zoran ZR36060 (rev %d)\n", - zr->name, zr36060_read_8(zr, 0x023)); - } else { - printk(KERN_ERR "%s: Zoran ZR36060 not found (Rev=%d)\n", zr->name, rev); - kfree((void *) zr->stat_com); - return -1; - } - break; - - case BOARD_LML33: -// btwrite(btread(ZR36057_SPGPPCR)&~ZR36057_SPGPPCR_SoftReset , ZR36057_SPGPPCR); -// udelay(100); -// btwrite(btread(ZR36057_SPGPPCR)|ZR36057_SPGPPCR_SoftReset , ZR36057_SPGPPCR); -// udelay(1000); - - /* - * Set up the GPIO direction - */ - btwrite(btread(ZR36057_SPGPPCR_SoftReset)|0 , ZR36057_SPGPPCR); - /* Set up guest bus timing - Guests 0..2 Tdur=12, Trec=3 */ - btwrite(0xFF00F888, ZR36057_GPPGCR1); - mdelay(10); - GPIO(zr, 5, 0); /* Analog video bypass */ - udelay(3000); - GPIO(zr, 0, 0); /* Reset 819 */ - udelay(3000); - GPIO(zr, 0, 1); /* 819 back */ - udelay(3000); - /* reset JPEG codec */ - zr36060_sleep(zr, 0); - udelay(3000); - zr36060_reset(zr); - udelay(3000); - - /* display codec revision */ - if ((rev=zr36060_read_8(zr, 0x022)) == 0x33) { - printk(KERN_INFO "%s: Zoran ZR36060 (rev %d)\n", - zr->name, zr36060_read_8(zr, 0x023)); - } else { - printk(KERN_ERR "%s: Zoran ZR36060 not found (rev=%d)\n", zr->name, rev); - kfree((void *) zr->stat_com); - return -1; - } - break; - } - /* i2c */ - memcpy(&zr->i2c, &zoran_i2c_bus_template, sizeof(struct i2c_bus)); - sprintf(zr->i2c.name, "zoran%u", zr->id); - zr->i2c.data = zr; - if (i2c_register_bus(&zr->i2c) < 0) { - kfree((void *) zr->stat_com); - return -1; - } - /* - * Now add the template and register the device unit. - */ - memcpy(&zr->video_dev, &zoran_template, sizeof(zoran_template)); - sprintf(zr->video_dev.name, "zoran%u", zr->id); - if (video_register_device(&zr->video_dev, VFL_TYPE_GRABBER) < 0) { - i2c_unregister_bus(&zr->i2c); - kfree((void *) zr->stat_com); - return -1; - } - /* toggle JPEG codec sleep to sync PLL */ - zr36060_sleep(zr, 1); - mdelay(10); - zr36060_sleep(zr, 0); - mdelay(10); - - /* Enable bus-mastering */ - pci_set_master(zr->pci_dev); - - j = zr->params.input == 0 ? 3 : 7; - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &j); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); - i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm); - - /* set individual interrupt enables (without GIRQ0) - but don't global enable until zoran_open() */ - - btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ0, ZR36057_ICR); - - if(request_irq(zr->pci_dev->irq, zoran_irq, - SA_SHIRQ | SA_INTERRUPT, zr->name, (void *) zr)<0) - { - printk(KERN_ERR "%s: Can't assign irq.\n", zr->name); - video_unregister_device(&zr->video_dev); - i2c_unregister_bus(&zr->i2c); - kfree((void *) zr->stat_com); - return -1; - } - zr->initialized = 1; - return 0; -} - - - -static void release_zoran(void) -{ - u8 command; - int i; - struct zoran *zr; - - for (i = 0; i < zoran_num; i++) { - zr = &zoran[i]; - - if (!zr->initialized) - continue; - - /* unregister i2c_bus */ - i2c_unregister_bus((&zr->i2c)); - - /* disable PCI bus-mastering */ - pci_read_config_byte(zr->pci_dev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MASTER; - pci_write_config_byte(zr->pci_dev, PCI_COMMAND, command); - - /* put chip into reset */ - btwrite(0, ZR36057_SPGPPCR); - - free_irq(zr->pci_dev->irq, zr); - - /* unmap and free memory */ - - kfree((void *) zr->stat_com); - - iounmap(zr->zr36057_mem); - - video_unregister_device(&zr->video_dev); - } -} - -/* - * Scan for a Buz card (actually for the PCI controller ZR36057), - * request the irq and map the io memory - */ - -static int find_zr36057(void) -{ - unsigned char latency; - struct zoran *zr; - struct pci_dev *dev = NULL; - - zoran_num = 0; - - while (zoran_num < BUZ_MAX - && (dev = pci_find_device(PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057, dev)) != NULL) { - zr = &zoran[zoran_num]; - zr->pci_dev = dev; - zr->zr36057_mem = NULL; - zr->id = zoran_num; - sprintf(zr->name, "zoran%u", zr->id); - - spin_lock_init(&zr->lock); - - if (pci_enable_device(dev)) - continue; - - zr->zr36057_adr = pci_resource_start(zr->pci_dev, 0); - pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION, &zr->revision); - if (zr->revision < 2) { - printk(KERN_INFO "%s: Zoran ZR36057 (rev %d) irq: %d, memory: 0x%08x.\n", - zr->name, zr->revision, zr->pci_dev->irq, zr->zr36057_adr); - } else { - unsigned short ss_vendor_id, ss_id; - - ss_vendor_id = zr->pci_dev->subsystem_vendor; - ss_id = zr->pci_dev->subsystem_device; - printk(KERN_INFO "%s: Zoran ZR36067 (rev %d) irq: %d, memory: 0x%08x\n", - zr->name, zr->revision, zr->pci_dev->irq, zr->zr36057_adr); - printk(KERN_INFO "%s: subsystem vendor=0x%04x id=0x%04x\n", - zr->name, ss_vendor_id, ss_id); - if(ss_vendor_id==0xFF10 && ss_id == 0xDE41) - { - zr->board = BOARD_LML33; - printk(KERN_INFO "%s: LML33 detected.\n", zr->name); - } - } - - zr->zr36057_mem = ioremap(zr->zr36057_adr, 0x1000); - if (!zr->zr36057_mem) { - printk(KERN_ERR "%s: ioremap failed\n", zr->name); - break; - } - - /* set PCI latency timer */ - pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, &latency); - if (latency != 48) { - printk(KERN_INFO "%s: Changing PCI latency from %d to 48.\n", zr->name, latency); - latency = 48; - pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, latency); - } - zoran_num++; - } - if (zoran_num == 0) - printk(KERN_INFO "zoran: no cards found.\n"); - - return zoran_num; -} - -static void handle_chipset(void) -{ - if(pci_pci_problems&PCIPCI_FAIL) - { - printk(KERN_WARNING "buz: This configuration is known to have PCI to PCI DMA problems\n"); - printk(KERN_WARNING "buz: You may not be able to use overlay mode.\n"); - } - - - if(pci_pci_problems&PCIPCI_TRITON) - { - printk("buz: Enabling Triton support.\n"); - triton = 1; - } - - if(pci_pci_problems&PCIPCI_NATOMA) - { - printk("buz: Enabling Natoma workaround.\n"); - natoma = 1; - } -} - -#ifdef MODULE -int init_module(void) -#else -int init_zoran_cards(struct video_init *unused) -#endif -{ - int i; - - - printk(KERN_INFO "Zoran driver 1.00 (c) 1999 Rainer Johanni, Dave Perks.\n"); - - /* Look for Buz cards */ - - if (find_zr36057() <= 0) { - return -EIO; - } - printk(KERN_INFO"zoran: %d zoran card(s) found\n", zoran_num); - - if (zoran_num == 0) - return -ENXIO; - - - /* check the parameters we have been given, adjust if necessary */ - - if (v4l_nbufs < 0) - v4l_nbufs = 0; - if (v4l_nbufs > VIDEO_MAX_FRAME) - v4l_nbufs = VIDEO_MAX_FRAME; - /* The user specfies the in KB, we want them in byte (and page aligned) */ - v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024); - if (v4l_bufsize < 32768) - v4l_bufsize = 32768; - /* 2 MB is arbitrary but sufficient for the maximum possible images */ - if (v4l_bufsize > 2048 * 1024) - v4l_bufsize = 2048 * 1024; - - printk(KERN_INFO "zoran: using %d V4L buffers of size %d KB\n", v4l_nbufs, v4l_bufsize >> 10); - - /* Use parameter for vidmem or try to find a video card */ - - if (vidmem) { - printk(KERN_INFO "zoran: Using supplied video memory base address @ 0x%lx\n", vidmem); - } - - /* check if we have a Triton or Natome chipset */ - - handle_chipset(); - - /* take care of Natoma chipset and a revision 1 zr36057 */ - - for (i = 0; i < zoran_num; i++) { - if (natoma && zoran[i].revision <= 1) { - zoran[i].need_contiguous = 1; - printk(KERN_INFO "%s: ZR36057/Natome bug, max. buffer size is 128K\n", zoran[i].name); - } else { - zoran[i].need_contiguous = 0; - } - } - - /* initialize the Buzs */ - - /* We have to know which ones must be released if an error occurs */ - for (i = 0; i < zoran_num; i++) - zoran[i].initialized = 0; - - for (i = 0; i < zoran_num; i++) { - if (zr36057_init(i) < 0) { - release_zoran(); - return -EIO; - } - } - - return 0; -} - - - -#ifdef MODULE - -void cleanup_module(void) -{ - release_zoran(); -} - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/buz.h linux.ac/drivers/media/video/buz.h --- linux.vanilla/drivers/media/video/buz.h Mon Dec 11 21:15:49 2000 +++ linux.ac/drivers/media/video/buz.h Thu Jan 1 01:00:00 1970 @@ -1,319 +0,0 @@ -/* - buz - Iomega Buz driver - - Copyright (C) 1999 Rainer Johanni - - based on - - buz.0.0.3 Copyright (C) 1998 Dave Perks - - and - - bttv - Bt848 frame grabber driver - Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.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. - */ - -#ifndef _BUZ_H_ -#define _BUZ_H_ - -/* The Buz only supports a maximum width of 720, but some V4L - applications (e.g. xawtv are more happy with 768). - If XAWTV_HACK is defined, we try to fake a device with bigger width */ - -#define XAWTV_HACK - -#ifdef XAWTV_HACK -#define BUZ_MAX_WIDTH 768 /* never display more than 768 pixels */ -#else -#define BUZ_MAX_WIDTH 720 /* never display more than 720 pixels */ -#endif -#define BUZ_MAX_HEIGHT 576 /* never display more than 576 rows */ -#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */ -#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */ - -struct zoran_requestbuffers { - unsigned long count; /* Number of buffers for MJPEG grabbing */ - unsigned long size; /* Size PER BUFFER in bytes */ -}; - -struct zoran_sync { - unsigned long frame; /* number of buffer that has been free'd */ - unsigned long length; /* number of code bytes in buffer (capture only) */ - unsigned long seq; /* frame sequence number */ - struct timeval timestamp; /* timestamp */ -}; - -struct zoran_status { - int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */ - int signal; /* Returned: 1 if valid video signal detected */ - int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ - int color; /* Returned: 1 if color signal detected */ -}; - -struct zoran_params { - - /* The following parameters can only be queried */ - - int major_version; /* Major version number of driver */ - int minor_version; /* Minor version number of driver */ - - /* Main control parameters */ - - int input; /* Input channel: 0 = Composite, 1 = S-VHS */ - int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ - int decimation; /* decimation of captured video, - enlargement of video played back. - Valid values are 1, 2, 4 or 0. - 0 is a special value where the user - has full control over video scaling */ - - /* The following parameters only have to be set if decimation==0, - for other values of decimation they provide the data how the image is captured */ - - int HorDcm; /* Horizontal decimation: 1, 2 or 4 */ - int VerDcm; /* Vertical decimation: 1 or 2 */ - int TmpDcm; /* Temporal decimation: 1 or 2, - if TmpDcm==2 in capture every second frame is dropped, - in playback every frame is played twice */ - int field_per_buff; /* Number of fields per buffer: 1 or 2 */ - int img_x; /* start of image in x direction */ - int img_y; /* start of image in y direction */ - int img_width; /* image width BEFORE decimation, - must be a multiple of HorDcm*16 */ - int img_height; /* image height BEFORE decimation, - must be a multiple of VerDcm*8 */ - - /* --- End of parameters for decimation==0 only --- */ - - /* JPEG control parameters */ - - int quality; /* Measure for quality of compressed images. - Scales linearly with the size of the compressed images. - Must be beetween 0 and 100, 100 is a compression - ratio of 1:4 */ - - int odd_even; /* Which field should come first ??? */ - - int APPn; /* Number of APP segment to be written, must be 0..15 */ - int APP_len; /* Length of data in JPEG APPn segment */ - char APP_data[60]; /* Data in the JPEG APPn segment. */ - - int COM_len; /* Length of data in JPEG COM segment */ - char COM_data[60]; /* Data in JPEG COM segment */ - - unsigned long jpeg_markers; /* Which markers should go into the JPEG output. - Unless you exactly know what you do, leave them untouched. - Inluding less markers will make the resulting code - smaller, but there will be fewer aplications - which can read it. - The presence of the APP and COM marker is - influenced by APP0_len and COM_len ONLY! */ -#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */ -#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */ -#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */ -#define JPEG_MARKER_COM (1<<6) /* Comment segment */ -#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */ - - int VFIFO_FB; /* Flag for enabling Video Fifo Feedback. - If this flag is turned on and JPEG decompressing - is going to the screen, the decompress process - is stopped every time the Video Fifo is full. - This enables a smooth decompress to the screen - but the video output signal will get scrambled */ - - /* Misc */ - - char reserved[312]; /* Makes 512 bytes for this structure */ -}; - -/* - Private IOCTL to set up for displaying MJPEG - */ -#define BUZIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct zoran_params) -#define BUZIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct zoran_params) -#define BUZIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct zoran_requestbuffers) -#define BUZIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int) -#define BUZIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int) -#define BUZIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct zoran_sync) -#define BUZIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct zoran_status) - - -#ifdef __KERNEL__ - -#define BUZ_NUM_STAT_COM 4 -#define BUZ_MASK_STAT_COM 3 - -#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */ -#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */ - -#if VIDEO_MAX_FRAME <= 32 -#define V4L_MAX_FRAME 32 -#elif VIDEO_MAX_FRAME <= 64 -#define V4L_MAX_FRAME 64 -#else -#error "Too many video frame buffers to handle" -#endif -#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1) - - -#include "zr36057.h" - -enum zoran_codec_mode { - BUZ_MODE_IDLE, /* nothing going on */ - BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */ - BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */ - BUZ_MODE_STILL_COMPRESS, /* still frame conversion */ - BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */ -}; - -enum zoran_buffer_state { - BUZ_STATE_USER, /* buffer is owned by application */ - BUZ_STATE_PEND, /* buffer is queued in pend[] ready to feed to I/O */ - BUZ_STATE_DMA, /* buffer is queued in dma[] for I/O */ - BUZ_STATE_DONE /* buffer is ready to return to application */ -}; - -struct zoran_gbuffer { - u32 *frag_tab; /* addresses of frag table */ - u32 frag_tab_bus; /* same value cached to save time in ISR */ - enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */ - struct zoran_sync bs; /* DONE: info to return to application */ -}; - -struct v4l_gbuffer { - char *fbuffer; /* virtual address of frame buffer */ - unsigned long fbuffer_phys; /* physical address of frame buffer */ - unsigned long fbuffer_bus; /* bus address of frame buffer */ - enum zoran_buffer_state state; /* state: unused/pending/done */ -}; - -struct zoran { - struct video_device video_dev; - struct i2c_bus i2c; - - int initialized; /* flag if zoran has been correctly initalized */ - int user; /* number of current users (0 or 1) */ - - unsigned short id; /* number of this device */ - char name[32]; /* name of this device */ - struct pci_dev *pci_dev; /* PCI device */ - unsigned char revision; /* revision of zr36057 */ - int board; /* Board type */ -#define BOARD_BUZ 0 -#define BOARD_LML33 1 - unsigned int zr36057_adr; /* bus address of IO mem returned by PCI BIOS */ - unsigned char *zr36057_mem; /* pointer to mapped IO memory */ - - int map_mjpeg_buffers; /* Flag which bufferset will map by next mmap() */ - - spinlock_t lock; /* Spinlock */ - - /* Video for Linux parameters */ - - struct video_picture picture; /* Current picture params */ - struct video_buffer buffer; /* Current buffer params */ - struct video_window window; /* Current window params */ - int buffer_set, window_set; /* Flags if the above structures are set */ - int video_interlace; /* Image on screen is interlaced */ - - u32 *overlay_mask; - - wait_queue_head_t v4l_capq; /* wait here for grab to finish */ - - int v4l_overlay_active; /* Overlay grab is activated */ - int v4l_memgrab_active; /* Memory grab is activated */ - - int v4l_grab_frame; /* Frame number being currently grabbed */ -#define NO_GRAB_ACTIVE (-1) - int v4l_grab_seq; /* Number of frames grabbed */ - int gwidth; /* Width of current memory capture */ - int gheight; /* Height of current memory capture */ - int gformat; /* Format of ... */ - int gbpl; /* byte per line of ... */ - - /* V4L grab queue of frames pending */ - - unsigned v4l_pend_head; - unsigned v4l_pend_tail; - int v4l_pend[V4L_MAX_FRAME]; - - struct v4l_gbuffer v4l_gbuf[VIDEO_MAX_FRAME]; /* V4L buffers' info */ - - /* Buz MJPEG parameters */ - - unsigned long jpg_nbufs; /* Number of buffers */ - unsigned long jpg_bufsize; /* Size of mjpeg buffers in bytes */ - int jpg_buffers_allocated; /* Flag if buffers are allocated */ - int need_contiguous; /* Flag if contiguous buffers are needed */ - - enum zoran_codec_mode codec_mode; /* status of codec */ - struct zoran_params params; /* structure with a lot of things to play with */ - - wait_queue_head_t jpg_capq; /* wait here for grab to finish */ - - /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */ - /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */ - /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */ - unsigned long jpg_que_head; /* Index where to put next buffer which is queued */ - unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */ - unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */ - unsigned long jpg_que_tail; /* Index of last buffer in queue */ - unsigned long jpg_seq_num; /* count of frames since grab/play started */ - - /* zr36057's code buffer table */ - u32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ - - /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */ - int jpg_pend[BUZ_MAX_FRAME]; - - /* array indexed by frame number */ - struct zoran_gbuffer jpg_gbuf[BUZ_MAX_FRAME]; /* MJPEG buffers' info */ -}; - -#endif - -/*The following should be done in more portable way. It depends on define - of _ALPHA_BUZ in the Makefile. */ - -#ifdef _ALPHA_BUZ -#define btwrite(dat,adr) writel((dat),(char *) (zr->zr36057_adr+(adr))) -#define btread(adr) readl(zr->zr36057_adr+(adr)) -#else -#define btwrite(dat,adr) writel((dat), (char *) (zr->zr36057_mem+(adr))) -#define btread(adr) readl(zr->zr36057_mem+(adr)) -#endif - -#define btand(dat,adr) btwrite((dat) & btread(adr), adr) -#define btor(dat,adr) btwrite((dat) | btread(adr), adr) -#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) - -#define I2C_TSA5522 0xc2 -#define I2C_TDA9850 0xb6 -#define I2C_HAUPEE 0xa0 -#define I2C_STBEE 0xae -#define I2C_SAA7111 0x48 -#define I2C_SAA7185 0x88 - -#define TDA9850_CON1 0x04 -#define TDA9850_CON2 0x05 -#define TDA9850_CON3 0x06 -#define TDA9850_CON4 0x07 -#define TDA9850_ALI1 0x08 -#define TDA9850_ALI2 0x09 -#define TDA9850_ALI3 0x0a - -#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/planb.c linux.ac/drivers/media/video/planb.c --- linux.vanilla/drivers/media/video/planb.c Sat May 26 16:53:05 2001 +++ linux.ac/drivers/media/video/planb.c Tue May 22 08:49:26 2001 @@ -50,6 +50,7 @@ #include #include #include +#include #include "planb.h" #include "saa7196.h" @@ -326,41 +327,14 @@ /* misc. supporting functions */ /******************************/ -static void __planb_wait(struct planb *pb) -{ - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&pb->lockq, &wait); -repeat: - set_current_state(TASK_UNINTERRUPTIBLE); - if (pb->lock) { - schedule(); - goto repeat; - } - remove_wait_queue(&pb->lockq, &wait); - current->state = TASK_RUNNING; -} - -static inline void planb_wait(struct planb *pb) -{ - DEBUG("PlanB: planb_wait\n"); - if(pb->lock) - __planb_wait(pb); -} - static inline void planb_lock(struct planb *pb) { - DEBUG("PlanB: planb_lock\n"); - if(pb->lock) - __planb_wait(pb); - pb->lock = 1; + down(&pb->lock); } static inline void planb_unlock(struct planb *pb) { - DEBUG("PlanB: planb_unlock\n"); - pb->lock = 0; - wake_up(&pb->lockq); + up(&pb->lock); } /***************/ @@ -2098,7 +2072,7 @@ pb->tab_size = PLANB_MAXLINES + 40; pb->suspend = 0; pb->lock = 0; - init_waitqueue_head(&pb->lockq); + init_MUTEX(&pb->lock); pb->ch1_cmd = 0; pb->ch2_cmd = 0; pb->mask = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/planb.h linux.ac/drivers/media/video/planb.h --- linux.vanilla/drivers/media/video/planb.h Wed Apr 12 17:38:53 2000 +++ linux.ac/drivers/media/video/planb.h Tue May 22 08:49:13 2001 @@ -174,8 +174,7 @@ int user; unsigned int tab_size; int maxlines; - int lock; - wait_queue_head_t lockq; + struct semaphore lock; unsigned int irq; /* interrupt number */ volatile unsigned int intr_mask; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/saa5249.c linux.ac/drivers/media/video/saa5249.c --- linux.vanilla/drivers/media/video/saa5249.c Sat May 26 16:53:05 2001 +++ linux.ac/drivers/media/video/saa5249.c Tue May 22 12:23:18 2001 @@ -102,6 +102,7 @@ int disp_mode; int virtual_mode; struct i2c_client *client; + struct semaphore lock; }; @@ -175,6 +176,7 @@ } memset(t, 0, sizeof(*t)); strcpy(client->name, IF_NAME); + init_MUTEX(&t->lock); /* * Now create a video4linux device @@ -188,7 +190,7 @@ return -ENOMEM; } memcpy(vd, &saa_template, sizeof(*vd)); - + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); @@ -199,7 +201,8 @@ t->vdau[pgbuf].stopped = TRUE; t->is_searching[pgbuf] = FALSE; } - vd->priv=t; + vd->priv=t; + /* * Register it @@ -342,9 +345,8 @@ * Standard character-device-driver functions */ -static int saa5249_ioctl(struct video_device *vd, unsigned int cmd, void *arg) +static int do_saa5249_ioctl(struct saa5249_device *t, unsigned int cmd, void *arg) { - struct saa5249_device *t=vd->priv; static int virtual_mode = FALSE; switch(cmd) @@ -602,6 +604,21 @@ return -EINVAL; } +/* + * Handle the locking + */ + +static int saa5249_ioctl(struct video_device *vd, unsigned int cmd, void *arg) +{ + struct saa5249_device *t=vd->priv; + int err; + + down(&t->lock); + err = do_saa5249_ioctl(t, cmd, arg); + up(&t->lock); + + return err; +} static int saa5249_open(struct video_device *vd, int nb) { @@ -632,7 +649,6 @@ t->is_searching[pgbuf] = FALSE; } t->virtual_mode=FALSE; - MOD_INC_USE_COUNT; return 0; } @@ -643,7 +659,6 @@ struct saa5249_device *t=vd->priv; i2c_senddata(t, 1, 0x20, -1); /* Turn off CCT */ i2c_senddata(t, 5, 3, 3, -1); /* Turn off TV-display */ - MOD_DEC_USE_COUNT; return; } @@ -669,6 +684,7 @@ static struct video_device saa_template = { + owner: THIS_MODULE, name: IF_NAME, type: VID_TYPE_TELETEXT, /*| VID_TYPE_TUNER ?? */ hardware: VID_HARDWARE_SAA5249, diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/saa7146.h linux.ac/drivers/media/video/saa7146.h --- linux.vanilla/drivers/media/video/saa7146.h Mon Dec 11 21:15:51 2000 +++ linux.ac/drivers/media/video/saa7146.h Sat May 26 17:58:15 2001 @@ -92,7 +92,15 @@ wait_queue_head_t i2cq, debiq, audq, vidq; u8 *vidbuf, *audbuf, *osdbuf, *dmadebi; int audhead, vidhead, osdhead, audtail, vidtail, osdtail; + + /* lock guards between IRQ and writer. sem guards between + * parallel writes/ioctls and provides ioctl level atomicity + * to the apps + */ + spinlock_t lock; /* the device lock */ + struct semaphore sem; /* operation level lock */ + }; #endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/stradis.c linux.ac/drivers/media/video/stradis.c --- linux.vanilla/drivers/media/video/stradis.c Sat May 26 16:53:05 2001 +++ linux.ac/drivers/media/video/stradis.c Wed May 23 23:46:05 2001 @@ -1350,6 +1350,8 @@ static int saa_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { struct saa7146 *saa = (struct saa7146 *) dev; + int err; + switch (cmd) { case VIDIOCGCAP: { @@ -1398,6 +1400,7 @@ saa->win.color_fmt = format; saawrite(format|0x60, SAA7146_CLIP_FORMAT_CTRL); } + down(&saa->sem); saawrite(((p.brightness & 0xff00) << 16) | ((p.contrast & 0xfe00) << 7) | ((p.colour & 0xfe00) >> 9), SAA7146_BCS_CTRL); @@ -1407,12 +1410,14 @@ SAA7146_MC2_UPLD_HPS_V) << 16) | SAA7146_MC2_UPLD_HPS_H | SAA7146_MC2_UPLD_HPS_V, SAA7146_MC2); + up(&saa->sem); return 0; } case VIDIOCSWIN: { struct video_window vw; struct video_clip *vcp = NULL; + int err = -EFAULT; if (copy_from_user(&vw, arg, sizeof(vw))) return -EFAULT; @@ -1440,6 +1445,8 @@ if (saa->win.height > 576) saa->win.height = 576; } + + down(&saa->sem); /* stop capture */ saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1); @@ -1451,16 +1458,19 @@ if (vw.clipcount < 0) { if (copy_from_user(saa->dmavid2, vw.clips, VIDEO_CLIPMAP_SIZE)) - return -EFAULT; + goto out; } else if (vw.clipcount > 0) { if ((vcp = vmalloc(sizeof(struct video_clip) * (vw.clipcount))) == NULL) - return -ENOMEM; + { + err = -ENOMEM; + goto out; + } if (copy_from_user(vcp, vw.clips, sizeof(struct video_clip) * vw.clipcount)) { vfree(vcp); - return -EFAULT; + goto out; } } else /* nothing clipped */ memset(saa->dmavid2, 0, VIDEO_CLIPMAP_SIZE); @@ -1473,7 +1483,10 @@ saawrite(((SAA7146_MC1_TR_E_1 | SAA7146_MC1_TR_E_2) << 16) | 0xffff, SAA7146_MC1); - return 0; + err = 0; +out: + up(&saa->sem); + return err; } case VIDIOCGWIN: { @@ -1493,18 +1506,22 @@ int v; if (copy_from_user(&v, arg, sizeof(v))) return -EFAULT; + down(&saa->sem); if (v == 0) { saa->cap &= ~1; saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1); } else { if (saa->win.vidadr == 0 || saa->win.width == 0 - || saa->win.height == 0) + || saa->win.height == 0) { + up(&saa->sem); return -EINVAL; + } saa->cap |= 1; saawrite((SAA7146_MC1_TR_E_1 << 16) | 0xffff, SAA7146_MC1); } + up(&saa->sem); return 0; } case VIDIOCGFBUF: @@ -1531,6 +1548,8 @@ v.depth != 24 && v.depth != 32 && v.width > 16 && v.height > 16 && v.bytesperline > 16) return -EINVAL; + + down(&saa->sem); if (v.base) saa->win.vidadr = (unsigned long) v.base; saa->win.sheight = v.height; @@ -1542,6 +1561,7 @@ DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", v.base, v.width, v.height, saa->win.bpp, saa->win.bpl)); saa7146_set_winsize(saa); + up(&saa->sem); return 0; } case VIDIOCKEY: @@ -1569,6 +1589,9 @@ if (copy_from_user(&v, arg, sizeof(v))) return -EFAULT; i = (~(v.volume>>8))&0xff; + + down(&saa->sem); + if (!HaveCS4341) { if (v.flags & VIDEO_AUDIO_MUTE) { debiwrite(saa, debNormal, @@ -1592,6 +1615,8 @@ cs4341_setlevel(saa, i, i); } saa->audio_dev = v; + + up(&saa->sem); return 0; } @@ -1613,13 +1638,19 @@ if (copy_from_user((void *) &pmode, arg, sizeof(struct video_play_mode))) return -EFAULT; + + down(&saa->sem); + switch (pmode.mode) { case VID_PLAY_VID_OUT_MODE: if (pmode.p1 != VIDEO_MODE_NTSC && pmode.p1 != VIDEO_MODE_PAL) + { + up(&saa->sem); return -EINVAL; + } set_out_format(saa, pmode.p1); - return 0; + break; case VID_PLAY_GENLOCK: debiwrite(saa, debNormal, XILINX_CTL0, @@ -1628,7 +1659,7 @@ if (NewCard) set_genlock_offset(saa, pmode.p2); - return 0; + break; case VID_PLAY_NORMAL: debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, @@ -1636,7 +1667,7 @@ ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_PAUSE: /* IBM removed the PAUSE command */ /* they say use SINGLE_FRAME now */ @@ -1650,18 +1681,18 @@ ChipControl, 2); } saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_FAST_FORWARD: ibm_send_command(saa, IBM_MP2_FAST_FORWARD, 0, 0); saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_SLOW_MOTION: ibm_send_command(saa, IBM_MP2_SLOW_MOTION, pmode.p1, 0); saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_IMMEDIATE_NORMAL: /* ensure transfers resume */ debiwrite(saa, debNormal, @@ -1670,7 +1701,7 @@ ibm_send_command(saa, IBM_MP2_IMED_NORM_PLAY, 0, 0); saa->playmode = VID_PLAY_NORMAL; - return 0; + break; case VID_PLAY_SWITCH_CHANNELS: saa->audhead = saa->audtail = 0; saa->vidhead = saa->vidtail = 0; @@ -1688,17 +1719,17 @@ ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); saa->playmode = VID_PLAY_NORMAL; - return 0; + break; case VID_PLAY_FREEZE_FRAME: ibm_send_command(saa, IBM_MP2_FREEZE_FRAME, 0, 0); saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_STILL_MODE: ibm_send_command(saa, IBM_MP2_SET_STILL_MODE, 0, 0); saa->playmode = pmode.mode; - return 0; + break; case VID_PLAY_MASTER_MODE: if (pmode.p1 == VID_PLAY_MASTER_NONE) saa->boardcfg[1] = 0x13; @@ -1713,7 +1744,7 @@ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); - return 0; + break; case VID_PLAY_ACTIVE_SCANLINES: if (CurrentMode == VIDEO_MODE_PAL) { if (pmode.p1 < 1 || @@ -1734,28 +1765,37 @@ } set_out_format(saa, CurrentMode); case VID_PLAY_RESET: - return do_ibm_reset(saa); + err = do_ibm_reset(saa); + up(&saa->sem); + return err; case VID_PLAY_END_MARK: if (saa->endmarktail < saa->endmarkhead) { if (saa->endmarkhead - - saa->endmarktail < 2) + saa->endmarktail < 2) { + up(&saa->sem); return -ENOSPC; - } else if (saa->endmarkhead <= - saa->endmarktail) { + } + } else { if (saa->endmarktail - saa->endmarkhead > - (MAX_MARKS - 2)) + (MAX_MARKS - 2)) { + up(&saa->sem); return -ENOSPC; - } else - return -ENOSPC; + } + } saa->endmark[saa->endmarktail] = saa->audtail; saa->endmarktail++; if (saa->endmarktail >= MAX_MARKS) saa->endmarktail = 0; + break; + default: + up(&saa->sem); + return -EINVAL; } - return -EINVAL; + up(&saa->sem); + return 0; } case VIDIOCSWRITEMODE: { @@ -1791,11 +1831,13 @@ return -EFAULT; } ucode.data = udata; + down(&saa->sem); if (!strncmp(ucode.loadwhat, "decoder.aud", 11) || !strncmp(ucode.loadwhat, "decoder.vid", 11)) i = initialize_ibmmpeg2(&ucode); else i = initialize_fpga(&ucode); + up(&saa->sem); vfree(udata); if (i) return -EINVAL; @@ -1816,14 +1858,6 @@ return -EFAULT; return 0; } - case VIDIOCSCHAN: /* this makes xawtv happy */ - { - struct video_channel v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - /* do nothing */ - return 0; - } default: return -ENOIOCTLCMD; } @@ -1833,8 +1867,6 @@ static int saa_mmap(struct video_device *dev, const char *adr, unsigned long size) { - struct saa7146 *saa = (struct saa7146 *) dev; - printk(KERN_DEBUG "stradis%d: saa_mmap called\n", saa->nr); return -EINVAL; } @@ -1851,6 +1883,8 @@ unsigned long todo = count; int blocksize, split; unsigned long flags; + + down(&saa->sem); while (todo > 0) { if (saa->writemode == VID_WRITE_MPEG_AUD) { @@ -1881,19 +1915,27 @@ blocksize = todo; /* double check that we really have space */ if (!blocksize) - return -ENOSPC; + { + count =-ENOSPC; + goto out; + } if (split < blocksize) { if (copy_from_user(saa->audbuf + saa->audtail, buf, split)) - return -EFAULT; + { + count =-EFAULT; + goto out; + } buf += split; todo -= split; blocksize -= split; saa->audtail = 0; } if (copy_from_user(saa->audbuf + saa->audtail, buf, - blocksize)) - return -EFAULT; + blocksize)) { + count = -EFAULT; + goto out; + } saa->audtail += blocksize; todo -= blocksize; buf += blocksize; @@ -1926,11 +1968,17 @@ blocksize = todo; /* double check that we really have space */ if (!blocksize) - return -ENOSPC; + { + count =-ENOSPC; + goto out; + } if (split < blocksize) { if (copy_from_user(saa->vidbuf + saa->vidtail, buf, split)) - return -EFAULT; + { + count =-EFAULT; + goto out; + } buf += split; todo -= split; blocksize -= split; @@ -1938,16 +1986,25 @@ } if (copy_from_user(saa->vidbuf + saa->vidtail, buf, blocksize)) - return -EFAULT; + { + count =-EFAULT; + goto out; + } saa->vidtail += blocksize; todo -= blocksize; buf += blocksize; saa->vidtail &= 0x7ffff; } else if (saa->writemode == VID_WRITE_OSD) { if (count > 131072) - return -ENOSPC; + { + count = -ENOSPC; + goto out; + } if (copy_from_user(saa->osdbuf, buf, count)) - return -EFAULT; + { + count = -EFAULT; + goto out; + } buf += count; saa->osdhead = 0; saa->osdtail = count; @@ -1963,6 +2020,8 @@ saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); } } +out: + up(&saa->sem); return count; } @@ -2010,6 +2069,7 @@ saa = &saa7146s[num]; + init_MUTEX(&saa->sem); saa->endmarkhead = saa->endmarktail = 0; saa->win.x = saa->win.y = 0; saa->win.width = saa->win.cropwidth = 720; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/zoran.h linux.ac/drivers/media/video/zoran.h --- linux.vanilla/drivers/media/video/zoran.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/media/video/zoran.h Tue May 22 19:10:33 2001 @@ -0,0 +1,372 @@ +/* + zoran - Iomega Buz driver + + Copyright (C) 1999 Rainer Johanni + + based on + + zoran.0.0.3 Copyright (C) 1998 Dave Perks + + and + + bttv - Bt848 frame grabber driver + Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.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. +*/ + +#ifndef _BUZ_H_ +#define _BUZ_H_ + +#include + +#if LINUX_VERSION_CODE < 0x20212 +typedef struct wait_queue *wait_queue_head_t; +#endif + +/* The Buz only supports a maximum width of 720, but some V4L + applications (e.g. xawtv are more happy with 768). + If XAWTV_HACK is defined, we try to fake a device with bigger width */ + +//#define XAWTV_HACK + +//#ifdef XAWTV_HACK +//#define BUZ_MAX_WIDTH 768 /* never display more than 768 pixels */ +#define BUZ_MAX_WIDTH (zr->timing->Wa) +//#else +//#define BUZ_MAX_WIDTH 720 /* never display more than 720 pixels */ +//#endif +//#define BUZ_MAX_HEIGHT 576 /* never display more than 576 rows */ +#define BUZ_MAX_HEIGHT (zr->timing->Ha) +#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */ +#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */ + +struct zoran_requestbuffers { + unsigned long count; /* Number of buffers for MJPEG grabbing */ + unsigned long size; /* Size PER BUFFER in bytes */ +}; + +struct zoran_sync { + unsigned long frame; /* number of buffer that has been free'd */ + unsigned long length; /* number of code bytes in buffer (capture only) */ + unsigned long seq; /* frame sequence number */ + struct timeval timestamp; /* timestamp */ +}; + +struct zoran_status { + int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */ + int signal; /* Returned: 1 if valid video signal detected */ + int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int color; /* Returned: 1 if color signal detected */ +}; + +struct zoran_params { + + /* The following parameters can only be queried */ + + int major_version; /* Major version number of driver */ + int minor_version; /* Minor version number of driver */ + + /* Main control parameters */ + + int input; /* Input channel: 0 = Composite, 1 = S-VHS */ + int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int decimation; /* decimation of captured video, + enlargement of video played back. + Valid values are 1, 2, 4 or 0. + 0 is a special value where the user + has full control over video scaling */ + + /* The following parameters only have to be set if decimation==0, + for other values of decimation they provide the data how the image is captured */ + + int HorDcm; /* Horizontal decimation: 1, 2 or 4 */ + int VerDcm; /* Vertical decimation: 1 or 2 */ + int TmpDcm; /* Temporal decimation: 1 or 2, + if TmpDcm==2 in capture every second frame is dropped, + in playback every frame is played twice */ + int field_per_buff; /* Number of fields per buffer: 1 or 2 */ + int img_x; /* start of image in x direction */ + int img_y; /* start of image in y direction */ + int img_width; /* image width BEFORE decimation, + must be a multiple of HorDcm*16 */ + int img_height; /* image height BEFORE decimation, + must be a multiple of VerDcm*8 */ + + /* --- End of parameters for decimation==0 only --- */ + + /* JPEG control parameters */ + + int quality; /* Measure for quality of compressed images. + Scales linearly with the size of the compressed images. + Must be beetween 0 and 100, 100 is a compression + ratio of 1:4 */ + + int odd_even; /* Which field should come first ??? */ + + int APPn; /* Number of APP segment to be written, must be 0..15 */ + int APP_len; /* Length of data in JPEG APPn segment */ + char APP_data[60]; /* Data in the JPEG APPn segment. */ + + int COM_len; /* Length of data in JPEG COM segment */ + char COM_data[60]; /* Data in JPEG COM segment */ + + unsigned long jpeg_markers; /* Which markers should go into the JPEG output. + Unless you exactly know what you do, leave them untouched. + Inluding less markers will make the resulting code + smaller, but there will be fewer aplications + which can read it. + The presence of the APP and COM marker is + influenced by APP0_len and COM_len ONLY! */ +#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */ +#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */ +#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */ +#define JPEG_MARKER_COM (1<<6) /* Comment segment */ +#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */ + + int VFIFO_FB; /* Flag for enabling Video Fifo Feedback. + If this flag is turned on and JPEG decompressing + is going to the screen, the decompress process + is stopped every time the Video Fifo is full. + This enables a smooth decompress to the screen + but the video output signal will get scrambled */ + + /* Misc */ + + char reserved[312]; /* Makes 512 bytes for this structure */ +}; + +/* +Private IOCTL to set up for displaying MJPEG +*/ +#define BUZIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct zoran_params) +#define BUZIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct zoran_params) +#define BUZIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct zoran_requestbuffers) +#define BUZIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int) +#define BUZIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int) +#define BUZIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct zoran_sync) +#define BUZIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct zoran_status) + + +#ifdef __KERNEL__ + +#define BUZ_NUM_STAT_COM 4 +#define BUZ_MASK_STAT_COM 3 + +#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */ +#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */ + +#if VIDEO_MAX_FRAME <= 32 +# define V4L_MAX_FRAME 32 +#elif VIDEO_MAX_FRAME <= 64 +# define V4L_MAX_FRAME 64 +#else +# error "Too many video frame buffers to handle" +#endif +#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1) + + +#include "zr36057.h" + +enum card_type { + UNKNOWN = 0, + DC10, + DC10plus, + LML33, + BUZ +}; + +enum zoran_codec_mode { + BUZ_MODE_IDLE, /* nothing going on */ + BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */ + BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */ + BUZ_MODE_STILL_COMPRESS, /* still frame conversion */ + BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */ +}; + +enum zoran_buffer_state { + BUZ_STATE_USER, /* buffer is owned by application */ + BUZ_STATE_PEND, /* buffer is queued in pend[] ready to feed to I/O */ + BUZ_STATE_DMA, /* buffer is queued in dma[] for I/O */ + BUZ_STATE_DONE /* buffer is ready to return to application */ +}; + +struct zoran_gbuffer { + u32 *frag_tab; /* addresses of frag table */ + u32 frag_tab_bus; /* same value cached to save time in ISR */ + enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */ + struct zoran_sync bs; /* DONE: info to return to application */ +}; + +struct v4l_gbuffer { + char *fbuffer; /* virtual address of frame buffer */ + unsigned long fbuffer_phys; /* physical address of frame buffer */ + unsigned long fbuffer_bus; /* bus address of frame buffer */ + enum zoran_buffer_state state; /* state: unused/pending/done */ +}; + +struct tvnorm { + u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart; +}; + +struct zoran { + struct video_device video_dev; + struct i2c_bus i2c; + + int initialized; /* flag if zoran has been correctly initalized */ + int user; /* number of current users (0 or 1) */ + enum card_type card; + struct tvnorm *timing; + + unsigned short id; /* number of this device */ + char name[32]; /* name of this device */ + struct pci_dev *pci_dev; /* PCI device */ + unsigned char revision; /* revision of zr36057 */ + unsigned int zr36057_adr; /* bus address of IO mem returned by PCI BIOS */ + unsigned char *zr36057_mem; /* pointer to mapped IO memory */ + + int map_mjpeg_buffers; /* Flag which bufferset will map by next mmap() */ + + spinlock_t lock; /* Spinlock irq and hardware */ + struct semaphore sem; /* Guard parallel ioctls and mmap */ + + /* Video for Linux parameters */ + + struct video_picture picture; /* Current picture params */ + struct video_buffer buffer; /* Current buffer params */ + struct video_window window; /* Current window params */ + int buffer_set, window_set; /* Flags if the above structures are set */ + int video_interlace; /* Image on screen is interlaced */ + + u32 *overlay_mask; + wait_queue_head_t v4l_capq; + + int v4l_overlay_active; /* Overlay grab is activated */ + int v4l_memgrab_active; /* Memory grab is activated */ + + int v4l_grab_frame; /* Frame number being currently grabbed */ +#define NO_GRAB_ACTIVE (-1) + int v4l_grab_seq; /* Number of frames grabbed */ + int gwidth; /* Width of current memory capture */ + int gheight; /* Height of current memory capture */ + int gformat; /* Format of ... */ + int gbpl; /* byte per line of ... */ + + /* V4L grab queue of frames pending */ + + unsigned v4l_pend_head; + unsigned v4l_pend_tail; + int v4l_pend[V4L_MAX_FRAME]; + + struct v4l_gbuffer v4l_gbuf[VIDEO_MAX_FRAME]; /* V4L buffers' info */ + + /* Buz MJPEG parameters */ + + unsigned long jpg_nbufs; /* Number of buffers */ + unsigned long jpg_bufsize; /* Size of mjpeg buffers in bytes */ + int jpg_buffers_allocated; /* Flag if buffers are allocated */ + int need_contiguous; /* Flag if contiguous buffers are needed */ + + enum zoran_codec_mode codec_mode; /* status of codec */ + struct zoran_params params; /* structure with a lot of things to play with */ + + wait_queue_head_t jpg_capq; /* wait here for grab to finish */ + + /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */ + /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */ + /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */ + unsigned long jpg_que_head; /* Index where to put next buffer which is queued */ + unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */ + unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */ + unsigned long jpg_que_tail; /* Index of last buffer in queue */ + unsigned long jpg_seq_num; /* count of frames since grab/play started */ + unsigned long jpg_err_seq; /* last seq_num before error */ + unsigned long jpg_err_shift; + unsigned long jpg_queued_num; /* count of frames queued since grab/play started */ + + /* zr36057's code buffer table */ + u32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ + + /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */ + int jpg_pend[BUZ_MAX_FRAME]; + + /* array indexed by frame number */ + struct zoran_gbuffer jpg_gbuf[BUZ_MAX_FRAME]; /* MJPEG buffers' info */ + + /* Additional stuff for testing */ + struct proc_dir_entry *zoran_proc; + + int testing; + int jpeg_error; + int intr_counter_GIRQ1; + int intr_counter_GIRQ0; + int intr_counter_CodRepIRQ; + int intr_counter_JPEGRepIRQ; + int field_counter; + int IRQ1_in; + int IRQ1_out; + int JPEG_in; + int JPEG_out; + int JPEG_0; + int JPEG_1; + int END_event_missed; + int JPEG_missed; + int JPEG_error; + int num_errors; + int JPEG_max_missed; + int JPEG_min_missed; + + u32 last_isr; + unsigned long frame_num; + + wait_queue_head_t test_q; +}; + +#endif + +/*The following should be done in more portable way. It depends on define + of _ALPHA_BUZ in the Makefile.*/ + +#ifdef _ALPHA_BUZ +#define btwrite(dat,adr) writel((dat),(char *) (zr->zr36057_adr+(adr))) +#define btread(adr) readl(zr->zr36057_adr+(adr)) +#else +#define btwrite(dat,adr) writel((dat), (char *) (zr->zr36057_mem+(adr))) +#define btread(adr) readl(zr->zr36057_mem+(adr)) +#endif + +#define btand(dat,adr) btwrite((dat) & btread(adr), adr) +#define btor(dat,adr) btwrite((dat) | btread(adr), adr) +#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) + +#define I2C_TSA5522 0xc2 +#define I2C_TDA9850 0xb6 +#define I2C_HAUPEE 0xa0 +#define I2C_STBEE 0xae +#define I2C_SAA7111 0x48 +#define I2C_SAA7110 0x9c +#define I2C_SAA7185 0x88 +//#define I2C_ADV7175 0xd4 +#define I2C_ADV7175 0x54 + +#define TDA9850_CON1 0x04 +#define TDA9850_CON2 0x05 +#define TDA9850_CON3 0x06 +#define TDA9850_CON4 0x07 +#define TDA9850_ALI1 0x08 +#define TDA9850_ALI2 0x09 +#define TDA9850_ALI3 0x0a + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/zoran_procfs.c linux.ac/drivers/media/video/zoran_procfs.c --- linux.vanilla/drivers/media/video/zoran_procfs.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/media/video/zoran_procfs.c Tue Apr 3 17:54:46 2001 @@ -0,0 +1,170 @@ +#include +#include + +struct procfs_params_zr36067 { + char *name; + short reg; + u32 mask; + short bit; +}; + +static struct procfs_params_zr36067 zr67[] = { + {"HSPol", 0x000, 1, 30}, + {"HStart", 0x000, 0x3ff, 10}, + {"HEnd", 0x000, 0x3ff, 0}, + + {"VSPol", 0x004, 1, 30}, + {"VStart", 0x004, 0x3ff, 10}, + {"VEnd", 0x004, 0x3ff, 0}, + + {"ExtFl", 0x008, 1, 26}, + {"TopField", 0x008, 1, 25}, + {"VCLKPol", 0x008, 1, 24}, + {"DupFld", 0x008, 1, 20}, + {"LittleEndian", 0x008, 1, 0}, + + {"HsyncStart", 0x10c, 0xffff, 16}, + {"LineTot", 0x10c, 0xffff, 0}, + + {"NAX", 0x110, 0xffff, 16}, + {"PAX", 0x110, 0xffff, 0}, + + {"NAY", 0x114, 0xffff, 16}, + {"PAY", 0x114, 0xffff, 0}, +/* {"",,,}, */ + + {NULL, 0, 0, 0}, +}; + +static void setparam(struct zoran *zr, char *name, char *sval) +{ + int i, reg0, reg, val; + i = 0; + while (zr67[i].name != NULL) { + if (!strncmp(name, zr67[i].name, strlen(zr67[i].name))) { + reg = reg0 = btread(zr67[i].reg); + reg &= ~(zr67[i].mask << zr67[i].bit); + if (!isdigit(sval[0])) + break; + val = simple_strtoul(sval, NULL, 0); + if ((val & ~zr67[i].mask)) + break; + reg |= (val & zr67[i].mask) << zr67[i].bit; + printk(KERN_INFO "%s: setparam: setting ZR36067 register 0x%03x: 0x%08x=>0x%08x %s=%d\n", + zr->name, zr67[i].reg, reg0, reg, zr67[i].name, val); + btwrite(reg, zr67[i].reg); + break; + } + i++; + } +} + +/* This macro was stolen from /usr/src/drivers/char/nvram.c and modified */ +#define PRINT_PROC(args...) \ + do { \ + if (begin + len > offset + size) { \ + *eof = 0; \ + break; \ + } \ + len += sprintf( buffer+len, ##args ); \ + if (begin + len < offset) { \ + begin += len; \ + len = 0; \ + } \ + } while(0) + +static int zoran_read_proc(char *buffer, char **start, off_t offset, int size, int *eof, void *data) +{ +#ifdef CONFIG_PROC_FS + int len = 0; + off_t begin = 0; + + int i; + struct zoran *zr; + + zr = (struct zoran *) data; + DEBUG2(printk(KERN_INFO "%s: read_proc: buffer=%x, offset=%d, size=%d, data=%x\n", zr->name, (int) buffer, (int) offset, size, (int) data)); + *eof = 1; + PRINT_PROC("ZR36067 registers:"); + for (i = 0; i < 0x130; i += 4) { + if (!(i % 16)) { + PRINT_PROC("\n%03X", i); + } + PRINT_PROC(" %08X ", btread(i)); + } + PRINT_PROC("\n"); + if (offset >= len + begin) { + return 0; + } + *start = buffer + begin - offset; + return ((size < begin + len - offset) ? size : begin + len - offset); +#endif + return 0; +} + +static int zoran_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) +{ +#ifdef CONFIG_PROC_FS + char *string, *sp; + char *line, *ldelim, *varname, *svar, *tdelim; + struct zoran *zr; + + zr = (struct zoran *) data; + + string = sp = vmalloc(count + 1); + if (!string) { + printk(KERN_ERR "%s: write_proc: can not allocate memory\n", zr->name); + return -ENOMEM; + } + memcpy(string, buffer, count); + string[count] = 0; + DEBUG2(printk(KERN_INFO "%s: write_proc: name=%s count=%lu data=%x\n", zr->name, file->f_dentry->d_name.name, count, (int) data)); + ldelim = " \t\n"; + tdelim = "="; + line = strpbrk(sp, ldelim); + while (line) { + *line = 0; + svar = strpbrk(sp, tdelim); + if (svar) { + *svar = 0; + varname = sp; + svar++; + setparam(zr, varname, svar); + } + sp = line + 1; + line = strpbrk(sp, ldelim); + } + vfree(string); +#endif + return count; +} + +static int zoran_proc_init(int i) +{ +#ifdef CONFIG_PROC_FS + char name[8]; + sprintf(name, "zoran%d", i); + if ((zoran[i].zoran_proc = create_proc_entry(name, 0, 0))) { + zoran[i].zoran_proc->read_proc = zoran_read_proc; + zoran[i].zoran_proc->write_proc = zoran_write_proc; + zoran[i].zoran_proc->data = &zoran[i]; + printk(KERN_INFO "%s: procfs entry /proc/%s allocated. data=%x\n", zoran[i].name, name, (int) zoran[i].zoran_proc->data); + } else { + printk(KERN_ERR "%s: Unable to initialise /proc/%s\n", zoran[i].name, name); + return 1; + } +#endif + return 0; +} + +static void zoran_proc_cleanup(int i) +{ +#ifdef CONFIG_PROC_FS + char name[8]; + sprintf(name, "zoran%d", i); + if (zoran[i].zoran_proc) { + remove_proc_entry(name, 0); + } + zoran[i].zoran_proc = NULL; +#endif +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/zr36057.h linux.ac/drivers/media/video/zr36057.h --- linux.vanilla/drivers/media/video/zr36057.h Tue Jul 6 04:09:40 1999 +++ linux.ac/drivers/media/video/zr36057.h Tue Apr 3 17:54:47 2001 @@ -1,22 +1,22 @@ /* - zr36057.h - zr36057 register offsets + zr36057.h - zr36057 register offsets - Copyright (C) 1998 Dave Perks + Copyright (C) 1998 Dave Perks - 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. - */ + 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 _ZR36057_H_ #define _ZR36057_H_ @@ -24,19 +24,19 @@ /* Zoran ZR36057 registers */ -#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ +#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ #define ZR36057_VFEHCR_HSPol (1<<30) #define ZR36057_VFEHCR_HStart 10 #define ZR36057_VFEHCR_HEnd 0 #define ZR36057_VFEHCR_Hmask 0x3ff -#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ +#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ #define ZR36057_VFEVCR_VSPol (1<<30) #define ZR36057_VFEVCR_VStart 10 #define ZR36057_VFEVCR_VEnd 0 #define ZR36057_VFEVCR_Vmask 0x3ff -#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ +#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ #define ZR36057_VFESPFR_ExtFl (1<<26) #define ZR36057_VFESPFR_TopField (1<<25) #define ZR36057_VFESPFR_VCLKPol (1<<24) @@ -52,65 +52,65 @@ #define ZR36057_VFESPFR_Pack24 (1<<1) #define ZR36057_VFESPFR_LittleEndian (1<<0) -#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ +#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ -#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ +#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ -#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ +#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ #define ZR36057_VSSFGR_DispStride 16 #define ZR36057_VSSFGR_VidOvf (1<<8) #define ZR36057_VSSFGR_SnapShot (1<<1) #define ZR36057_VSSFGR_FrameGrab (1<<0) -#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ +#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ #define ZR36057_VDCR_VidEn (1<<31) #define ZR36057_VDCR_MinPix 24 #define ZR36057_VDCR_Triton (1<<24) #define ZR36057_VDCR_VidWinHt 12 #define ZR36057_VDCR_VidWinWid 0 -#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ +#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ -#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ +#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ -#define ZR36057_OCR 0x024 /* Overlay Control Register */ +#define ZR36057_OCR 0x024 /* Overlay Control Register */ #define ZR36057_OCR_OvlEnable (1 << 15) #define ZR36057_OCR_MaskStride 0 -#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ +#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ #define ZR36057_SPGPPCR_SoftReset (1<<24) -#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ +#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ -#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ +#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ -#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ +#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ #define ZR36057_MCTCR_CodTime (1 << 30) #define ZR36057_MCTCR_CEmpty (1 << 29) #define ZR36057_MCTCR_CFlush (1 << 28) #define ZR36057_MCTCR_CodGuestID 20 #define ZR36057_MCTCR_CodGuestReg 16 -#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ +#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ -#define ZR36057_ISR 0x03c /* Interrupt Status Register */ +#define ZR36057_ISR 0x03c /* Interrupt Status Register */ #define ZR36057_ISR_GIRQ1 (1<<30) #define ZR36057_ISR_GIRQ0 (1<<29) #define ZR36057_ISR_CodRepIRQ (1<<28) #define ZR36057_ISR_JPEGRepIRQ (1<<27) -#define ZR36057_ICR 0x040 /* Interrupt Control Register */ +#define ZR36057_ICR 0x040 /* Interrupt Control Register */ #define ZR36057_ICR_GIRQ1 (1<<30) #define ZR36057_ICR_GIRQ0 (1<<29) #define ZR36057_ICR_CodRepIRQ (1<<28) #define ZR36057_ICR_JPEGRepIRQ (1<<27) #define ZR36057_ICR_IntPinEn (1<<24) -#define ZR36057_I2CBR 0x044 /* I2C Bus Register */ +#define ZR36057_I2CBR 0x044 /* I2C Bus Register */ #define ZR36057_I2CBR_SDA (1<<1) #define ZR36057_I2CBR_SCL (1<<0) -#define ZR36057_JMC 0x100 /* JPEG Mode and Control */ +#define ZR36057_JMC 0x100 /* JPEG Mode and Control */ #define ZR36057_JMC_JPG (1 << 31) #define ZR36057_JMC_JPGExpMode (0 << 29) #define ZR36057_JMC_JPGCmpMode (1 << 29) @@ -124,45 +124,45 @@ #define ZR36057_JMC_CFIFO_FB (1 << 1) #define ZR36057_JMC_Stll_LitEndian (1 << 0) -#define ZR36057_JPC 0x104 /* JPEG Process Control */ +#define ZR36057_JPC 0x104 /* JPEG Process Control */ #define ZR36057_JPC_P_Reset (1 << 7) #define ZR36057_JPC_CodTrnsEn (1 << 5) #define ZR36057_JPC_Active (1 << 0) -#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ +#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ #define ZR36057_VSP_VsyncSize 16 #define ZR36057_VSP_FrmTot 0 -#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ +#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ #define ZR36057_HSP_HsyncStart 16 #define ZR36057_HSP_LineTot 0 -#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ +#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ #define ZR36057_FHAP_NAX 16 #define ZR36057_FHAP_PAX 0 -#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */ +#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */ #define ZR36057_FVAP_NAY 16 #define ZR36057_FVAP_PAY 0 -#define ZR36057_FPP 0x118 /* Field Process Parameters */ +#define ZR36057_FPP 0x118 /* Field Process Parameters */ #define ZR36057_FPP_Odd_Even (1 << 0) -#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ +#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ -#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ +#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ -#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ +#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ #define ZR36057_JCGI_JPEGuestID 4 #define ZR36057_JCGI_JPEGuestReg 0 -#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ +#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ -#define ZR36057_POR 0x200 /* Post Office Register */ +#define ZR36057_POR 0x200 /* Post Office Register */ #define ZR36057_POR_POPen (1<<25) #define ZR36057_POR_POTime (1<<24) #define ZR36057_POR_PODir (1<<23) -#define ZR36057_STR 0x300 /* "Still" Transfer Register */ +#define ZR36057_STR 0x300 /* "Still" Transfer Register */ #endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/zr36060.h linux.ac/drivers/media/video/zr36060.h --- linux.vanilla/drivers/media/video/zr36060.h Tue Jul 6 04:09:40 1999 +++ linux.ac/drivers/media/video/zr36060.h Tue Apr 3 17:54:47 2001 @@ -1,22 +1,22 @@ /* - zr36060.h - zr36060 register offsets + zr36060.h - zr36060 register offsets - Copyright (C) 1998 Dave Perks + Copyright (C) 1998 Dave Perks - 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. - 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. + 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. - */ + 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 _ZR36060_H_ #define _ZR36060_H_ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/media/video/zr36067.c linux.ac/drivers/media/video/zr36067.c --- linux.vanilla/drivers/media/video/zr36067.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/media/video/zr36067.c Tue May 22 12:25:14 2001 @@ -0,0 +1,4897 @@ +#define DEBUGLEVEL 0 +#define MAX_KMALLOC_MEM (128*1024) + +/* + Miro/Pinnacle Systems Inc. DC10/DC10plus and + Linux Media Labs LML33 video capture boards driver + now with IOMega BUZ support! + + Copyright (C) 2000 Serguei Miridonov + + Changes for BUZ by Wolfgang Scherr + + Based on + + Miro DC10 driver + Copyright (C) 1999 Wolfgang Scherr + + Iomega Buz driver version 1.0 + Copyright (C) 1999 Rainer Johanni + + buz.0.0.3 + Copyright (C) 1998 Dave Perks + + bttv - Bt848 frame grabber driver + Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + & Marcus Metzler (mocm@thp.uni-koeln.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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#define MAP_NR(x) virt_to_page(x) +#define ZORAN_HARDWARE VID_HARDWARE_ZR36067 + +#include + +#include +#include + +#include "zoran.h" +#include +#include + +// RJ: Test only - want to test BUZ_USE_HIMEM even when CONFIG_BIGPHYS_AREA is defined +#if !defined(CONFIG_BIGPHYS_AREA) +//#undef CONFIG_BIGPHYS_AREA +#define BUZ_USE_HIMEM +#endif + +#if defined(CONFIG_BIGPHYS_AREA) +# include +#endif + +#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | ZR36057_ISR_GIRQ1 | /* ZR36057_ISR_CodRepIRQ | */ ZR36057_ISR_JPEGRepIRQ ) // SW +//#define GPIO_MASK 0xcd +//#define GPIO_MASK 0x8d + +/* +DC10: +GPIO0 = /RESET ZR 36060 +GPIO1 = VIDEO BUS DIRECTION (0: CAPTURE, 1: DISPLAY) +GPIO2 = VIDEO BUS ENABLE (0: ON, 1: OFF) +GPIO3 = /SLEEP ZR 36060 +GPIO4 = ADC7175 (video out) FREQUENCY (0: LCC/SAA7110, 1: 27 MHz (quarz)) +GPIO5 = ZORAN FREQUENCY (0: LCC and LCC2 from SAA7110, + 1: 27 and 13.5 MHz (quarz)) +GPIO6 = /FRAME ZR 36060 +GPIO7 = /RESET ADV7175 (video out) +(I think they lost the SAA7110 reset.....) + +GIRQ0 signals that ZR36060's DATERR# line is asserted. +GIRQ1 signals a vertical sync of the video signal (VS SAA7110) + + SAA7110A: + mode 0 - Composite + mode 1 - + mode 2 - + mode 3 - + mode 4 - + mode 5 - internal Composite (from PCTV) + mode 6 - + mode 7 - S-Video + +BUZ: +GPIO0 = 1, take board out of reset +GPIO1 = 1, take JPEG codec out of sleep mode +GPIO3 = 1, deassert FRAME# to 36060 + +GIRQ0 signals a vertical sync of the video signal +GIRQ1 signals that ZR36060's DATERR# line is asserted. + +SAA7111A + + In their infinite wisdom, the Iomega engineers decided to + use the same input line for composite and S-Video Color, + although there are two entries not connected at all! + Through this ingenious strike, it is not possible to + keep two running video sources connected at the same time + to Composite and S-VHS input! + + mode 0 - N/C + mode 1 - S-Video Y + mode 2 - noise or something I don't know + mode 3 - Composite and S-Video C + mode 4 - N/C + mode 5 - S-Video (gain C independently selectable of gain Y) + mode 6 - N/C + mode 7 - S-Video (gain C adapted to gain Y) +*/ + +#define MAJOR_VERSION 0 /* driver major version */ +#define MINOR_VERSION 7 /* driver minor version */ + +#define ZORAN_NAME "zr36067" /* name of the device */ + +#define BUZ_ERR KERN_ERR ZORAN_NAME +#define BUZ_DEBUG KERN_INFO ZORAN_NAME +#define BUZ_INFO KERN_INFO ZORAN_NAME +#define BUZ_WARNING KERN_WARNING ZORAN_NAME + +#if(DEBUGLEVEL>0) +#define DEBUG1(x...) x +#else +#define DEBUG1(x...) +#endif + +#if(DEBUGLEVEL>1) +#define DEBUG2(x...) x +#else +#define DEBUG2(x...) +#endif + +#if(DEBUGLEVEL>2) +#define DEBUG3(x...) x +#else +#define DEBUG3(x...) +#endif + +#if(DEBUGLEVEL>3) +#define DEBUG4(x...) x +#else +#define DEBUG4(x...) +#endif + +/* The parameters for this driver */ + +/* + The video mem address of the video card. + The driver has a little database for some videocards + to determine it from there. If your video card is not in there + you have either to give it to the driver as a parameter + or set in in a VIDIOCSFBUF ioctl + */ + +static unsigned long vidmem = 0; /* Video memory base address */ + +/* Special purposes only: */ + +static int triton = 0; /* 0=no, 1=yes */ +static int natoma = 0; /* 0=no, 1=yes */ + +/* + Number and size of grab buffers for Video 4 Linux + The vast majority of applications should not need more than 2, + the very popular BTTV driver actually does ONLY have 2. + Time sensitive applications might need more, the maximum + is VIDEO_MAX_FRAME (defined in ). + + The size is set so that the maximum possible request + can be satisfied. Decrease it, if bigphys_area alloc'd + memory is low. If you don't have the bigphys_area patch, + set it to 128 KB. Will you allow only to grab small + images with V4L, but that's better than nothing. + + v4l_bufsize has to be given in KB ! + +*/ + +static int v4l_nbufs = 2; +static int v4l_bufsize = 128; /* Everybody should be able to work with this setting */ + +/* + Default input and video norm at startup of the driver. +*/ + +static int default_input = 0; /* 0=Composite, 1=S-VHS */ +static int default_norm = 0; /* 0=PAL, 1=NTSC 2=SECAM */ +static int lock_norm = 0; /* 1=Don't change TV standard (norm) */ + +static int pass_through = 0; /* 1=Pass through TV signal when device is not used */ + /* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */ + +static int lml33dpath = 0; /* 1 will use digital path in capture mode instead of analog. + It can be used for picture adjustments using tool like xawtv + while watching image on TV monitor connected to the output. + However, due to absence of 75 Ohm load on Bt819 input, there + will be some image imperfections */ +static int video_nr = -1; + +MODULE_PARM(vidmem, "i"); +MODULE_PARM(triton, "i"); +MODULE_PARM(natoma, "i"); +MODULE_PARM(v4l_nbufs, "i"); +MODULE_PARM(v4l_bufsize, "i"); +MODULE_PARM(default_input, "i"); +MODULE_PARM(default_norm, "i"); +MODULE_PARM(lock_norm, "i"); +MODULE_PARM(pass_through, "i"); +MODULE_PARM(lml33dpath, "i"); +MODULE_PARM(video_nr, "i"); + +/* Anybody who uses more than four? */ +#define BUZ_MAX 4 + +static int zoran_num; /* number of Buzs in use */ +static struct zoran zoran[BUZ_MAX]; + +/* forward references */ + +static void v4l_fbuffer_free(struct zoran *zr); +static void jpg_fbuffer_free(struct zoran *zr); +static void zoran_feed_stat_com(struct zoran *zr); + +/* + * Allocate the V4L grab buffers + * + * These have to be pysically contiguous. + * If v4l_bufsize <= MAX_KMALLOC_MEM we use kmalloc + * else we try to allocate them with bigphysarea_alloc_pages + * if the bigphysarea patch is present in the kernel, + * else we try to use high memory (if the user has bootet + * Linux with the necessary memory left over). + */ + +static int v4l_fbuffer_alloc(struct zoran *zr) +{ + int i, off; + unsigned char *mem; + + for (i = 0; i < v4l_nbufs; i++) { + if (zr->v4l_gbuf[i].fbuffer) + printk(KERN_WARNING + "%s: v4l_fbuffer_alloc: buffer %d allready allocated ???\n", + zr->name, i); + + //udelay(20); + if (v4l_bufsize <= MAX_KMALLOC_MEM) { + /* Use kmalloc */ + + mem = + (unsigned char *) kmalloc(v4l_bufsize, + GFP_KERNEL); + if (mem == 0) { + printk(KERN_ERR + "%s: kmalloc for V4L bufs failed\n", + zr->name); + v4l_fbuffer_free(zr); + return -ENOBUFS; + } + zr->v4l_gbuf[i].fbuffer = mem; + zr->v4l_gbuf[i].fbuffer_phys = virt_to_phys(mem); + zr->v4l_gbuf[i].fbuffer_bus = virt_to_bus(mem); + for (off = 0; off < v4l_bufsize; off += PAGE_SIZE) + mem_map_reserve(MAP_NR(mem + off)); + DEBUG1(printk + (KERN_INFO + "%s: V4L frame %d mem 0x%lx (bus: 0x%lx)\n", + zr->name, i, (unsigned long) mem, + virt_to_bus(mem))); + } else { +#if defined(CONFIG_BIGPHYS_AREA) + /* Use bigphysarea_alloc_pages */ + + int n = (v4l_bufsize + PAGE_SIZE - 1) / PAGE_SIZE; + mem = + (unsigned char *) bigphysarea_alloc_pages(n, 0, + GFP_KERNEL); + if (mem == 0) { + printk(KERN_ERR + "%s: bigphysarea_alloc_pages for V4L bufs failed\n", + zr->name); + v4l_fbuffer_free(zr); + return -ENOBUFS; + } + zr->v4l_gbuf[i].fbuffer = mem; + zr->v4l_gbuf[i].fbuffer_phys = virt_to_phys(mem); + zr->v4l_gbuf[i].fbuffer_bus = virt_to_bus(mem); + DEBUG1(printk + (KERN_INFO + "%s: Bigphysarea frame %d mem 0x%x (bus: 0x%x)\n", + zr->name, i, (unsigned) mem, + (unsigned) virt_to_bus(mem))); + + /* Zero out the allocated memory */ + memset(zr->v4l_gbuf[i].fbuffer, 0, v4l_bufsize); +#else + /* No bigphysarea present, usage of high memory disabled, + but user wants buffers of more than MAX_KMALLOC_MEM */ + printk(KERN_ERR + "%s: No bigphysarea_patch present, usage of high memory disabled,\n", + zr->name); + printk(KERN_ERR + "%s: sorry, could not allocate V4L buffers of size %d KB.\n", + zr->name, v4l_bufsize >> 10); + return -ENOBUFS; +#endif + } + } + + return 0; +} + +/* free the V4L grab buffers */ + +static void v4l_fbuffer_free(struct zoran *zr) +{ + int i, off; + unsigned char *mem; + + for (i = 0; i < v4l_nbufs; i++) { + if (!zr->v4l_gbuf[i].fbuffer) + continue; + + if (v4l_bufsize <= MAX_KMALLOC_MEM) { + mem = zr->v4l_gbuf[i].fbuffer; + for (off = 0; off < v4l_bufsize; off += PAGE_SIZE) + mem_map_unreserve(MAP_NR(mem + off)); + kfree((void *) zr->v4l_gbuf[i].fbuffer); + } +#if defined(CONFIG_BIGPHYS_AREA) + else + bigphysarea_free_pages((void *) zr->v4l_gbuf[i]. + fbuffer); +#endif + zr->v4l_gbuf[i].fbuffer = NULL; + } +} + +/* + * Allocate the MJPEG grab buffers. + * + * If the requested buffer size is smaller than MAX_KMALLOC_MEM, + * kmalloc is used to request a physically contiguous area, + * else we allocate the memory in framgents with get_free_page. + * + * If a Natoma chipset is present and this is a revision 1 zr36057, + * each MJPEG buffer needs to be physically contiguous. + * (RJ: This statement is from Dave Perks' original driver, + * I could never check it because I have a zr36067) + * The driver cares about this because it reduces the buffer + * size to MAX_KMALLOC_MEM in that case (which forces contiguous allocation). + * + * RJ: The contents grab buffers needs never be accessed in the driver. + * Therefore there is no need to allocate them with vmalloc in order + * to get a contiguous virtual memory space. + * I don't understand why many other drivers first allocate them with + * vmalloc (which uses internally also get_free_page, but delivers you + * virtual addresses) and then again have to make a lot of efforts + * to get the physical address. + * + */ + +static int jpg_fbuffer_alloc(struct zoran *zr) +{ + int i, j, off; //alloc_contig; + unsigned long mem; + + /* Decide if we should alloc contiguous or fragmented memory */ + /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */ + + //alloc_contig = (zr->jpg_bufsize <= MAX_KMALLOC_MEM); + + for (i = 0; i < zr->jpg_nbufs; i++) { + if (zr->jpg_gbuf[i].frag_tab) + printk(KERN_WARNING + "%s: jpg_fbuffer_alloc: buffer %d allready allocated ???\n", + zr->name, i); + + /* Allocate fragment table for this buffer */ + + mem = get_free_page(GFP_KERNEL); + if (mem == 0) { + printk(KERN_ERR + "%s: jpg_fbuffer_alloc: get_free_page (frag_tab) failed for buffer %d\n", + zr->name, i); + jpg_fbuffer_free(zr); + return -ENOBUFS; + } + memset((void *) mem, 0, PAGE_SIZE); + zr->jpg_gbuf[i].frag_tab = (u32 *) mem; + zr->jpg_gbuf[i].frag_tab_bus = virt_to_bus((void *) mem); + + //if (alloc_contig) { + if (zr->need_contiguous) { + mem = (unsigned long) kmalloc(zr->jpg_bufsize, GFP_KERNEL); + if (mem == 0) { + printk(KERN_ERR "%s: jpg_fbuffer_alloc: kmalloc failed for buffer %d\n", + zr->name, i); + jpg_fbuffer_free(zr); + return -ENOBUFS; + } + zr->jpg_gbuf[i].frag_tab[0] = virt_to_bus((void *) mem); + zr->jpg_gbuf[i].frag_tab[1] = + ((zr->jpg_bufsize / 4) << 1) | 1; + for (off = 0; off < zr->jpg_bufsize; off += PAGE_SIZE) + mem_map_reserve(MAP_NR(mem + off)); + } else { + /* jpg_bufsize is allreay page aligned */ + for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) + { + mem = get_free_page(GFP_KERNEL); + if (mem == 0) { + printk(KERN_ERR + "%s: jpg_fbuffer_alloc: get_free_page failed for buffer %d\n", + zr->name, i); + jpg_fbuffer_free(zr); + return -ENOBUFS; + } + + zr->jpg_gbuf[i].frag_tab[2 * j] = + virt_to_bus((void *) mem); + zr->jpg_gbuf[i].frag_tab[2 * j + 1] = + (PAGE_SIZE / 4) << 1; + mem_map_reserve(MAP_NR(mem)); + } + + zr->jpg_gbuf[i].frag_tab[2 * j - 1] |= 1; + } + } + + DEBUG1(printk + ("%s: jpg_fbuffer_alloc: %ld KB allocated\n", zr->name, + (zr->jpg_nbufs * zr->jpg_bufsize) >> 10)); + zr->jpg_buffers_allocated = 1; + return 0; +} + +/* free the MJPEG grab buffers */ +static void jpg_fbuffer_free(struct zoran *zr) +{ + int i, j, off; // alloc_contig; + unsigned char *mem; + + /* Decide if we should alloc contiguous or fragmented memory */ + /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */ + + //alloc_contig = (zr->jpg_bufsize <= MAX_KMALLOC_MEM); + + for (i = 0; i < zr->jpg_nbufs; i++) { + if (!zr->jpg_gbuf[i].frag_tab) + continue; + + //if (alloc_contig) { + if (zr->need_contiguous) { + if (zr->jpg_gbuf[i].frag_tab[0]) { + mem = + (unsigned char *) bus_to_virt(zr-> + jpg_gbuf + [i]. + frag_tab + [0]); + for (off = 0; off < zr->jpg_bufsize; + off += PAGE_SIZE) + mem_map_unreserve(MAP_NR + (mem + off)); + kfree((void *) mem); + zr->jpg_gbuf[i].frag_tab[0] = 0; + zr->jpg_gbuf[i].frag_tab[1] = 0; + } + } else { + for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) { + if (!zr->jpg_gbuf[i].frag_tab[2 * j]) + break; + mem_map_unreserve(MAP_NR + (bus_to_virt + (zr->jpg_gbuf[i]. + frag_tab[2 * j]))); + free_page((unsigned long) + bus_to_virt(zr->jpg_gbuf[i]. + frag_tab[2 * j])); + zr->jpg_gbuf[i].frag_tab[2 * j] = 0; + zr->jpg_gbuf[i].frag_tab[2 * j + 1] = 0; + } + } + + free_page((unsigned long) zr->jpg_gbuf[i].frag_tab); + zr->jpg_gbuf[i].frag_tab = NULL; + } + zr->jpg_buffers_allocated = 0; +} + + +/* ----------------------------------------------------------------------- */ + +/* I2C functions */ + +#define I2C_DELAY 10 + + +/* software I2C functions */ + +static void i2c_setlines(struct i2c_bus *bus, int ctrl, int data) +{ + struct zoran *zr = (struct zoran *) bus->data; + btwrite((data << 1) | ctrl, ZR36057_I2CBR); + udelay(I2C_DELAY); +} + +static int i2c_getdataline(struct i2c_bus *bus) +{ + struct zoran *zr = (struct zoran *) bus->data; + return (btread(ZR36057_I2CBR) >> 1) & 1; +} + +static void attach_inform(struct i2c_bus *bus, int id) +{ + int i; + struct zoran *zr = (struct zoran *) bus->data; + + DEBUG1(printk(KERN_DEBUG "%s: i2c attach %02x\n", zr->name, id)); + for (i = 0; i < bus->devcount; i++) { + if (strcmp(bus->devices[i]->name, "saa7110") == 0) { + if (zr->revision < 2) { + zr->card = DC10; + sprintf(zr->name, "DC10[%u]", zr->id); + } else { + zr->card = DC10plus; + sprintf(zr->name, "DC10plus[%u]", zr->id); + } + break; + } + if (strcmp(bus->devices[i]->name, "bt819") == 0) { + zr->card = LML33; + sprintf(zr->name, "LML33[%u]", zr->id); + break; + } + if (strcmp(bus->devices[i]->name, "saa7111") == 0) { + zr->card = BUZ; + sprintf(zr->name, "Buz[%u]", zr->id); + break; + } + } +} + +static void detach_inform(struct i2c_bus *bus, int id) +{ + DEBUG1(struct zoran *zr = (struct zoran *) bus->data); + DEBUG1(printk(KERN_DEBUG "%s: i2c detach %02x\n", zr->name, id)); +} + +static struct i2c_bus zoran_i2c_bus_template = { + "zr36057", + I2C_BUSID_BT848, + NULL, + + SPIN_LOCK_UNLOCKED, + + attach_inform, + detach_inform, + + i2c_setlines, + i2c_getdataline, + NULL, + NULL, +}; + +/* + * Set the registers for the size we have specified. Don't bother + * trying to understand this without the ZR36057 manual in front of + * you [AC]. + * + * PS: The manual is free for download in .pdf format from + * www.zoran.com - nicely done those folks. + */ + +static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 }; +static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 }; + +static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 }; +static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 }; + +static struct tvnorm *dc10norms[] = { + &f50sqpixel, /* PAL-BDGHI */ + &f60sqpixel, /* NTSC */ + &f50sqpixel, /* SECAM */ +}; + +static struct tvnorm *lml33norms[] = { + &f50ccir601, /* PAL-BDGHI */ + &f60ccir601, /* NTSC */ + NULL, /* SECAM (not supported in LML33) */ +}; + +static struct tvnorm *buznorms[] = { + &f50ccir601, /* PAL-BDGHI */ + &f60ccir601, /* NTSC */ + NULL, /* SECAM */ +}; + +static struct tvnorm *unsupported[] = { + NULL, /* PAL-BDGHI */ + NULL, /* NTSC */ + NULL, /* SECAM */ +}; + +static struct tvnorm **cardnorms[] = { + unsupported, /* UNKNOWN */ + dc10norms, /* DC10 */ + dc10norms, /* DC10plus */ + lml33norms, /* LML33 */ + buznorms, /* Buz */ +}; + +static u32 cardvsync[] = { + 0, /* UNKNOWN */ + ZR36057_ISR_GIRQ1, /* DC10 */ + ZR36057_ISR_GIRQ1, /* DC10plus */ + ZR36057_ISR_GIRQ0, /* LML33 */ + ZR36057_ISR_GIRQ0, /* Buz */ +}; + +static u32 cardjpegint[] = { + 0, /* UNKNOWN */ + ZR36057_ISR_GIRQ0, /* DC10 */ + ZR36057_ISR_GIRQ0, /* DC10plus */ + ZR36057_ISR_GIRQ1, /* LML33 */ + ZR36057_ISR_GIRQ1, /* Buz */ +}; + +static int format2bpp(int format) +{ + int bpp; + + /* Determine the number of bytes per pixel for the video format requested */ + + switch (format) { + + case VIDEO_PALETTE_YUV422: + bpp = 2; + break; + + case VIDEO_PALETTE_RGB555: + bpp = 2; + break; + + case VIDEO_PALETTE_RGB565: + bpp = 2; + break; + + case VIDEO_PALETTE_RGB24: + bpp = 3; + break; + + case VIDEO_PALETTE_RGB32: + bpp = 4; + break; + + default: + bpp = 0; + } + + return bpp; +} + +static void zr36057_adjust_vfe(struct zoran *zr, + enum zoran_codec_mode mode) +{ + u32 reg; + switch (mode) { + case BUZ_MODE_MOTION_DECOMPRESS: + btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); + reg = btread(ZR36057_VFEHCR); + if (reg & (1 << 10)) { + reg += ((1 << 10) | 1); + } + btwrite(reg, ZR36057_VFEHCR); + break; + case BUZ_MODE_MOTION_COMPRESS: + case BUZ_MODE_IDLE: + default: + if (zr->params.norm == VIDEO_MODE_NTSC) + btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); + else + btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); + reg = btread(ZR36057_VFEHCR); + if (!(reg & (1 << 10))) { + reg -= ((1 << 10) | 1); + } + btwrite(reg, ZR36057_VFEHCR); + break; + } +} + +/* + * set geometry + */ +static void zr36057_set_vfe(struct zoran *zr, int video_width, + int video_height, unsigned int video_format) +{ + struct tvnorm *tvn; + unsigned HStart, HEnd, VStart, VEnd; + unsigned DispMode; + unsigned VidWinWid, VidWinHt; + unsigned hcrop1, hcrop2, vcrop1, vcrop2; + unsigned Wa, We, Ha, He; + unsigned X, Y, HorDcm, VerDcm; + u32 reg; + unsigned mask_line_size; + + tvn = zr->timing; + + Wa = tvn->Wa; + Ha = tvn->Ha; + + DEBUG1(printk (BUZ_INFO ": width = %d, height = %d\n", video_width, video_height)); + + if (zr->params.norm != VIDEO_MODE_PAL + && zr->params.norm != VIDEO_MODE_NTSC + && zr->params.norm != VIDEO_MODE_SECAM) { + printk(KERN_ERR "%s: set_vfe: video_norm = %d not valid\n", + zr->name, zr->params.norm); + return; + } + if (video_width < BUZ_MIN_WIDTH || video_height < BUZ_MIN_HEIGHT + || video_width > Wa || video_height > Ha) { + printk(KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n", + zr->name, video_width, video_height); + return; + } + + /* if window has more than half of active height, + switch on interlacing - we want the full information */ + + zr->video_interlace = (video_height > Ha / 2); + + /**** zr36057 ****/ + + /* horizontal */ + VidWinWid = video_width; + X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa; + We = (VidWinWid * 64) / X; + HorDcm = 64 - X; + hcrop1 = 2 * ((tvn->Wa - We) / 4); + hcrop2 = tvn->Wa - We - hcrop1; + HStart = tvn->HStart | 1; + if (zr->card == LML33) + HStart += 62; + if (zr->card == BUZ) { //HStart += 67; + HStart += 44; + } + HEnd = HStart + tvn->Wa - 1; + HStart += hcrop1; + HEnd -= hcrop2; + reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart) + | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd); + if (zr->card != BUZ) + reg |= ZR36057_VFEHCR_HSPol; + btwrite(reg, ZR36057_VFEHCR); + + /* Vertical */ + DispMode = !zr->video_interlace; + VidWinHt = DispMode ? video_height : video_height / 2; + Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha; + He = (VidWinHt * 64) / Y; + VerDcm = 64 - Y; + vcrop1 = (tvn->Ha / 2 - He) / 2; + vcrop2 = tvn->Ha / 2 - He - vcrop1; + VStart = tvn->VStart; + VEnd = VStart + tvn->Ha / 2 - 1; + VStart += vcrop1; + VEnd -= vcrop2; + reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart) + | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd); + reg |= ZR36057_VFEVCR_VSPol; + btwrite(reg, ZR36057_VFEVCR); + + /* scaler and pixel format */ + reg = 0; + reg |= (HorDcm << ZR36057_VFESPFR_HorDcm); + reg |= (VerDcm << ZR36057_VFESPFR_VerDcm); + reg |= (DispMode << ZR36057_VFESPFR_DispMode); + reg |= ZR36057_VFESPFR_LittleEndian; + /* RJ: I don't know, why the following has to be the opposite + of the corresponding ZR36060 setting, but only this way + we get the correct colors when uncompressing to the screen */ + //reg |= ZR36057_VFESPFR_VCLKPol; /**/ + /* RJ: Don't know if that is needed for NTSC also */ + if (zr->params.norm != VIDEO_MODE_NTSC) + reg |= ZR36057_VFESPFR_ExtFl; // NEEDED!!!!!!! Wolfgang + reg |= ZR36057_VFESPFR_TopField; + switch (video_format) { + + case VIDEO_PALETTE_YUV422: + reg |= ZR36057_VFESPFR_YUV422; + break; + + case VIDEO_PALETTE_RGB555: + reg |= ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ErrDif; + break; + + case VIDEO_PALETTE_RGB565: + reg |= ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ErrDif; + break; + + case VIDEO_PALETTE_RGB24: + reg |= ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_Pack24; + break; + + case VIDEO_PALETTE_RGB32: + reg |= ZR36057_VFESPFR_RGB888; + break; + + default: + printk(KERN_INFO "%s: Unknown color_fmt=%x\n", zr->name, + video_format); + return; + + } + if (HorDcm >= 48) { + reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */ + } else if (HorDcm >= 32) { + reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */ + } else if (HorDcm >= 16) { + reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */ + } + btwrite(reg, ZR36057_VFESPFR); + + /* display configuration */ + + reg = (16 << ZR36057_VDCR_MinPix) + | (VidWinHt << ZR36057_VDCR_VidWinHt) + | (VidWinWid << ZR36057_VDCR_VidWinWid); + if (triton || zr->revision <= 1) + reg &= ~ZR36057_VDCR_Triton; + else + reg |= ZR36057_VDCR_Triton; + btwrite(reg, ZR36057_VDCR); + + /* Write overlay clipping mask data, but don't enable overlay clipping */ + /* RJ: since this makes only sense on the screen, we use + zr->window.width instead of video_width */ + + mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; + reg = virt_to_bus(zr->overlay_mask); + btwrite(reg, ZR36057_MMTR); + reg = virt_to_bus(zr->overlay_mask + mask_line_size); + btwrite(reg, ZR36057_MMBR); + reg = mask_line_size - (zr->window.width + 31) / 32; + if (DispMode == 0) + reg += mask_line_size; + reg <<= ZR36057_OCR_MaskStride; + btwrite(reg, ZR36057_OCR); + + zr36057_adjust_vfe(zr, zr->codec_mode); + +} + +/* + * Switch overlay on or off + */ + +static void zr36057_overlay(struct zoran *zr, int on) +{ + int fmt, bpp; + u32 reg; + + if (on) { + /* do the necessary settings ... */ + + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */ + + switch (zr->buffer.depth) { + case 15: + fmt = VIDEO_PALETTE_RGB555; + bpp = 2; + break; + case 16: + fmt = VIDEO_PALETTE_RGB565; + bpp = 2; + break; + case 24: + fmt = VIDEO_PALETTE_RGB24; + bpp = 3; + break; + case 32: + fmt = VIDEO_PALETTE_RGB32; + bpp = 4; + break; + default: + fmt = 0; + bpp = 0; + } + + zr36057_set_vfe(zr, zr->window.width, zr->window.height, + fmt); + + /* Start and length of each line MUST be 4-byte aligned. + This should be allready checked before the call to this routine. + All error messages are internal driver checking only! */ + + /* video display top and bottom registers */ + + reg = + (u32) zr->buffer.base + zr->window.x * bpp + + zr->window.y * zr->buffer.bytesperline; + btwrite(reg, ZR36057_VDTR); + if (reg & 3) + printk(KERN_ERR + "%s: zr36057_overlay: video_address not aligned\n", + zr->name); + if (zr->video_interlace) + reg += zr->buffer.bytesperline; + btwrite(reg, ZR36057_VDBR); + + /* video stride, status, and frame grab register */ + + reg = zr->buffer.bytesperline - zr->window.width * bpp; + if (zr->video_interlace) + reg += zr->buffer.bytesperline; + if (reg & 3) + printk(KERN_ERR + "%s: zr36057_overlay: video_stride not aligned\n", + zr->name); + reg = (reg << ZR36057_VSSFGR_DispStride); + reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */ + btwrite(reg, ZR36057_VSSFGR); + + /* Set overlay clipping */ + + if (zr->window.clipcount) + btor(ZR36057_OCR_OvlEnable, ZR36057_OCR); + + /* ... and switch it on */ + + btor(ZR36057_VDCR_VidEn, ZR36057_VDCR); + } else { + /* Switch it off */ + + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); + } +} + +/* + * The overlay mask has one bit for each pixel on a scan line, + * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels. + */ +static void write_overlay_mask(struct zoran *zr, struct video_clip *vp, + int count) +{ + unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; + u32 *mask; + int x, y, width, height; + unsigned i, j, k; + u32 reg; + + /* fill mask with one bits */ + memset(zr->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT); + reg = 0; + + for (i = 0; i < count; ++i) { + /* pick up local copy of clip */ + x = vp[i].x; + y = vp[i].y; + width = vp[i].width; + height = vp[i].height; + + /* trim clips that extend beyond the window */ + if (x < 0) { + width += x; + x = 0; + } + if (y < 0) { + height += y; + y = 0; + } + if (x + width > zr->window.width) { + width = zr->window.width - x; + } + if (y + height > zr->window.height) { + height = zr->window.height - y; + } + + /* ignore degenerate clips */ + if (height <= 0) { + continue; + } + if (width <= 0) { + continue; + } + + /* apply clip for each scan line */ + for (j = 0; j < height; ++j) { + /* reset bit for each pixel */ + /* this can be optimized later if need be */ + mask = zr->overlay_mask + (y + j) * mask_line_size; + for (k = 0; k < width; ++k) { + mask[(x + k) / 32] &= + ~((u32) 1 << (x + k) % 32); + } + } + } +} + +/* Enable/Disable uncompressed memory grabbing of the 36057 */ + +static void zr36057_set_memgrab(struct zoran *zr, int mode) +{ + if (mode) { + if (btread(ZR36057_VSSFGR) & + (ZR36057_VSSFGR_SnapShot | ZR36057_VSSFGR_FrameGrab)) + printk(KERN_WARNING + "%s: zr36057_set_memgrab_on with SnapShot or FrameGrab on ???\n", + zr->name); + + /* switch on VSync interrupts */ + + btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts + btor(cardvsync[zr->card], ZR36057_ICR); // SW + + /* enable SnapShot */ + + btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); + + /* Set zr36057 video front end and enable video */ + + zr36057_set_vfe(zr, zr->gwidth, zr->gheight, zr->gformat); + + zr->v4l_memgrab_active = 1; + } else { + zr->v4l_memgrab_active = 0; + + /* switch off VSync interrupts */ + + //btand(~ZR36057_ICR_GIRQ1, ZR36057_ICR); // SW + + /* reenable grabbing to screen if it was running */ + + if (zr->v4l_overlay_active) { + zr36057_overlay(zr, 1); + } else { + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); + btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); + } + } +} + +static int wait_grab_pending(struct zoran *zr) +{ + unsigned long flags; + + /* wait until all pending grabs are finished */ + + if (!zr->v4l_memgrab_active) + return 0; + + while (zr->v4l_pend_tail != zr->v4l_pend_head) { + interruptible_sleep_on(&zr->v4l_capq); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + spin_lock_irqsave(&zr->lock, flags); + zr36057_set_memgrab(zr, 0); + spin_unlock_irqrestore(&zr->lock, flags); + + return 0; +} + +/* + * V4L Buffer grabbing + */ + +static int v4l_grab(struct zoran *zr, struct video_mmap *mp) +{ + unsigned long flags; + int res, bpp; + + /* + * There is a long list of limitations to what is allowed to be grabbed + * We don't output error messages here, since some programs (e.g. xawtv) + * just try several settings to find out what is valid or not. + */ + + /* No grabbing outside the buffer range! */ + + if (mp->frame >= v4l_nbufs || mp->frame < 0) { + DEBUG2(printk + (KERN_ERR "%s: Can not grab frame %d\n", zr->name, + mp->frame)); + return -EINVAL; + } + + /* Check size and format of the grab wanted */ + + if (mp->height < BUZ_MIN_HEIGHT || mp->width < BUZ_MIN_WIDTH + || mp->height > BUZ_MAX_HEIGHT || mp->width > BUZ_MAX_WIDTH) { + DEBUG2(printk + (KERN_ERR "%s: Wrong frame size.\n", zr->name)); + return -EINVAL; + } + + bpp = format2bpp(mp->format); + if (bpp == 0) { + DEBUG2(printk + (KERN_ERR "%s: Wrong bytes-per-pixel format\n", + zr->name)); + return -EINVAL; + } + + /* Check against available buffer size */ + + if (mp->height * mp->width * bpp > v4l_bufsize) { + DEBUG2(printk + (KERN_ERR "%s: Video buffer size is too small\n", + zr->name)); + return -EINVAL; + } + + /* The video front end needs 4-byte alinged line sizes */ + + if ((bpp == 2 && (mp->width & 1)) || (bpp == 3 && (mp->width & 3))) { + DEBUG2(printk + (KERN_ERR "%s: Wrong frame alingment\n", zr->name)); + return -EINVAL; + } + + /* + * To minimize the time spent in the IRQ routine, we avoid setting up + * the video front end there. + * If this grab has different parameters from a running streaming capture + * we stop the streaming capture and start it over again. + */ + + if (zr->v4l_memgrab_active + && (zr->gwidth != mp->width || zr->gheight != mp->height + || zr->gformat != mp->format)) { + res = wait_grab_pending(zr); + if (res) + return res; + } + zr->gwidth = mp->width; + zr->gheight = mp->height; + zr->gformat = mp->format; + zr->gbpl = bpp * zr->gwidth; + + + spin_lock_irqsave(&zr->lock, flags); + + /* make sure a grab isn't going on currently with this buffer */ + + switch (zr->v4l_gbuf[mp->frame].state) { + + default: + case BUZ_STATE_PEND: + res = -EBUSY; /* what are you doing? */ + break; + + case BUZ_STATE_USER: + case BUZ_STATE_DONE: + /* since there is at least one unused buffer there's room for at least one more pend[] entry */ + zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = + mp->frame; + zr->v4l_gbuf[mp->frame].state = BUZ_STATE_PEND; + res = 0; + break; + + } + + /* put the 36057 into frame grabbing mode */ + + if (!res && !zr->v4l_memgrab_active) + zr36057_set_memgrab(zr, 1); + + spin_unlock_irqrestore(&zr->lock, flags); + //DEBUG2(printk(KERN_INFO "%s: Frame grab 3...\n", zr->name)); + + return res; +} + +/* + * Sync on a V4L buffer + */ + +static int v4l_sync(struct zoran *zr, int frame) +{ + unsigned long flags; + + /* check passed-in frame number */ + + if (frame >= v4l_nbufs || frame < 0) { + DEBUG1(printk(KERN_ERR "%s: v4l_sync: frame %d is invalid\n", + zr->name, frame)); + return -EINVAL; + } + + /* Check if is buffer was queued at all */ + + if (zr->v4l_gbuf[frame].state == BUZ_STATE_USER) { + DEBUG1(printk(KERN_ERR + "%s: v4l_sync: Attempt to sync on a buffer which was not queued?\n", + zr->name)); + return -EPROTO; + } + + /* wait on this buffer to get ready */ + + while (zr->v4l_gbuf[frame].state == BUZ_STATE_PEND) { + interruptible_sleep_on(&zr->v4l_capq); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + /* buffer should now be in BUZ_STATE_DONE */ + + if (zr->v4l_gbuf[frame].state != BUZ_STATE_DONE) + printk(KERN_ERR "%s: v4l_sync - internal error\n", + zr->name); + + /* Check if streaming capture has finished */ + + spin_lock_irqsave(&zr->lock, flags); + + if (zr->v4l_pend_tail == zr->v4l_pend_head) + zr36057_set_memgrab(zr, 0); + + spin_unlock_irqrestore(&zr->lock, flags); + + return 0; +} + +/***************************************************************************** + * * + * Set up the Buz-specific MJPEG part * + * * + *****************************************************************************/ + +/* +Wait til post office is no longer busy */ +static int post_office_wait(struct zoran *zr) +{ + u32 por; + +// while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) { + while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) { + /* wait for something to happen */ + } + if ((por & ZR36057_POR_POTime) && zr->card != LML33 + && zr->card != BUZ) { + /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */ + printk(KERN_WARNING "%s: pop timeout %08x\n", zr->name, por); + return -1; + } + return 0; +} + +static int post_office_write(struct zoran *zr, unsigned guest, + unsigned reg, unsigned value) +{ + u32 por; + + por = + ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) | + ((reg & 7) << 16) | (value & 0xFF); + btwrite(por, ZR36057_POR); + return post_office_wait(zr); +} + +static int post_office_read(struct zoran *zr, unsigned guest, unsigned reg) +{ + u32 por; + + por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16); + btwrite(por, ZR36057_POR); + if (post_office_wait(zr) < 0) { + return -1; + } + return btread(ZR36057_POR) & 0xFF; +} + +static int zr36060_write_8(struct zoran *zr, unsigned reg, unsigned val) +{ + if (post_office_wait(zr) + || post_office_write(zr, 0, 1, reg >> 8) + || post_office_write(zr, 0, 2, reg)) { + return -1; + } + return post_office_write(zr, 0, 3, val); +} + +static int zr36060_write_16(struct zoran *zr, unsigned reg, unsigned val) +{ + if (zr36060_write_8(zr, reg + 0, val >> 8)) { + return -1; + } + return zr36060_write_8(zr, reg + 1, val >> 0); +} + +static int zr36060_write_24(struct zoran *zr, unsigned reg, unsigned val) +{ + if (zr36060_write_8(zr, reg + 0, val >> 16)) { + return -1; + } + return zr36060_write_16(zr, reg + 1, val >> 0); +} + +static int zr36060_write_32(struct zoran *zr, unsigned reg, unsigned val) +{ + if (zr36060_write_16(zr, reg + 0, val >> 16)) { + return -1; + } + return zr36060_write_16(zr, reg + 2, val >> 0); +} + +static u32 zr36060_read_8(struct zoran *zr, unsigned reg) +{ + if (post_office_wait(zr) + || post_office_write(zr, 0, 1, reg >> 8) + || post_office_write(zr, 0, 2, reg)) { + return -1; + } + return post_office_read(zr, 0, 3) & 0xFF; +} + +/* ----------------------------------------------------------------------- */ + +static void GPIO(struct zoran *zr, unsigned bit, unsigned value) +{ + u32 reg; + u32 mask; + + mask = 1 << (24 + bit); + reg = btread(ZR36057_GPPGCR1) & ~mask; + if (value) { + reg |= mask; + } + btwrite(reg, ZR36057_GPPGCR1); + udelay(1); +} + + +static void zr36060_sleep(struct zoran *zr, int sleep) +{ + switch (zr->card) { + case DC10: + case DC10plus: + GPIO(zr, 3, !sleep); + break; + case BUZ: + case LML33: + GPIO(zr, 1, !sleep); + break; + default: + break; + } + if (!sleep) + udelay(500); + else + udelay(2); +} + +static int zr36060_reset(struct zoran *zr) +{ + switch (zr->card) { + case DC10: + case DC10plus: + zr36060_sleep(zr, 0); + GPIO(zr, 0, 0); + udelay(2); + GPIO(zr, 0, 1); + udelay(2); + break; + case LML33: + case BUZ: + zr36060_sleep(zr, 0); + post_office_write(zr, 3, 0, 0); + udelay(2); + default: + } + return 0; +} + +static void set_frame(struct zoran *zr, int val) +{ + switch (zr->card) { + case DC10: + case DC10plus: + GPIO(zr, 6, val); + break; + case LML33: + case BUZ: + GPIO(zr, 3, val); + break; + default: + break; + } +} + +static void set_videobus_dir(struct zoran *zr, int val) +{ + switch (zr->card) { + case DC10: + case DC10plus: + GPIO(zr, 1, val); + break; + case LML33: + if (lml33dpath == 0) + GPIO(zr, 5, val); + else + GPIO(zr, 5, 1); + break; + case BUZ: + default: + break; + } +} + +static void set_videobus_enable(struct zoran *zr, int val) +{ + switch (zr->card) { + case LML33: + GPIO(zr, 7, val); + break; + case DC10: + case DC10plus: + case BUZ: + default: + break; + } +} + +static void zr36060_set_jpg(struct zoran *zr, enum zoran_codec_mode mode) +{ + struct tvnorm *tvn; + u32 reg; + int size, blocks; + + reg = (1 << 0) /* CodeMstr */ + |(0 << 2) /* CFIS=0 */ + |(0 << 6) /* Endian=0 */ + |(0 << 7); /* Code16=0 */ + zr36060_write_8(zr, 0x002, reg); + + switch (mode) { + + case BUZ_MODE_MOTION_DECOMPRESS: + case BUZ_MODE_STILL_DECOMPRESS: + reg = 0x00; /* Codec mode = decompression */ + break; + + case BUZ_MODE_MOTION_COMPRESS: + case BUZ_MODE_STILL_COMPRESS: + default: + reg = 0xa4; /* Codec mode = compression with variable scale factor */ + break; + + } + zr36060_write_8(zr, 0x003, reg); + + reg = 0x00; /* reserved, mbz */ + zr36060_write_8(zr, 0x004, reg); + + /* code volume */ + + /* Target field size in pixels: */ + tvn = zr->timing; + size = + (tvn->Ha / 2) * (tvn->Wa) / (zr->params.HorDcm) / + (zr->params.VerDcm); + blocks = size / 64; + + /* Target compressed field size in bits: */ + size = size * 16; /* uncompressed size in bits */ + size = size * zr->params.quality / 400; /* quality = 100 is a compression ratio 1:4 */ + + /* Lower limit (arbitrary, 1 KB) */ + if (size < 8192) + size = 8192; + + /* Upper limit: 6/8 of the code buffers */ + if (size * zr->params.field_per_buff > zr->jpg_bufsize * 6) + size = zr->jpg_bufsize * 6 / zr->params.field_per_buff; + + reg = size * 4 / blocks; + if (reg > 0xf0) + reg = 0xf0; /* 480 bits/block, does 0xff represents unlimited? */ + zr36060_write_8(zr, 0x005, reg); + + /* JPEG markers */ + reg = (zr->params.jpeg_markers) & 0x38; /* DRI, DQT, DHT */ + if (zr->params.COM_len) + reg |= JPEG_MARKER_COM; + if (zr->params.APP_len) + reg |= JPEG_MARKER_APP; + zr36060_write_8(zr, 0x006, reg); + + if (zr->card != LML33 && zr->card != BUZ) { + reg = (0 << 3) /* EOAV=0 */ + |(0 << 2) /* EOI=0 */ + |(0 << 1) /* END=0 */ + |(1 << 0); /* DATERR=1 */ + } else { + reg = (0 << 3) /* EOAV=0 */ + |(0 << 2) /* EOI=0 */ + |(0 << 1) /* END=0 */ + |(0 << 0); /* DATERR=0 */ + } + zr36060_write_8(zr, 0x007, reg); + + reg = size; + zr36060_write_32(zr, 0x009, reg); + + reg = (size * 10) / 11; + zr36060_write_32(zr, 0x00d, reg); // Not needed for compr. with variable scale factor, just in case ... + + /* how do we set initial SF as a function of quality parameter? */ + reg = 0x0100; /* SF=1.0 */ + zr36060_write_16(zr, 0x011, reg); + + reg = 0x00ffffff; /* AF=max */ + zr36060_write_24(zr, 0x013, reg); + + reg = 0x0000; /* test */ + zr36060_write_16(zr, 0x024, reg); + + //post_office_read(zr,1,0); +} + +static void zr36060_set_video(struct zoran *zr, enum zoran_codec_mode mode) +{ + struct tvnorm *tvn; + u32 reg; + unsigned HStart; + + tvn = zr->timing; + + reg = (0 << 7) /* Video8 */ + |(0 << 6) /* Range */ + |(0 << 3) /* FlDet */ + |(1 << 2) /* FlVedge */ + |(0 << 1) /* FlExt */ + |(0 << 0); /* SyncMstr */ + + if (mode != BUZ_MODE_STILL_DECOMPRESS) { + /* limit pixels to range 16..235 as per CCIR-601 */ + reg |= (1 << 6); /* Range=1 */ + } + zr36060_write_8(zr, 0x030, reg); + + switch (zr->card) { + case DC10: + case DC10plus: + reg = (0 << 7) /* VCLKPol */ + |(0 << 6) /* PValPol */ + |(0 << 5) /* PoePol */ + |(0 << 4) /* SImgPol */ + |(1 << 3) /* BLPol */ + |(1 << 2) /* FlPol */ + |(1 << 1) /* HSPol */ + |(1 << 0); /* VSPol */ + break; + case LML33: + reg = (0 << 7) /* VCLKPol=0 */ + |(0 << 6) /* PValPol=0 */ + |(1 << 5) /* PoePol=1 */ + |(0 << 4) /* SImgPol=0 */ + |(0 << 3) /* BLPol=0 */ + |(0 << 2) /* FlPol=0 */ + |(0 << 1) /* HSPol=0, sync on falling edge */ + |(1 << 0); /* VSPol=1 */ + break; + case BUZ: + default: + reg = (0 << 7) /* VCLKPol=0 */ + |(0 << 6) /* PValPol=0 */ + |(1 << 5) /* PoePol=1 */ + |(0 << 4) /* SImgPol=0 */ + |(0 << 3) /* BLPol=0 */ + |(0 << 2) /* FlPol=0 */ + |(1 << 1) /* HSPol=0, sync on falling edge */ + |(1 << 0); /* VSPol=1 */ + break; + } + zr36060_write_8(zr, 0x031, reg); + + switch (zr->params.HorDcm) { + default: + case 1: + reg = (0 << 0); + break; /* HScale = 0 */ + + case 2: + reg = (1 << 0); + break; /* HScale = 1 */ + + case 4: + reg = (2 << 0); + break; /* HScale = 2 */ + } + if (zr->params.VerDcm == 2) + reg |= (1 << 2); + zr36060_write_8(zr, 0x032, reg); + + reg = 0x00; /* BackY */ + zr36060_write_8(zr, 0x033, reg); + + reg = 0x80; /* BackU */ + zr36060_write_8(zr, 0x034, reg); + + reg = 0x80; /* BackV */ + zr36060_write_8(zr, 0x035, reg); + + /* sync generator */ + + reg = tvn->Ht - 1; /* Vtotal */ + zr36060_write_16(zr, 0x036, reg); + + reg = tvn->Wt - 1; /* Htotal */ + zr36060_write_16(zr, 0x038, reg); + + reg = 6 - 1; /* VsyncSize */ + zr36060_write_8(zr, 0x03a, reg); + + //reg = 30 - 1; /* HsyncSize */ + reg = (zr->params.norm == 1 ? 57 : 68); + zr36060_write_8(zr, 0x03b, reg); + + reg = tvn->VStart - 1; /* BVstart */ + zr36060_write_8(zr, 0x03c, reg); + + reg += tvn->Ha / 2; /* BVend */ + zr36060_write_16(zr, 0x03e, reg); + + reg = tvn->HStart + 64 - 1; /* BHstart */ + zr36060_write_8(zr, 0x03d, reg); + + reg += tvn->Wa; /* BHend */ + zr36060_write_16(zr, 0x040, reg); + + /* active area */ + reg = zr->params.img_y + tvn->VStart; /* Vstart */ + zr36060_write_16(zr, 0x042, reg); + + reg += zr->params.img_height; /* Vend */ + zr36060_write_16(zr, 0x044, reg); + + HStart = tvn->HStart; + if (zr->card == BUZ) { + HStart += 44; + } else { + HStart += 64; + } + reg = zr->params.img_x + HStart; /* Hstart */ + zr36060_write_16(zr, 0x046, reg); + + reg += zr->params.img_width; /* Hend */ + zr36060_write_16(zr, 0x048, reg); + + /* subimage area */ + reg = tvn->VStart - 4; /* SVstart */ + zr36060_write_16(zr, 0x04a, reg); + + reg += tvn->Ha / 2 + 8; /* SVend */ + zr36060_write_16(zr, 0x04c, reg); + + reg = tvn->HStart + 64 - 4; /* SHstart */ + zr36060_write_16(zr, 0x04e, reg); + + reg += tvn->Wa + 8; /* SHend */ + zr36060_write_16(zr, 0x050, reg); +} + +static void zr36060_set_jpg_SOF(struct zoran *zr) +{ + u32 reg; + + + reg = 0xffc0; /* SOF marker */ + zr36060_write_16(zr, 0x060, reg); + + reg = 17; /* SOF length */ + zr36060_write_16(zr, 0x062, reg); + + reg = 8; /* precision 8 bits */ + zr36060_write_8(zr, 0x064, reg); + + reg = zr->params.img_height / zr->params.VerDcm; /* image height */ + zr36060_write_16(zr, 0x065, reg); + + reg = zr->params.img_width / zr->params.HorDcm; /* image width */ + zr36060_write_16(zr, 0x067, reg); + + reg = 3; /* 3 color components */ + zr36060_write_8(zr, 0x069, reg); + + reg = 0x002100; /* Y component */ + zr36060_write_24(zr, 0x06a, reg); + + reg = 0x011101; /* U component */ + zr36060_write_24(zr, 0x06d, reg); + + reg = 0x021101; /* V component */ + zr36060_write_24(zr, 0x070, reg); +} + +static void zr36060_set_jpg_SOS(struct zoran *zr) +{ + u32 reg; + + + reg = 0xffda; /* SOS marker */ + zr36060_write_16(zr, 0x07a, reg); + + reg = 12; /* SOS length */ + zr36060_write_16(zr, 0x07c, reg); + + reg = 3; /* 3 color components */ + zr36060_write_8(zr, 0x07e, reg); + + reg = 0x0000; /* Y component */ + zr36060_write_16(zr, 0x07f, reg); + + reg = 0x0111; /* U component */ + zr36060_write_16(zr, 0x081, reg); + + reg = 0x0211; /* V component */ + zr36060_write_16(zr, 0x083, reg); + + reg = 0x003f00; /* Start, end spectral scans */ + zr36060_write_24(zr, 0x085, reg); +} + +static void zr36060_set_jpg_DRI(struct zoran *zr) +{ + u32 reg; + + + reg = 0xffdd; /* DRI marker */ + zr36060_write_16(zr, 0x0c0, reg); + + reg = 4; /* DRI length */ + zr36060_write_16(zr, 0x0c2, reg); + + reg = 8; /* length in MCUs */ + zr36060_write_16(zr, 0x0c4, reg); +} + +static void zr36060_set_jpg_DQT(struct zoran *zr) +{ + unsigned i; + unsigned adr; + static const u8 dqt[] = { + 0xff, 0xdb, /* DHT marker */ + 0x00, 0x84, /* DHT length */ + 0x00, /* table ID 0 */ + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + 0x01, /* table ID 1 */ + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 + }; + + /* write fixed quantitization tables */ + adr = 0x0cc; + for (i = 0; i < sizeof(dqt); ++i) { + zr36060_write_8(zr, adr++, dqt[i]); + } +} + +static void zr36060_set_jpg_DHT(struct zoran *zr) +{ + unsigned i; + unsigned adr; + static const u8 dht[] = { + 0xff, 0xc4, /* DHT marker */ + 0x01, 0xa2, /* DHT length */ + 0x00, /* table class 0, ID 0 */ + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, /* # codes of length 1..8 */ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* # codes of length 8..16 */ + 0x00, /* values for codes of length 2 */ + 0x01, 0x02, 0x03, 0x04, 0x05, /* values for codes of length 3 */ + 0x06, /* values for codes of length 4 */ + 0x07, /* values for codes of length 5 */ + 0x08, /* values for codes of length 6 */ + 0x09, /* values for codes of length 7 */ + 0x0a, /* values for codes of length 8 */ + 0x0b, /* values for codes of length 9 */ + 0x01, /* table class 0, ID 1 */ + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* # codes of length 1..8 */ + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, /* # codes of length 9..16 */ + 0x00, 0x01, 0x02, /* values for codes of length 2 */ + 0x03, /* values for codes of length 3 */ + 0x04, /* values for codes of length 4 */ + 0x05, /* values for codes of length 5 */ + 0x06, /* values for codes of length 6 */ + 0x07, /* values for codes of length 7 */ + 0x08, /* values for codes of length 8 */ + 0x09, /* values for codes of length 9 */ + 0x0a, /* values for codes of length 10 */ + 0x0b, /* values for codes of length 11 */ + 0x10, + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, + 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, + 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, + 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, + 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, + 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, + 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, + 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, + 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, + 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, + 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa + }; + + /* write fixed Huffman tables */ + adr = 0x1d4; + for (i = 0; i < sizeof(dht); ++i) { + zr36060_write_8(zr, adr++, dht[i]); + } +} + +static void zr36060_set_jpg_APP(struct zoran *zr) +{ + unsigned adr; + int len, i; + u32 reg; + + + len = zr->params.APP_len; + if (len < 0) + len = 0; + if (len > 60) + len = 60; + + i = zr->params.APPn; + if (i < 0) + i = 0; + if (i > 15) + i = 15; + + reg = 0xffe0 + i; /* APPn marker */ + zr36060_write_16(zr, 0x380, reg); + + reg = len + 2; /* APPn len */ + zr36060_write_16(zr, 0x382, reg); + + /* write APPn data */ + adr = 0x384; + for (i = 0; i < 60; i++) { + zr36060_write_8(zr, adr++, + (i < len ? zr->params.APP_data[i] : 0)); + } +} + +static void zr36060_set_jpg_COM(struct zoran *zr) +{ + unsigned adr; + int len, i; + u32 reg; + + + len = zr->params.COM_len; + if (len < 0) + len = 0; + if (len > 60) + len = 60; + + reg = 0xfffe; /* COM marker */ + zr36060_write_16(zr, 0x3c0, reg); + + reg = len + 2; /* COM len */ + zr36060_write_16(zr, 0x3c2, reg); + + /* write COM data */ + adr = 0x3c4; + for (i = 0; i < 60; i++) { + zr36060_write_8(zr, adr++, + (i < len ? zr->params.COM_data[i] : 0)); + } +} + +static void zr36060_set_cap(struct zoran *zr, enum zoran_codec_mode mode) +{ + unsigned i; + u32 reg; + + zr36060_reset(zr); + mdelay(10); + + reg = (0 << 7) /* Load=0 */ + |(1 << 0); /* SynRst=1 */ + zr36060_write_8(zr, 0x000, reg); + + zr36060_set_jpg(zr, mode); + zr36060_set_video(zr, mode); + zr36060_set_jpg_SOF(zr); + zr36060_set_jpg_SOS(zr); + zr36060_set_jpg_DRI(zr); + zr36060_set_jpg_DQT(zr); + zr36060_set_jpg_DHT(zr); + zr36060_set_jpg_APP(zr); + zr36060_set_jpg_COM(zr); + + reg = (1 << 7) /* Load=1 */ + |(1 << 0); /* SynRst=0 */ + zr36060_write_8(zr, 0x000, reg); + + /* wait for codec to unbusy */ + for (i = 0; i < 100000; ++i) { + reg = zr36060_read_8(zr, 0x001); + if ((reg & (1 << 7)) == 0) { + return; + } + //udelay(100); + } + printk(KERN_ERR "%sZR36060: stuck busy, statux=%02x\n", zr->name, + reg); +} + +static void init_jpeg_queue(struct zoran *zr) +{ + int i; + /* re-initialize DMA ring stuff */ + zr->jpg_que_head = 0; + zr->jpg_dma_head = 0; + zr->jpg_dma_tail = 0; + zr->jpg_que_tail = 0; + zr->jpg_seq_num = 0; + zr->JPEG_error = 0; + zr->num_errors = 0; + zr->jpg_err_seq = 0; + zr->jpg_err_shift = 0; + zr->jpg_queued_num = 0; + for (i = 0; i < zr->jpg_nbufs; i++) { + zr->jpg_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */ + } + for (i = 0; i < BUZ_NUM_STAT_COM; i++) { + zr->stat_com[i] = 1; /* mark as unavailable to zr36057 */ + } +} + +static void zr36057_set_jpg(struct zoran *zr, enum zoran_codec_mode mode) +{ + struct tvnorm *tvn; + u32 reg; + + tvn = zr->timing; + + /* assert P_Reset */ + btwrite(0, ZR36057_JPC); + + /* MJPEG compression mode */ + switch (mode) { + + case BUZ_MODE_MOTION_COMPRESS: + default: + reg = ZR36057_JMC_MJPGCmpMode; + break; + + case BUZ_MODE_MOTION_DECOMPRESS: + reg = ZR36057_JMC_MJPGExpMode; + reg |= ZR36057_JMC_SyncMstr; + /* RJ: The following is experimental - improves the output to screen */ + //if(zr->params.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM + break; + + case BUZ_MODE_STILL_COMPRESS: + reg = ZR36057_JMC_JPGCmpMode; + break; + + case BUZ_MODE_STILL_DECOMPRESS: + reg = ZR36057_JMC_JPGExpMode; + break; + + } + reg |= ZR36057_JMC_JPG; + if (zr->params.field_per_buff == 1) + reg |= ZR36057_JMC_Fld_per_buff; + btwrite(reg, ZR36057_JMC); + + /* vertical */ + btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR); + reg = + (6 << ZR36057_VSP_VsyncSize) | (tvn->Ht << ZR36057_VSP_FrmTot); + btwrite(reg, ZR36057_VSP); + reg = ((zr->params.img_y + tvn->VStart) << ZR36057_FVAP_NAY) + | (zr->params.img_height << ZR36057_FVAP_PAY); + btwrite(reg, ZR36057_FVAP); + + /* horizontal */ + btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR); + reg = + ((tvn->HSyncStart) << ZR36057_HSP_HsyncStart) | (tvn-> + Wt << + ZR36057_HSP_LineTot); + btwrite(reg, ZR36057_HSP); + reg = ((zr->params.img_x + tvn->HStart + 4) << ZR36057_FHAP_NAX) + | (zr->params.img_width << ZR36057_FHAP_PAX); + btwrite(reg, ZR36057_FHAP); + + /* field process parameters */ + if (zr->params.odd_even) + reg = ZR36057_FPP_Odd_Even; + else + reg = 0; + if (mode == BUZ_MODE_MOTION_DECOMPRESS && zr->card != LML33 + && zr->card != BUZ) + reg ^= ZR36057_FPP_Odd_Even; + + btwrite(reg, ZR36057_FPP); + + /* Set proper VCLK Polarity, else colors will be wrong during playback */ + //btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR); + + /* code base address */ + reg = virt_to_bus(zr->stat_com); + btwrite(reg, ZR36057_JCBA); + + /* FIFO threshold (FIFO is 160. double words) */ + /* NOTE: decimal values here */ + switch (mode) { + + case BUZ_MODE_STILL_COMPRESS: + case BUZ_MODE_MOTION_COMPRESS: + reg = 140; + break; + + case BUZ_MODE_STILL_DECOMPRESS: + case BUZ_MODE_MOTION_DECOMPRESS: + reg = 20; + break; + + default: + reg = 80; + break; + + } + btwrite(reg, ZR36057_JCFT); + zr36057_adjust_vfe(zr, mode); + +} + +#if (DEBUGLEVEL > 2) +static void dump_guests(struct zoran *zr) +{ + int i, guest[8]; + + for (i = 1; i < 8; i++) { // Don't read zr36060 here + guest[i] = post_office_read(zr, i, 0); + } + + printk(KERN_INFO "%s: Guests:", zr->name); + + for (i = 1; i < 8; i++) { + printk(" 0x%02x", guest[i]); + } + printk("\n"); +} + +static unsigned long get_time(void) +{ + struct timeval tv; + do_gettimeofday(&tv); + return (1000000 * tv.tv_sec + tv.tv_usec); +} + +static void detect_guest_activity(struct zoran *zr) +{ + int timeout, i, j, res, guest[8], guest0[8], change[8][3]; + unsigned long t0, t1; + + dump_guests(zr); + printk(KERN_INFO "%s: Detecting guests activity, please wait...\n", + zr->name); + for (i = 1; i < 8; i++) { // Don't read zr36060 here + guest0[i] = guest[i] = post_office_read(zr, i, 0); + } + + timeout = 0; + j = 0; + t0 = get_time(); + while (timeout < 10000) { + udelay(10); + timeout++; + for (i = 1; (i < 8) && (j < 8); i++) { + res = post_office_read(zr, i, 0); + if (res != guest[i]) { + t1 = get_time(); + change[j][0] = (t1 - t0); + t0 = t1; + change[j][1] = i; + change[j][2] = res; + j++; + guest[i] = res; + } + } + if (j >= 8) + break; + } + printk(KERN_INFO "%s: Guests:", zr->name); + + for (i = 1; i < 8; i++) { + printk(" 0x%02x", guest0[i]); + } + printk("\n"); + if (j == 0) { + printk(KERN_INFO "%s: No activity detected.\n", zr->name); + return; + } + for (i = 0; i < j; i++) { + printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", zr->name, + change[i][0], change[i][1], change[i][2]); + } +} +#endif + +static void print_interrupts(struct zoran *zr) +{ + int res, noerr; + noerr = 0; + printk(KERN_INFO "%s: interrupts received:", zr->name); + if ((res = zr->field_counter) < -1 || res > 1) { + printk(" FD:%d", res); + } + if ((res = zr->intr_counter_GIRQ1) != 0) { + printk(" GIRQ1:%d", res); + noerr++; + } + if ((res = zr->intr_counter_GIRQ0) != 0) { + printk(" GIRQ0:%d", res); + noerr++; + } + if ((res = zr->intr_counter_CodRepIRQ) != 0) { + printk(" CodRepIRQ:%d", res); + noerr++; + } + if ((res = zr->intr_counter_JPEGRepIRQ) != 0) { + printk(" JPEGRepIRQ:%d", res); + noerr++; + } + if (zr->JPEG_max_missed) { + printk(" JPEG delays: max=%d min=%d", zr->JPEG_max_missed, + zr->JPEG_min_missed); + } + if (zr->END_event_missed) { + printk(" ENDs missed: %d", zr->END_event_missed); + } + //if (zr->jpg_queued_num) { + printk(" queue_state=%ld/%ld/%ld/%ld", zr->jpg_que_tail, + zr->jpg_dma_tail, zr->jpg_dma_head, zr->jpg_que_head); + //} + if (!noerr) { + printk(": no interrupts detected."); + } + printk("\n"); +} + +static void clear_interrupt_counters(struct zoran *zr) +{ + zr->intr_counter_GIRQ1 = 0; + zr->intr_counter_GIRQ0 = 0; + zr->intr_counter_CodRepIRQ = 0; + zr->intr_counter_JPEGRepIRQ = 0; + zr->field_counter = 0; + zr->IRQ1_in = 0; + zr->IRQ1_out = 0; + zr->JPEG_in = 0; + zr->JPEG_out = 0; + zr->JPEG_0 = 0; + zr->JPEG_1 = 0; + zr->END_event_missed = 0; + zr->JPEG_missed = 0; + zr->JPEG_max_missed = 0; + zr->JPEG_min_missed = 0x7fffffff; +} + +static u32 count_reset_interrupt(struct zoran *zr) +{ + u32 isr; + if ((isr = btread(ZR36057_ISR) & 0x78000000)) { + if (isr & ZR36057_ISR_GIRQ1) { + btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR); + zr->intr_counter_GIRQ1++; + } + if (isr & ZR36057_ISR_GIRQ0) { + btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR); + zr->intr_counter_GIRQ0++; + } + if (isr & ZR36057_ISR_CodRepIRQ) { + btwrite(ZR36057_ISR_CodRepIRQ, ZR36057_ISR); + zr->intr_counter_CodRepIRQ++; + } + if (isr & ZR36057_ISR_JPEGRepIRQ) { + btwrite(ZR36057_ISR_JPEGRepIRQ, ZR36057_ISR); + zr->intr_counter_JPEGRepIRQ++; + } + } + return isr; +} + +static void jpeg_start(struct zoran *zr) +{ + int reg; + zr->frame_num = 0; + + btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC); // /P_Reset + btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR); // \CFlush + btor(ZR36057_JPC_CodTrnsEn, ZR36057_JPC); // /CodTrnsEn + btwrite(IRQ_MASK, ZR36057_ISR); // Clear IRQs + btwrite(IRQ_MASK | ZR36057_ICR_IntPinEn, ZR36057_ICR); // Enable IRQs + + set_frame(zr, 0); // \FRAME + + /* JPEG codec guest ID */ + reg = + (1 << ZR36057_JCGI_JPEGuestID) | (0 << + ZR36057_JCGI_JPEGuestReg); + btwrite(reg, ZR36057_JCGI); + + btor(ZR36057_JPC_Active, ZR36057_JPC); // /Active + btor(ZR36057_JMC_Go_en, ZR36057_JMC); // /Go_en + udelay(30); + set_frame(zr, 1); // /FRAME +} + +static void zr36057_enable_jpg(struct zoran *zr, + enum zoran_codec_mode mode) +{ + static int zero = 0; + static int one = 1; + + zr->codec_mode = mode; + switch (mode) { + + case BUZ_MODE_MOTION_COMPRESS: + set_videobus_enable(zr, 0); + set_videobus_dir(zr, 0); // GPIO(zr, 1, 0); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, + DECODER_ENABLE_OUTPUT, &one); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, + ENCODER_SET_INPUT, &zero); + set_videobus_enable(zr, 1); + zr36060_sleep(zr, 0); + zr36060_set_cap(zr, mode); // Load ZR36060 + init_jpeg_queue(zr); + zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO + + clear_interrupt_counters(zr); + DEBUG1(printk + (KERN_INFO "%s: enable_jpg MOTION_COMPRESS\n", + zr->name)); + break; + + case BUZ_MODE_MOTION_DECOMPRESS: + set_videobus_enable(zr, 0); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, + DECODER_ENABLE_OUTPUT, &zero); + set_videobus_dir(zr, 1); // GPIO(zr, 1, 1); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, + ENCODER_SET_INPUT, &one); + set_videobus_enable(zr, 1); + zr36060_sleep(zr, 0); + zr36060_set_cap(zr, mode); // Load ZR36060 + init_jpeg_queue(zr); + zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO + + clear_interrupt_counters(zr); + DEBUG1(printk + (KERN_INFO "%s: enable_jpg MOTION_DECOMPRESS\n", + zr->name)); + break; + + case BUZ_MODE_IDLE: + default: + /* shut down processing */ + btand(~(cardjpegint[zr->card] | ZR36057_ICR_JPEGRepIRQ), + ZR36057_ICR); + btwrite(cardjpegint[zr->card] | ZR36057_ICR_JPEGRepIRQ, + ZR36057_ISR); + btand(~ZR36057_JMC_Go_en, ZR36057_JMC); // \Go_en + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + + set_videobus_dir(zr, 0); // GPIO(zr, 1, 0); + set_frame(zr, 1); //GPIO(zr, 6, 1); // /FRAME + btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); // /CFlush + btwrite(0, ZR36057_JPC); // \P_Reset,\CodTrnsEn,\Active + btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC); + btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC); + zr36060_reset(zr); + zr36060_sleep(zr, 1); + zr36057_adjust_vfe(zr, mode); + set_videobus_enable(zr, 0); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, + DECODER_ENABLE_OUTPUT, &one); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, + ENCODER_SET_INPUT, &zero); + set_videobus_enable(zr, 1); + DEBUG1(printk + (KERN_INFO "%s: enable_jpg IDLE\n", zr->name)); + break; + + } +} + +/* + * Queue a MJPEG buffer for capture/playback + */ + +static int jpg_qbuf(struct zoran *zr, int frame, + enum zoran_codec_mode mode) +{ + unsigned long flags; + int res; + + /* Check if buffers are allocated */ + + if (!zr->jpg_buffers_allocated) { + printk(KERN_ERR + "%s: jpg_qbuf: buffers not yet allocated\n", + zr->name); + return -ENOMEM; + } + + /* Does the user want to stop streaming? */ + + if (frame < 0) { + if (zr->codec_mode == mode) { + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + return 0; + } else { + printk(KERN_ERR + "%s: jpg_qbuf - stop streaming but not in streaming mode\n", + zr->name); + return -EINVAL; + } + } + + /* No grabbing outside the buffer range! */ + + if (frame >= zr->jpg_nbufs) { + printk(KERN_ERR "%s: jpg_qbuf: buffer %d out of range\n", + zr->name, frame); + return -EINVAL; + } + + /* what is the codec mode right now? */ + + if (zr->codec_mode == BUZ_MODE_IDLE) { + /* Ok load up the zr36060 */ + zr36057_enable_jpg(zr, mode); + } else if (zr->codec_mode != mode) { + /* wrong codec mode active - invalid */ + printk(KERN_ERR "%s: jpg_qbuf - codec in wrong mode\n", + zr->name); + return -EINVAL; + } + + spin_lock_irqsave(&zr->lock, flags); + + /* make sure a grab isn't going on currently with this buffer */ + + switch (zr->jpg_gbuf[frame].state) { + + case BUZ_STATE_DONE: + DEBUG1(printk + (KERN_WARNING + "%s: Warning: queing frame in BUZ_STATE_DONE state\n", + zr->name)); + case BUZ_STATE_USER: + /* since there is at least one unused buffer there's room for at least one more pend[] entry */ + zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = frame; + zr->jpg_gbuf[frame].state = BUZ_STATE_PEND; + zoran_feed_stat_com(zr); + res = 0; + break; + + default: + case BUZ_STATE_DMA: + case BUZ_STATE_PEND: + res = -EBUSY; /* what are you doing? */ + break; + + } + + spin_unlock_irqrestore(&zr->lock, flags); + + /* Start the zr36060 when the first frame is queued */ + if (zr->jpg_que_head == 1) + jpeg_start(zr); + + return res; +} + +/* + * Sync on a MJPEG buffer + */ + +static int jpg_sync(struct zoran *zr, struct zoran_sync *bs) +{ + unsigned long flags; + int frame, timeout; + + if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS + && zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { + DEBUG1(printk(KERN_ERR + "%s: BUZIOCSYNC: - codec not in streaming mode\n", + zr->name)); + return -EINVAL; + } + while (zr->jpg_que_tail == zr->jpg_dma_tail) { + if (zr->jpg_dma_tail == zr->jpg_dma_head) + break; + timeout = + interruptible_sleep_on_timeout(&zr->jpg_capq, 10 * HZ); + if (!timeout) { + btand(~ZR36057_JMC_Go_en, ZR36057_JMC); + udelay(1); + printk(KERN_ERR + "%s: timeout: codec isr=0x%02x, csr=0x%02x\n", + zr->name, zr36060_read_8(zr, 0x008), + zr36060_read_8(zr, 0x001)); + return -ETIME; + } else if (signal_pending(current)) + return -ERESTARTSYS; + } + + spin_lock_irqsave(&zr->lock, flags); + + if (zr->jpg_dma_tail != zr->jpg_dma_head) + frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME]; + else + frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; + /* buffer should now be in BUZ_STATE_DONE */ + +#if(DEBUGLEVEL > 0) + if (zr->jpg_gbuf[frame].state != BUZ_STATE_DONE) + printk(KERN_ERR "%s: jpg_sync - internal error\n", + zr->name); +#endif + + *bs = zr->jpg_gbuf[frame].bs; + zr->jpg_gbuf[frame].state = BUZ_STATE_USER; + + spin_unlock_irqrestore(&zr->lock, flags); + + return 0; +} + +/* when this is called the spinlock must be held */ +static void zoran_feed_stat_com(struct zoran *zr) +{ + /* move frames from pending queue to DMA */ + + int frame, i, max_stat_com; + + max_stat_com = + (zr->params.TmpDcm == + 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1); + + while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com + && zr->jpg_dma_head < zr->jpg_que_head) { + + frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME]; + if (zr->params.TmpDcm == 1) { + /* fill 1 stat_com entry */ + i = (zr->jpg_dma_head - + zr->jpg_err_shift) & BUZ_MASK_STAT_COM; + if (!(zr->stat_com[i] & 1)) + break; + zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus; + } else { + /* fill 2 stat_com entries */ + i = ((zr->jpg_dma_head - + zr->jpg_err_shift) & 1) * 2; + if (!(zr->stat_com[i] & 1)) + break; + zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus; + zr->stat_com[i + 1] = + zr->jpg_gbuf[frame].frag_tab_bus; + } + zr->jpg_gbuf[frame].state = BUZ_STATE_DMA; + zr->jpg_dma_head++; + + } + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) + zr->jpg_queued_num++; +} + +/* when this is called the spinlock must be held */ +static void zoran_reap_stat_com(struct zoran *zr) +{ + /* move frames from DMA queue to done queue */ + + int i; + u32 stat_com; + unsigned int seq; + unsigned int dif; + int frame; + struct zoran_gbuffer *gbuf; + + /* In motion decompress we don't have a hardware frame counter, + we just count the interrupts here */ + + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) { + zr->jpg_seq_num++; + } + while (zr->jpg_dma_tail < zr->jpg_dma_head) { + if (zr->params.TmpDcm == 1) + i = (zr->jpg_dma_tail - + zr->jpg_err_shift) & BUZ_MASK_STAT_COM; + else + i = ((zr->jpg_dma_tail - + zr->jpg_err_shift) & 1) * 2 + 1; + + stat_com = zr->stat_com[i]; + + if ((stat_com & 1) == 0) { + return; + } + frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; + gbuf = &zr->jpg_gbuf[frame]; + get_fast_time(&gbuf->bs.timestamp); + + if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { + gbuf->bs.length = (stat_com & 0x7fffff) >> 1; + + /* update sequence number with the help of the counter in stat_com */ + + seq = ((stat_com >> 24) + zr->jpg_err_seq) & 0xff; + dif = (seq - zr->jpg_seq_num) & 0xff; + zr->jpg_seq_num += dif; + } else { + gbuf->bs.length = 0; + } + gbuf->bs.seq = + zr->params.TmpDcm == + 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num; + gbuf->state = BUZ_STATE_DONE; + + zr->jpg_dma_tail++; + } +} + +static void error_handler(struct zoran *zr, u32 astat, u32 stat) +{ + /* This is JPEG error handling part */ + if ((zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) + && (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS)) { + //printk(KERN_ERR "%s: Internal error: error handling request in mode %d\n", zr->name, zr->codec_mode); + return; + } + if ((stat & 1) == 0 + && zr->codec_mode == BUZ_MODE_MOTION_COMPRESS + && zr->jpg_dma_tail - zr->jpg_que_tail >= zr->jpg_nbufs) { + /* No free buffers... */ + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + wake_up_interruptible(&zr->jpg_capq); + zr->JPEG_missed = 0; + return; + } + if (zr->JPEG_error != 1) { + /* + * First entry: error just happened during normal operation + * + * In BUZ_MODE_MOTION_COMPRESS: + * + * Possible glitch in TV signal. In this case we should + * stop the codec and wait for good quality signal before + * restarting it to avoid further problems + * + * In BUZ_MODE_MOTION_DECOMPRESS: + * + * Bad JPEG frame: we have to mark it as processed (codec crashed + * and was not able to do it itself), and to remove it from queue. + */ + btand(~ZR36057_JMC_Go_en, ZR36057_JMC); + udelay(1); + stat = + stat | (post_office_read(zr, 7, 0) & 3) << 8 | + zr36060_read_8(zr, 0x008); + btwrite(0, ZR36057_JPC); + btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + zr36060_reset(zr); + zr36060_sleep(zr, 1); + zr->JPEG_error = 1; + zr->num_errors++; + /* Report error */ +#if(DEBUGLEVEL > 1) + if (zr->num_errors <= 8) { + long frame; + frame = + zr->jpg_pend[zr-> + jpg_dma_tail & BUZ_MASK_FRAME]; + printk(KERN_ERR + "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ", + zr->name, stat, zr->last_isr, + zr->jpg_que_tail, zr->jpg_dma_tail, + zr->jpg_dma_head, zr->jpg_que_head, + zr->jpg_seq_num, frame); + printk("stat_com frames:"); + { + int i, j; + for (j = 0; j < BUZ_NUM_STAT_COM; j++) { + for (i = 0; i < zr->jpg_nbufs; i++) { + if (zr->stat_com[j] == + zr->jpg_gbuf[i]. + frag_tab_bus) { + printk("% d->%d", + j, i); + } + } + } + printk("\n"); + } + } +#endif + /* Find an entry in stat_com and rotate contents */ + { + int i; + + if (zr->params.TmpDcm == 1) + i = (zr->jpg_dma_tail - + zr-> + jpg_err_shift) & BUZ_MASK_STAT_COM; + else + i = ((zr->jpg_dma_tail - + zr->jpg_err_shift) & 1) * 2; + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) { + /* Mimic zr36067 operation */ + zr->stat_com[i] |= 1; + if (zr->params.TmpDcm != 1) + zr->stat_com[i + 1] |= 1; + /* Refill */ + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + wake_up_interruptible(&zr->jpg_capq); + /* Find an entry in stat_com again after refill */ + if (zr->params.TmpDcm == 1) + i = (zr->jpg_dma_tail - + zr-> + jpg_err_shift) & + BUZ_MASK_STAT_COM; + else + i = ((zr->jpg_dma_tail - + zr->jpg_err_shift) & 1) * 2; + } + if (i) { + /* Rotate stat_comm entries to make current entry first */ + int j; + u32 bus_addr[BUZ_NUM_STAT_COM]; + + memcpy(bus_addr, zr->stat_com, + sizeof(bus_addr)); + for (j = 0; j < BUZ_NUM_STAT_COM; j++) { + zr->stat_com[j] = + bus_addr[(i + + j) & + BUZ_MASK_STAT_COM]; + } + zr->jpg_err_shift += i; + zr->jpg_err_shift &= BUZ_MASK_STAT_COM; + } + if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) + zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */ + } + } + /* Now the stat_comm buffer is ready for restart */ + { + int status; + + status = 0; + if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEODECODER, + DECODER_GET_STATUS, &status); + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS + || (status & DECODER_STATUS_GOOD)) { + /********** RESTART code *************/ + zr36060_reset(zr); + zr36060_set_cap(zr, zr->codec_mode); + zr36057_set_jpg(zr, zr->codec_mode); + jpeg_start(zr); +#if(DEBUGLEVEL > 1) + if (zr->num_errors <= 8) + printk(KERN_INFO "%s: Restart\n", + zr->name); +#endif + zr->JPEG_missed = 0; + zr->JPEG_error = 2; + /********** End RESTART code ***********/ + } + } +} + +static void zoran_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 stat, astat; + int count; + struct zoran *zr; + unsigned long flags; + + zr = (struct zoran *) dev_id; + count = 0; + + if (zr->testing) { + /* Testing interrupts */ + spin_lock_irqsave(&zr->lock, flags); + while ((stat = count_reset_interrupt(zr))) { + if (count++ > 100) { + btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); + printk(KERN_ERR + "%s: IRQ lockup while testing, isr=0x%08x, cleared int mask\n", + zr->name, stat); + wake_up_interruptible(&zr->test_q); + } + } + zr->last_isr = stat; + spin_unlock_irqrestore(&zr->lock, flags); + return; + } + + spin_lock_irqsave(&zr->lock, flags); + while (1) { + /* get/clear interrupt status bits */ + stat = count_reset_interrupt(zr); + astat = stat & IRQ_MASK; + if (!astat) { + break; + } + if (astat & cardvsync[zr->card]) { // SW + + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS + || zr->codec_mode == + BUZ_MODE_MOTION_COMPRESS) { + /* count missed interrupts */ + zr->JPEG_missed++; + } + //post_office_read(zr,1,0); + /* Interrupts may still happen when zr->v4l_memgrab_active is switched off. + We simply ignore them */ + + if (zr->v4l_memgrab_active) { + + /* A lot more checks should be here ... */ + if ((btread(ZR36057_VSSFGR) & + ZR36057_VSSFGR_SnapShot) == 0) + printk(KERN_WARNING + "%s: BuzIRQ with SnapShot off ???\n", + zr->name); + + if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) { + /* There is a grab on a frame going on, check if it has finished */ + + if ((btread(ZR36057_VSSFGR) & + ZR36057_VSSFGR_FrameGrab) == + 0) { + /* it is finished, notify the user */ + + zr->v4l_gbuf[zr-> + v4l_grab_frame]. + state = BUZ_STATE_DONE; + zr->v4l_grab_frame = + NO_GRAB_ACTIVE; + zr->v4l_grab_seq++; + zr->v4l_pend_tail++; + } + } + + if (zr->v4l_grab_frame == NO_GRAB_ACTIVE) + wake_up_interruptible(&zr-> + v4l_capq); + + /* Check if there is another grab queued */ + + if (zr->v4l_grab_frame == NO_GRAB_ACTIVE + && zr->v4l_pend_tail != + zr->v4l_pend_head) { + + int frame = + zr->v4l_pend[zr-> + v4l_pend_tail & + V4L_MASK_FRAME]; + u32 reg; + + zr->v4l_grab_frame = frame; + + /* Set zr36057 video front end and enable video */ + + /* Buffer address */ + + reg = + zr->v4l_gbuf[frame]. + fbuffer_bus; + btwrite(reg, ZR36057_VDTR); + if (zr->video_interlace) + reg += zr->gbpl; + btwrite(reg, ZR36057_VDBR); + + /* video stride, status, and frame grab register */ + + reg = 0; + if (zr->video_interlace) + reg += zr->gbpl; + reg = + (reg << + ZR36057_VSSFGR_DispStride); + reg |= ZR36057_VSSFGR_VidOvf; + reg |= ZR36057_VSSFGR_SnapShot; + reg |= ZR36057_VSSFGR_FrameGrab; + btwrite(reg, ZR36057_VSSFGR); + + btor(ZR36057_VDCR_VidEn, + ZR36057_VDCR); + } + } + } +#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ) + if (astat & ZR36057_ISR_CodRepIRQ) { + zr->intr_counter_CodRepIRQ++; + IDEBUG(printk + (KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n", + zr->name)); + btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR); + } +#endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */ + +#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) + if (astat & ZR36057_ISR_JPEGRepIRQ) { + + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS + || zr->codec_mode == + BUZ_MODE_MOTION_COMPRESS) { +#if(DEBUGLEVEL > 1) + if (!zr->frame_num || zr->JPEG_error) { + printk(KERN_INFO + "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n", + zr->name, stat, + zr->params.odd_even, + zr->params.field_per_buff, + zr->JPEG_missed); + { + char sc[] = "0000"; + char sv[5]; + int i; + strcpy(sv, sc); + for (i = 0; i < 4; i++) { + if (zr-> + stat_com[i] & + 1) + sv[i] = + '1'; + } + sv[4] = 0; + printk(KERN_INFO + "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n", + zr->name, sv, + zr->jpg_que_tail, + zr->jpg_dma_tail, + zr->jpg_dma_head, + zr->jpg_que_head); + } + } else { + if (zr->JPEG_missed > zr->JPEG_max_missed) // Get statistics + zr->JPEG_max_missed = + zr->JPEG_missed; + if (zr->JPEG_missed < + zr->JPEG_min_missed) + zr->JPEG_min_missed = + zr->JPEG_missed; + } +#endif +#if(DEBUGLEVEL > 2) + if (zr->frame_num < 6) { + int i; + printk("%s: seq=%ld stat_com:", + zr->name, zr->jpg_seq_num); + for (i = 0; i < 4; i++) { + printk(" %08x", + zr->stat_com[i]); + } + printk("\n"); + } +#endif + zr->frame_num++; + zr->JPEG_missed = 0; + zr->JPEG_error = 0; + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + wake_up_interruptible(&zr->jpg_capq); + } //else { + // printk(KERN_ERR "%s: JPEG interrupt while not in motion (de)compress mode!\n", zr->name); + //} + } +#endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */ + + if ((astat & cardjpegint[zr->card]) /* DATERR interrupt received */ + ||zr->JPEG_missed > 25 /* Too many fields missed without processing */ + || zr->JPEG_error == 1 /* We are already in error processing */ + || ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) + && (zr-> + frame_num & (zr->JPEG_missed > + zr->params.field_per_buff))) + /* fields missed during decompression */ + ) { + error_handler(zr, astat, stat); + } + + count++; + if (count > 10) { + printk(KERN_WARNING "%s: irq loop %d\n", zr->name, + count); + if (count > 20) { + btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); + printk(KERN_ERR + "%s: IRQ lockup, cleared int mask\n", + zr->name); + break; + } + } + zr->last_isr = stat; + } + spin_unlock_irqrestore(&zr->lock, flags); +} + +/* Check a zoran_params struct for correctness, insert default params */ + +static int zoran_check_params(struct zoran *zr, + struct zoran_params *params) +{ + int err = 0, err0 = 0; + + /* insert constant params */ + + params->major_version = MAJOR_VERSION; + params->minor_version = MINOR_VERSION; + + /* Check input and norm: must be set by calling VIDIOCSCHAN only! */ + + params->norm = zr->params.norm; + params->input = zr->params.input; + + /* Check decimation, set default values for decimation = 1, 2, 4 */ + + switch (params->decimation) { + case 1: + + params->HorDcm = 1; + params->VerDcm = 1; + params->TmpDcm = 1; + params->field_per_buff = 2; + + params->img_x = 0; + params->img_y = 0; + params->img_width = zr->timing->Wa; + params->img_height = zr->timing->Ha / 2; + break; + + case 2: + + params->HorDcm = 2; + params->VerDcm = 1; + params->TmpDcm = 2; + params->field_per_buff = 1; + + params->img_x = 8; + params->img_y = 0; + params->img_width = zr->timing->Wa; + params->img_height = zr->timing->Ha / 2; + break; + + case 4: + + params->HorDcm = 4; + params->VerDcm = 2; + params->TmpDcm = 2; + params->field_per_buff = 1; + + params->img_x = 8; + params->img_y = 0; + params->img_width = zr->timing->Wa; + params->img_height = zr->timing->Ha / 2; + break; + + case 0: + + /* We have to check the data the user has set */ + + if (params->HorDcm != 1 && params->HorDcm != 2 + && params->HorDcm != 4) + err0++; + if (params->VerDcm != 1 && params->VerDcm != 2) + err0++; + if (params->TmpDcm != 1 && params->TmpDcm != 2) + err0++; + if (params->field_per_buff != 1 + && params->field_per_buff != 2) + err0++; + + if (params->img_x < 0) + err0++; + if (params->img_y < 0) + err0++; + if (params->img_width < 0) + err0++; + if (params->img_height < 0) + err0++; + if (params->img_x + params->img_width > zr->timing->Wa) + err0++; + if (params->img_y + params->img_height > + zr->timing->Ha / 2) + err0++; + if (params->HorDcm) { + if (params->img_width % (16 * params->HorDcm) != 0) + err0++; + if (params->img_height % (8 * params->VerDcm) != 0) + err0++; + } + + if (err0) { + DEBUG1(printk(KERN_ERR + "%s: SET PARAMS: error in params for decimation = 0\n", + zr->name)); + err++; + } + break; + + default: + DEBUG1(printk(KERN_ERR + "%s: SET PARAMS: decimation = %d, must be 0, 1, 2 or 4\n", + zr->name, params->decimation)); + err++; + break; + } + + if (params->quality > 100) + params->quality = 100; + if (params->quality < 5) + params->quality = 5; + + if (params->APPn < 0) + params->APPn = 0; + if (params->APPn > 15) + params->APPn = 15; + if (params->APP_len < 0) + params->APP_len = 0; + if (params->APP_len > 60) + params->APP_len = 60; + if (params->COM_len < 0) + params->COM_len = 0; + if (params->COM_len > 60) + params->COM_len = 60; + + if (err) + return -EINVAL; + + return 0; + +} +static void zoran_open_init_params(struct zoran *zr) +{ + int i; + + /* Per default, map the V4L Buffers */ + + zr->map_mjpeg_buffers = 0; + + /* User must explicitly set a window */ + + zr->window_set = 0; + + zr->window.x = 0; + zr->window.y = 0; + zr->window.width = 0; + zr->window.height = 0; + zr->window.chromakey = 0; + zr->window.flags = 0; + zr->window.clips = NULL; + zr->window.clipcount = 0; + + zr->video_interlace = 0; + + zr->v4l_memgrab_active = 0; + zr->v4l_overlay_active = 0; + + zr->v4l_grab_frame = NO_GRAB_ACTIVE; + zr->v4l_grab_seq = 0; + + zr->gwidth = 0; + zr->gheight = 0; + zr->gformat = 0; + zr->gbpl = 0; + + /* DMA ring stuff for V4L */ + + zr->v4l_pend_tail = 0; + zr->v4l_pend_head = 0; + for (i = 0; i < v4l_nbufs; i++) { + zr->v4l_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */ + } + + /* Set necessary params and call zoran_check_params to set the defaults */ + + zr->params.decimation = 1; + + zr->params.quality = 50; /* default compression factor 8 */ + if (zr->card != BUZ) + zr->params.odd_even = 1; + else + zr->params.odd_even = 0; + + zr->params.APPn = 0; + zr->params.APP_len = 0; /* No APPn marker */ + for (i = 0; i < 60; i++) + zr->params.APP_data[i] = 0; + + zr->params.COM_len = 0; /* No COM marker */ + for (i = 0; i < 60; i++) + zr->params.COM_data[i] = 0; + + zr->params.VFIFO_FB = 0; + + memset(zr->params.reserved, 0, sizeof(zr->params.reserved)); + + zr->params.jpeg_markers = JPEG_MARKER_DHT | JPEG_MARKER_DQT; + + i = zoran_check_params(zr, &zr->params); + if (i) + printk(KERN_ERR + "%s: zoran_open_init_params internal error\n", + zr->name); + + clear_interrupt_counters(zr); + zr->testing = 0; +} + +/* + * Open a zoran card. Right now the flags stuff is just playing + */ + +static int zoran_open(struct video_device *dev, int flags) +{ + struct zoran *zr = (struct zoran *) dev; + //int one = 1; + + DEBUG1(printk + (KERN_INFO "%s: zoran_open, %s pid=[%d]\n", zr->name, + current->comm, current->pid)); + + switch (flags) { + + case 0: + if (zr->user > 1) { + DEBUG1(printk(KERN_WARNING + "%s: zoran_open: Buz is allready in use\n", + zr->name)); + return -EBUSY; + } + zr->user++; + + if (zr->user == 1 && v4l_fbuffer_alloc(zr) < 0) { + zr->user--; + printk(KERN_ERR + "%s: zoran_open: v4l_fbuffer_alloc failed\n", + zr->name); + return -ENOMEM; + } + + /* default setup */ + + if (zr->user == 1) { /* First device open */ + zoran_open_init_params(zr); + + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + + btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts + btor(ZR36057_ICR_IntPinEn, ZR36057_ICR); + dev->busy = 0; /* Allow second open */ + } + + break; + + default: + DEBUG1(printk(KERN_WARNING + "%s: zoran_open: flags = 0x%x not yet supported\n", + zr->name, flags)); + return -EBUSY; + break; + + } + MOD_INC_USE_COUNT; + return 0; +} + +static void zoran_close(struct video_device *dev) +{ + struct zoran *zr = (struct zoran *) dev; + int zero = 0, two = 2; + + DEBUG1(printk + (KERN_INFO "%s: zoran_close, %s pid=[%d]\n", zr->name, + current->comm, current->pid)); + /* Clean up JPEG process */ + + wake_up_interruptible(&zr->jpg_capq); + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + jpg_fbuffer_free(zr); + zr->jpg_nbufs = 0; + + if (zr->user == 1) { /* Last process */ + /* disable interrupts */ + btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); + +#if(DEBUGLEVEL > 1) + print_interrupts(zr); +#endif + /* Overlay off */ + wake_up_interruptible(&zr->v4l_capq); + zr36057_set_memgrab(zr, 0); + if (zr->v4l_overlay_active) + zr36057_overlay(zr, 0); + v4l_fbuffer_free(zr); + + if (!pass_through) { /* Switch to color bar */ + set_videobus_enable(zr, 0); + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEODECODER, + DECODER_ENABLE_OUTPUT, &zero); + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEOENCODER, + ENCODER_SET_INPUT, &two); + set_videobus_enable(zr, 1); + } + } + + zr->user--; + + MOD_DEC_USE_COUNT; + DEBUG2(printk(KERN_INFO "%s: zoran_close done\n", zr->name)); +} + + +static long zoran_read(struct video_device *dev, char *buf, + unsigned long count, int nonblock) +{ + return -EINVAL; +} + +static long zoran_write(struct video_device *dev, const char *buf, + unsigned long count, int nonblock) +{ + return -EINVAL; +} + +/* + * ioctl routine + */ + +static int do_zoran_ioctl(struct zoran *zr, unsigned int cmd, + void *arg) +{ + switch (cmd) { + + case VIDIOCGCAP: + { + struct video_capability b; + DEBUG2(printk("%s: ioctl VIDIOCGCAP\n", zr->name)); + + strncpy(b.name, zr->video_dev.name, + sizeof(b.name)); + b.type = + VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | + VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM | + VID_TYPE_SCALES; + /* theoretically we could also flag VID_TYPE_SUBCAPTURE + but this is not even implemented in the BTTV driver */ + + if (zr->card == DC10 || zr->card == DC10plus) { + b.channels = 3; /* composite, svhs, internal */ + } else { + b.channels = 2; /* composite, svhs */ + } + b.audios = 0; + b.maxwidth = BUZ_MAX_WIDTH; + b.maxheight = BUZ_MAX_HEIGHT; + b.minwidth = BUZ_MIN_WIDTH; + b.minheight = BUZ_MIN_HEIGHT; + if (copy_to_user(arg, &b, sizeof(b))) { + return -EFAULT; + } + return 0; + } + break; + + case VIDIOCGCHAN: + { + struct video_channel v; + + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + DEBUG2(printk + ("%s: ioctl VIDIOCGCHAN for channel %d\n", + zr->name, v.channel)); + switch (v.channel) { + case 0: + strcpy(v.name, "Composite"); + break; + case 1: + strcpy(v.name, "SVHS"); + break; + case 2: + if (zr->card == DC10 + || zr->card == DC10plus) { + strcpy(v.name, "Internal/comp"); + break; + } + default: + DEBUG1(printk(KERN_ERR + "%s: VIDIOCGCHAN on not existing channel %d\n", + zr->name, v.channel)); + return -EINVAL; + } + v.tuners = 0; + v.flags = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = zr->params.norm; + if (copy_to_user(arg, &v, sizeof(v))) { + return -EFAULT; + } + return 0; + } + break; + + /* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says: + + * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input." + * ^^^^^^^ + * The famos BTTV driver has it implemented with a struct video_channel argument + * and we follow it for compatibility reasons + * + * BTW: this is the only way the user can set the norm! + */ + + case VIDIOCSCHAN: + { + struct video_channel v; + int input; + int on, res; + int encoder_norm; + + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + + if (zr->codec_mode != BUZ_MODE_IDLE) { + if (v.norm != zr->params.norm + || v.channel != zr->params.input) { + DEBUG1(printk(KERN_ERR + "%s: VIDIOCSCHAN called while the card in capture/playback mode\n", + zr->name)); + return -EINVAL; + } else { + DEBUG1(printk(BUZ_WARNING + "%s: Warning: VIDIOCSCHAN called while the card in capture/playback mode\n", + zr->name)); + } + } + + DEBUG2(printk + ("%s: ioctl VIDIOCSCHAN: channel=%d, norm=%d\n", + zr->name, v.channel, v.norm)); + switch (v.channel) { + case 0: + if (zr->card == BUZ) + input = 3; + else + input = 0; + break; + case 1: + input = 7; + break; + case 2: + if (zr->card == DC10 + || zr->card == DC10plus) { + input = 5; + break; + } + default: + DEBUG1(printk(KERN_ERR + "%s: VIDIOCSCHAN on not existing channel %d\n", + zr->name, v.channel)); + return -EINVAL; + break; + } + + if (lock_norm && v.norm != zr->params.norm) { + if (lock_norm > 1) { + DEBUG1(printk(KERN_WARNING + "%s: VIDIOCSCHAN: TV standard is locked, can not switch norm.\n", + zr->name)); + return -EINVAL; + } else { + DEBUG1(printk(KERN_WARNING + "%s: VIDIOCSCHAN: TV standard is locked, norm was not changed.\n", + zr->name)); + v.norm = zr->params.norm; + } + } + + if (!cardnorms[zr->card][v.norm]) { + DEBUG1(printk(KERN_ERR + "%s: VIDIOCSCHAN with not supported norm %d\n", + zr->name, v.norm)); + return -EOPNOTSUPP; + break; + } + encoder_norm = v.norm; + + zr->params.norm = v.norm; + zr->params.input = v.channel; + zr->timing = cardnorms[zr->card][zr->params.norm]; + + /* We switch overlay off and on since a change in the norm + needs different VFE settings */ + + on = zr->v4l_overlay_active + && !zr->v4l_memgrab_active; + if (on) + zr36057_overlay(zr, 0); + + set_videobus_enable(zr, 0); + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEODECODER, + DECODER_SET_INPUT, &input); + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEODECODER, + DECODER_SET_NORM, + &zr->params.norm); + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEOENCODER, + ENCODER_SET_NORM, + &encoder_norm); + set_videobus_enable(zr, 1); + + if (on) + zr36057_overlay(zr, 1); + + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); + if (res) + return res; + + return 0; + } + break; + + case VIDIOCGTUNER: + { + DEBUG1(printk(KERN_ERR + "%s: ioctl VIDIOCGTUNER not supported\n", + zr->name)); + return -EINVAL; + } + break; + + case VIDIOCSTUNER: + { + DEBUG1(printk(KERN_ERR + "%s: ioctl VIDIOCSTUNER not supported\n", + zr->name)); + return -EINVAL; + } + break; + + case VIDIOCGPICT: + { + struct video_picture p = zr->picture; + + DEBUG2(printk + ("%s: ioctl VIDIOCGPICT\n", zr->name)); + p.depth = zr->buffer.depth; + switch (zr->buffer.depth) { + case 15: + p.palette = VIDEO_PALETTE_RGB555; + break; + + case 16: + p.palette = VIDEO_PALETTE_RGB565; + break; + + case 24: + p.palette = VIDEO_PALETTE_RGB24; + break; + + case 32: + p.palette = VIDEO_PALETTE_RGB32; + break; + } + + if (copy_to_user(arg, &p, sizeof(p))) { + return -EFAULT; + } + return 0; + } + break; + + case VIDIOCSPICT: + { + struct video_picture p; + + if (copy_from_user(&p, arg, sizeof(p))) { + return -EFAULT; + } + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEODECODER, + DECODER_SET_PICTURE, &p); + DEBUG2(printk + ("%s: ioctl VIDIOCSPICT bri=%d hue=%d col=%d con=%d dep=%d pal=%d\n", + zr->name, p.brightness, p.hue, p.colour, + p.contrast, p.depth, p.palette)); + /* The depth and palette values have no meaning to us, + should we return -EINVAL if they don't fit ? */ + zr->picture = p; + return 0; + } + break; + + case VIDIOCCAPTURE: + { + int v, res; + + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + DEBUG2(printk + ("%s: ioctl VIDIOCCAPTURE: %d\n", zr->name, + v)); + + /* If there is nothing to do, return immediatly */ + + if ((v && zr->v4l_overlay_active) + || (!v && !zr->v4l_overlay_active)) + return 0; + + if (v == 0) { + zr->v4l_overlay_active = 0; + if (!zr->v4l_memgrab_active) + zr36057_overlay(zr, 0); + /* When a grab is running, the video simply won't be switched on any more */ + } else { + if (!zr->buffer_set || !zr->window_set) { + DEBUG1(printk(KERN_ERR + "%s: VIDIOCCAPTURE: buffer or window not set\n", + zr->name)); + return -EINVAL; + } + zr->v4l_overlay_active = 1; + if (!zr->v4l_memgrab_active) + zr36057_overlay(zr, 1); + /* When a grab is running, the video will be switched on when grab is finished */ + } + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); + if (res) + return res; + return 0; + } + break; + + case VIDIOCGWIN: + { + DEBUG2(printk("%s: ioctl VIDIOCGWIN\n", zr->name)); + if (copy_to_user + (arg, &zr->window, sizeof(zr->window))) { + return -EFAULT; + } + return 0; + } + break; + + case VIDIOCSWIN: + { + struct video_clip *vcp; + struct video_window vw; + struct tvnorm *tvn; + int on, end, res, Wa, Ha; + + tvn = zr->timing; + + Wa = tvn->Wa; + Ha = tvn->Ha; + + if (copy_from_user(&vw, arg, sizeof(vw))) { + return -EFAULT; + } + + DEBUG2(printk + ("%s: ioctl VIDIOCSWIN: x=%d y=%d w=%d h=%d clipcount=%d\n", + zr->name, vw.x, vw.y, vw.width, vw.height, + vw.clipcount)); + + if (!zr->buffer_set) { + DEBUG1(printk(KERN_ERR + "%s: VIDIOCSWIN: frame buffer has to be set first\n", + zr->name)); + return -EINVAL; + } + + /* + * The video front end needs 4-byte alinged line sizes, we correct that + * silently here if necessary + */ + + if (zr->buffer.depth == 15 + || zr->buffer.depth == 16) { + end = (vw.x + vw.width) & ~1; /* round down */ + vw.x = (vw.x + 1) & ~1; /* round up */ + vw.width = end - vw.x; + } + + if (zr->buffer.depth == 24) { + end = (vw.x + vw.width) & ~3; /* round down */ + vw.x = (vw.x + 3) & ~3; /* round up */ + vw.width = end - vw.x; + } + + if (vw.width > Wa) + vw.width = Wa; + if (vw.height > Ha) + vw.height = Ha; + + /* Check for vaild parameters */ + if (vw.width < BUZ_MIN_WIDTH + || vw.height < BUZ_MIN_HEIGHT + || vw.width > BUZ_MAX_WIDTH + || vw.height > BUZ_MAX_HEIGHT) { + DEBUG1(printk(KERN_ERR + "%s: VIDIOCSWIN: width = %d or height = %d invalid\n", + zr->name, vw.width, vw.height)); + return -EINVAL; + } + + zr->window.x = vw.x; + zr->window.y = vw.y; + zr->window.width = vw.width; + zr->window.height = vw.height; + zr->window.chromakey = 0; + zr->window.flags = 0; // RJ: Is this intended for interlace on/off ? + zr->window.clips = NULL; + zr->window.clipcount = vw.clipcount; + + /* + * If an overlay is running, we have to switch it off + * and switch it on again in order to get the new settings in effect. + * + * We also want to avoid that the overlay mask is written + * when an overlay is running. + */ + + on = zr->v4l_overlay_active + && !zr->v4l_memgrab_active; + if (on) + zr36057_overlay(zr, 0); + + /* + * Write the overlay mask if clips are wanted. + */ + if (vw.clipcount) { + vcp = + vmalloc(sizeof(struct video_clip) * + (vw.clipcount + 4)); + if (vcp == NULL) { + printk(KERN_ERR + "%s: zoran_ioctl: Alloc of clip mask failed\n", + zr->name); + return -ENOMEM; + } + if (copy_from_user + (vcp, vw.clips, + sizeof(struct video_clip) * + vw.clipcount)) { + vfree(vcp); + return -EFAULT; + } + write_overlay_mask(zr, vcp, vw.clipcount); + vfree(vcp); + } + + if (on) + zr36057_overlay(zr, 1); + zr->window_set = 1; + + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); + if (res) + return res; + + return 0; + } + break; + + case VIDIOCGFBUF: + { + DEBUG2(printk + ("%s: ioctl VIDIOCGFBUF\n", zr->name)); + if (copy_to_user + (arg, &zr->buffer, sizeof(zr->buffer))) { + return -EFAULT; + } + return 0; + } + break; + + case VIDIOCSFBUF: + { + struct video_buffer v; + + /* RJ: Isn't this too restrictive? As long as the user doesn't set + the base address it shouldn't be too dangerous */ + + if (!capable(CAP_SYS_ADMIN)) { + DEBUG1(printk(KERN_ERR + "%s: Only the superuser may issue VIDIOCSFBUF ioctl\n", + zr->name)); + return -EPERM; + } + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + DEBUG2(printk + ("%s: ioctl VIDIOCSFBUF: base=0x%x w=%d h=%d depth=%d bpl=%d\n", + zr->name, (u32) v.base, v.width, v.height, + v.depth, v.bytesperline)); + if (zr->v4l_overlay_active) { + /* Has the user gotten crazy ... ? */ + DEBUG1(printk(KERN_ERR + "%s: VIDIOCSFBUF not allowed when overlay active\n", + zr->name)); + return -EINVAL; + } + if (v.depth != 15 && v.depth != 16 && v.depth != 24 + && v.depth != 32) { + DEBUG1(printk(KERN_ERR + "%s: VIDIOCSFBUF: depth=%d not supported\n", + zr->name, v.depth)); + return -EINVAL; + } + if (v.height <= 0 || v.width <= 0 + || v.bytesperline <= 0) { + DEBUG1(printk(KERN_ERR + "%s: VIDIOCSFBUF: invalid height/width/bpl value\n", + zr->name)); + return -EINVAL; + } + if (v.bytesperline & 3) { + DEBUG1(printk(KERN_ERR + "%s: VIDIOCSFBUF: bytesperline must be 4-byte aligned\n", + zr->name)); + return -EINVAL; + } + if (v.base) { + zr->buffer.base = + (void *) ((unsigned long) v.base & ~3); + } + zr->buffer.height = v.height; + zr->buffer.width = v.width; + zr->buffer.depth = v.depth; + zr->buffer.bytesperline = v.bytesperline; + + if (zr->buffer.base) + zr->buffer_set = 1; + zr->window_set = 0; /* The user should set new window parameters */ + return 0; + } + break; + + /* RJ: what is VIDIOCKEY intended to do ??? */ + + case VIDIOCKEY: + { + /* Will be handled higher up .. */ + DEBUG2(printk("%s: ioctl VIDIOCKEY\n", zr->name)); + return 0; + } + break; + + case VIDIOCGFREQ: + { + DEBUG1(printk(KERN_ERR + "%s: ioctl VIDIOCGFREQ not supported\n", + zr->name)); + return -EINVAL; + } + break; + + case VIDIOCSFREQ: + { + DEBUG1(printk(KERN_ERR + "%s: ioctl VIDIOCSFREQ not supported\n", + zr->name)); + return -EINVAL; + } + break; + + case VIDIOCGAUDIO: + { + DEBUG1(printk(KERN_ERR + "%s: ioctl VIDIOCGAUDIO not supported\n", + zr->name)); + return -EINVAL; + } + break; + + case VIDIOCSAUDIO: + { + DEBUG1(printk(KERN_ERR + "%s: ioctl VIDIOCSAUDIO not supported\n", + zr->name)); + return -EINVAL; + } + break; + + case VIDIOCSYNC: + { + int v; + + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + DEBUG3(printk + ("%s: ioctl VIDIOCSYNC %d\n", zr->name, v)); + return v4l_sync(zr, v); + } + break; + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (copy_from_user + ((void *) &vm, (void *) arg, sizeof(vm))) { + return -EFAULT; + } + DEBUG2(printk + ("%s: ioctl VIDIOCMCAPTURE frame=%d geom=%dx%d fmt=%d\n", + zr->name, vm.frame, vm.width, vm.height, + vm.format)); + return v4l_grab(zr, &vm); + } + break; + + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + DEBUG2(printk + ("%s: ioctl VIDIOCGMBUF\n", zr->name)); + vm.size = v4l_nbufs * v4l_bufsize; + vm.frames = v4l_nbufs; + for (i = 0; i < v4l_nbufs; i++) { + vm.offsets[i] = i * v4l_bufsize; + } + + /* The next mmap will map the V4L buffers */ + zr->map_mjpeg_buffers = 0; + + if (copy_to_user(arg, &vm, sizeof(vm))) { + return -EFAULT; + } + return 0; + } + break; + + case VIDIOCGUNIT: + { + struct video_unit vu; + + DEBUG2(printk + ("%s: ioctl VIDIOCGUNIT\n", zr->name)); + vu.video = zr->video_dev.minor; + vu.vbi = VIDEO_NO_UNIT; + vu.radio = VIDEO_NO_UNIT; + vu.audio = VIDEO_NO_UNIT; + vu.teletext = VIDEO_NO_UNIT; + if (copy_to_user(arg, &vu, sizeof(vu))) { + return -EFAULT; + } + return 0; + } + break; + + /* + * RJ: In principal we could support subcaptures for V4L grabbing. + * Not even the famous BTTV driver has them, however. + * If there should be a strong demand, one could consider + * to implement them. + */ + case VIDIOCGCAPTURE: + { + DEBUG1(printk(KERN_ERR + "%s: ioctl VIDIOCGCAPTURE not supported\n", + zr->name)); + return -EINVAL; + } + break; + + case VIDIOCSCAPTURE: + { + DEBUG1(printk(KERN_ERR + "%s: ioctl VIDIOCSCAPTURE not supported\n", + zr->name)); + return -EINVAL; + } + break; + + case BUZIOC_G_PARAMS: + { + DEBUG2(printk + ("%s: ioctl BUZIOC_G_PARAMS\n", zr->name)); + + if (copy_to_user + (arg, &(zr->params), sizeof(zr->params))) { + return -EFAULT; + } + return 0; + } + break; + + case BUZIOC_S_PARAMS: + { + struct zoran_params bp; + /* int input, on; */ + + if (zr->codec_mode != BUZ_MODE_IDLE) { + DEBUG1(printk(KERN_ERR + "%s: BUZIOC_S_PARAMS called but Buz in capture/playback mode\n", + zr->name)); + return -EINVAL; + } + + if (copy_from_user(&bp, arg, sizeof(bp))) { + return -EFAULT; + } + DEBUG2(printk + ("%s: ioctl BUZIOC_S_PARAMS\n", zr->name)); + + /* Check the params first before overwriting our internal values */ + + if (zoran_check_params(zr, &bp)) + return -EINVAL; + + zr->params = bp; + + /* Make changes of input and norm go into effect immediatly */ + + /* We switch overlay off and on since a change in the norm + needs different VFE settings */ + + if (copy_to_user(arg, &bp, sizeof(bp))) { + return -EFAULT; + } + return 0; + } + break; + + case BUZIOC_REQBUFS: + { + struct zoran_requestbuffers br; + + if (zr->jpg_buffers_allocated) { + DEBUG1(printk(KERN_ERR + "%s: BUZIOC_REQBUFS: buffers allready allocated\n", + zr->name)); + return -EINVAL; + } + if (copy_from_user(&br, arg, sizeof(br))) { + return -EFAULT; + } + DEBUG2(printk + ("%s: ioctl BUZIOC_REQBUFS count = %lu size=%lu\n", + zr->name, br.count, br.size)); + + /* Enforce reasonable lower and upper limits */ + if (br.count < 4) + br.count = 4; /* Could be choosen smaller */ + if (br.count > BUZ_MAX_FRAME) + br.count = BUZ_MAX_FRAME; + br.size = PAGE_ALIGN(br.size); + if (br.size < 8192) + br.size = 8192; /* Arbitrary */ + /* br.size is limited by 1 page for the stat_com tables to a Maximum of 2 MB */ + if (br.size > (512 * 1024)) + br.size = (512 * 1024); /* 512 K should be enough */ + if (zr->need_contiguous + && br.size > MAX_KMALLOC_MEM) + br.size = MAX_KMALLOC_MEM; + + zr->jpg_nbufs = br.count; + zr->jpg_bufsize = br.size; + + if (jpg_fbuffer_alloc(zr)) + return -ENOMEM; + + /* The next mmap will map the MJPEG buffers */ + zr->map_mjpeg_buffers = 1; + + if (copy_to_user(arg, &br, sizeof(br))) { + return -EFAULT; + } + return 0; + } + break; + + case BUZIOC_QBUF_CAPT: + { + int nb; + + if (copy_from_user + ((void *) &nb, (void *) arg, sizeof(int))) { + return -EFAULT; + } + DEBUG4(printk + ("%s: ioctl BUZIOC_QBUF_CAPT %d\n", + zr->name, nb)); + return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_COMPRESS); + } + break; + + case BUZIOC_QBUF_PLAY: + { + int nb; + + if (copy_from_user + ((void *) &nb, (void *) arg, sizeof(int))) { + return -EFAULT; + } + DEBUG4(printk + ("%s: ioctl BUZIOC_QBUF_PLAY %d\n", + zr->name, nb)); + return jpg_qbuf(zr, nb, + BUZ_MODE_MOTION_DECOMPRESS); + } + break; + + case BUZIOC_SYNC: + { + struct zoran_sync bs; + int res; + + DEBUG4(printk + ("%s: ioctl BUZIOC_SYNC\n", zr->name)); + res = jpg_sync(zr, &bs); + if (copy_to_user(arg, &bs, sizeof(bs))) { + return -EFAULT; + } + return res; + } + break; + + case BUZIOC_G_STATUS: + { + struct zoran_status bs; + int norm, input, status; + unsigned long timeout; + + if (zr->codec_mode != BUZ_MODE_IDLE) { + DEBUG1(printk(KERN_ERR + "%s: BUZIOC_G_STATUS called but Buz in capture/playback mode\n", + zr->name)); + return -EINVAL; + } + + if (copy_from_user(&bs, arg, sizeof(bs))) { + return -EFAULT; + } + DEBUG2(printk + ("%s: ioctl BUZIOC_G_STATUS\n", zr->name)); + + switch (bs.input) { + case 0: + if (zr->card == BUZ) + input = 3; + else + input = 0; + break; + case 1: + input = 7; + break; + default: + DEBUG1(printk(KERN_ERR + "%s: BUZIOC_G_STATUS on not existing input %d\n", + zr->name, bs.input)); + return -EINVAL; + } + + /* Set video norm to VIDEO_MODE_AUTO */ + + norm = VIDEO_MODE_AUTO; + set_videobus_enable(zr, 0); + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEODECODER, + DECODER_SET_INPUT, &input); + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEODECODER, + DECODER_SET_NORM, &norm); + set_videobus_enable(zr, 1); + + /* sleep 1 second */ + + timeout = jiffies + 1 * HZ; + while (jiffies < timeout) + schedule(); + + /* Get status of video decoder */ + + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEODECODER, + DECODER_GET_STATUS, &status); + bs.signal = (status & DECODER_STATUS_GOOD) ? 1 : 0; + + if (status & DECODER_STATUS_NTSC) + bs.norm = VIDEO_MODE_NTSC; + else if (status & DECODER_STATUS_SECAM) + bs.norm = VIDEO_MODE_SECAM; + else + bs.norm = VIDEO_MODE_PAL; + + bs.color = (status & DECODER_STATUS_COLOR) ? 1 : 0; + + /* restore previous input and norm */ + if (zr->card == BUZ) + input = zr->params.input == 0 ? 3 : 7; + else + input = zr->params.input == 0 ? 0 : 7; + set_videobus_enable(zr, 0); + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEODECODER, + DECODER_SET_INPUT, &input); + i2c_control_device(&zr->i2c, + I2C_DRIVERID_VIDEODECODER, + DECODER_SET_NORM, + &zr->params.norm); + set_videobus_enable(zr, 1); + + if (copy_to_user(arg, &bs, sizeof(bs))) { + return -EFAULT; + } + return 0; + } + break; + + default: + DEBUG1(printk + ("%s: UNKNOWN ioctl cmd: 0x%x\n", zr->name, cmd)); + return -ENOIOCTLCMD; + } + return 0; +} + +static int zoran_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct zoran *zr = (struct zoran *) dev; + int err; + + down(&zr->sem); + err = do_zoran_ioctl(zr, cmd, arg); + up(&zr->sem); + + return err; +} + +/* + * This maps the buffers to user space. + * + * Depending on the state of zr->map_mjpeg_buffers + * the V4L or the MJPEG buffers are mapped + * + */ + +static int do_zoran_mmap(struct zoran *zr, const char *adr, + unsigned long size) +{ + unsigned long start = (unsigned long) adr; + unsigned long page, pos, todo, fraglen; + int i, j; + + DEBUG2(printk + (KERN_INFO "%s: mmap at 0x%08lx, size %lu\n", zr->name, + start, size)); + if (zr->map_mjpeg_buffers) { + /* Map the MJPEG buffers */ + + if (!zr->jpg_buffers_allocated) { + DEBUG1(printk(KERN_ERR + "%s: zoran_mmap(MJPEG): buffers not yet allocated\n", + zr->name)); + return -ENOMEM; + } + + if (size > zr->jpg_nbufs * zr->jpg_bufsize) { + DEBUG1(printk(KERN_ERR + "%s: zoran_mmap(MJPEG): Max size is %lu - you wanted %lu\n", + zr->name, zr->jpg_nbufs * zr->jpg_bufsize, + size)); + return -EINVAL; + } + + if (size != zr->jpg_nbufs * zr->jpg_bufsize) + DEBUG1(printk(KERN_WARNING + "%s: zoran_mmap(MJPEG): Expected %lu - you wanted %lu\n", + zr->name, zr->jpg_nbufs * zr->jpg_bufsize, + size)); + + for (i = 0; i < zr->jpg_nbufs; i++) { + for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) { + fraglen = + (zr->jpg_gbuf[i]. + frag_tab[2 * j + 1] & ~1) << 1; + todo = size; + if (todo > fraglen) + todo = fraglen; + pos = + (unsigned long) zr->jpg_gbuf[i]. + frag_tab[2 * j]; + page = virt_to_phys(bus_to_virt(pos)); /* should just be pos on i386 */ + if (remap_page_range + (start, page, todo, PAGE_SHARED)) { + printk(KERN_ERR + "%s: zoran_mmap(V4L): remap_page_range failed\n", + zr->name); + return -EAGAIN; + } + size -= todo; + start += todo; + if (size == 0) + break; + if (zr->jpg_gbuf[i]. + frag_tab[2 * j + 1] & 1) + break; /* was last fragment */ + } + if (size == 0) + break; + } + } else { + /* Map the V4L buffers */ + + if (size > v4l_nbufs * v4l_bufsize) { + DEBUG1(printk(KERN_ERR + "%s: zoran_mmap(V4L): Max size is %d - you wanted %ld\n", + zr->name, v4l_nbufs * v4l_bufsize, size)); + return -EINVAL; + } + + if (size != v4l_nbufs * v4l_bufsize) + DEBUG1(printk(KERN_WARNING + "%s: zoran_mmap(V4L): Expected %d - you wanted %ld\n", + zr->name, v4l_nbufs * v4l_bufsize, size)); + + for (i = 0; i < v4l_nbufs; i++) { + todo = size; + if (todo > v4l_bufsize) + todo = v4l_bufsize; + page = zr->v4l_gbuf[i].fbuffer_phys; + DEBUG2(printk + ("V4L remap page range %d 0x%lx %ld to 0x%lx\n", + i, page, todo, start)); + if (remap_page_range + (start, page, todo, PAGE_SHARED)) { + printk(KERN_ERR + "%s: zoran_mmap(V4L): remap_page_range failed\n", + zr->name); + return -EAGAIN; + } + size -= todo; + start += todo; + if (size == 0) + break; + } + } + return 0; +} + +static int zoran_mmap(struct video_device *dev, const char *adr, + unsigned long size) +{ + int err; + struct zoran *zr = (struct zoran *) dev; + + down(&zr->sem); + err = do_zoran_mmap(zr, adr, size); + up(&zr->sem); + + return err; +} + +static int zoran_init_done(struct video_device *dev) +{ + return 0; +} + +static struct video_device zoran_template = { + THIS_MODULE, + ZORAN_NAME, + VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | VID_TYPE_CLIPPING | + VID_TYPE_FRAMERAM | VID_TYPE_SCALES | VID_TYPE_SUBCAPTURE, + ZORAN_HARDWARE, + zoran_open, + zoran_close, + zoran_read, + zoran_write, + NULL, + zoran_ioctl, + zoran_mmap, + zoran_init_done, + NULL, + 0, + 0 +}; + +/* + * initialize video front end + */ +static void zr36057_init_vfe(struct zoran *zr) +{ + u32 reg; + reg = btread(ZR36057_VFESPFR); + reg |= ZR36057_VFESPFR_LittleEndian; + reg &= ~ZR36057_VFESPFR_VCLKPol; + reg |= ZR36057_VFESPFR_ExtFl; + reg |= ZR36057_VFESPFR_TopField; + btwrite(reg, ZR36057_VFESPFR); + reg = btread(ZR36057_VDCR); + if (triton || zr->revision <= 1) + reg &= ~ZR36057_VDCR_Triton; + else + reg |= ZR36057_VDCR_Triton; + btwrite(reg, ZR36057_VDCR); +} + +static void test_interrupts(struct zoran *zr) +{ + int timeout, icr; + + clear_interrupt_counters(zr); + zr->testing = 1; + icr = btread(ZR36057_ICR); + btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR); + timeout = interruptible_sleep_on_timeout(&zr->test_q, 1 * HZ); + btwrite(0, ZR36057_ICR); + btwrite(0x78000000, ZR36057_ISR); + zr->testing = 0; + printk(KERN_INFO "%s: Testing interrupts...\n", zr->name); + if (timeout) { + printk(": time spent: %d\n", 1 * HZ - timeout); + } + print_interrupts(zr); + btwrite(icr, ZR36057_ICR); +} + +static int zr36057_init(int i) +{ + struct zoran *zr = &zoran[i]; + unsigned long mem; + unsigned mem_needed; + int j; + int two = 2; + int zero = 0; + + printk(KERN_INFO "%s: Initializing card[%d], zr=%x\n", zr->name, i, (int) zr); + + /* default setup of all parameters which will persist beetween opens */ + + zr->user = 0; + + init_waitqueue_head(&zr->v4l_capq); + init_waitqueue_head(&zr->jpg_capq); + init_waitqueue_head(&zr->test_q); + + zr->map_mjpeg_buffers = 0; /* Map V4L buffers by default */ + + zr->jpg_nbufs = 0; + zr->jpg_bufsize = 0; + zr->jpg_buffers_allocated = 0; + + zr->buffer_set = 0; /* Flag if frame buffer has been set */ + zr->buffer.base = (void *) vidmem; + zr->buffer.width = 0; + zr->buffer.height = 0; + zr->buffer.depth = 0; + zr->buffer.bytesperline = 0; + + zr->params.norm = default_norm = (default_norm < 3 ? default_norm : VIDEO_MODE_PAL); /* Avoid nonsense settings from user */ + if (!(zr->timing = cardnorms[zr->card][zr->params.norm])) { + printk(KERN_WARNING + "%s: default TV statdard not supported by hardware. PAL will be used.\n", + zr->name); + zr->params.norm = VIDEO_MODE_PAL; + zr->timing = cardnorms[zr->card][zr->params.norm]; + } + zr->params.input = default_input = (default_input ? 1 : 0); /* Avoid nonsense settings from user */ + zr->video_interlace = 0; + + /* Should the following be reset at every open ? */ + + zr->picture.colour = 32768; + zr->picture.brightness = 32768; + zr->picture.hue = 32768; + zr->picture.contrast = 32768; + zr->picture.whiteness = 0; + zr->picture.depth = 0; + zr->picture.palette = 0; + + for (j = 0; j < VIDEO_MAX_FRAME; j++) { + zr->v4l_gbuf[i].fbuffer = 0; + zr->v4l_gbuf[i].fbuffer_phys = 0; + zr->v4l_gbuf[i].fbuffer_bus = 0; + } + + zr->stat_com = 0; + + /* default setup (will be repeated at every open) */ + + zoran_open_init_params(zr); + + /* allocate memory *before* doing anything to the hardware in case allocation fails */ + + /* STAT_COM table and overlay mask */ + + mem_needed = (BUZ_NUM_STAT_COM + ((BUZ_MAX_WIDTH + 31) / 32) * BUZ_MAX_HEIGHT) * 4; + mem = (unsigned long) kmalloc(mem_needed, GFP_KERNEL); + if (!mem) { + printk(KERN_ERR "%s: zr36057_init: kmalloc (STAT_COM + ovl.mask) failed\n", zr->name); + return -ENOMEM; + } + memset((void *) mem, 0, mem_needed); + + zr->stat_com = (u32 *) mem; + for (j = 0; j < BUZ_NUM_STAT_COM; j++) { + zr->stat_com[j] = 1; /* mark as unavailable to zr36057 */ + } + zr->overlay_mask = (u32 *) (mem + BUZ_NUM_STAT_COM * 4); + + /* Initialize zr->jpg_gbuf */ + + for (j = 0; j < BUZ_MAX_FRAME; j++) { + zr->jpg_gbuf[j].frag_tab = 0; + zr->jpg_gbuf[j].frag_tab_bus = 0; + zr->jpg_gbuf[j].state = BUZ_STATE_USER; + zr->jpg_gbuf[j].bs.frame = j; + } + + /* + * Now add the template and register the device unit. + */ + memcpy(&zr->video_dev, &zoran_template, sizeof(zoran_template)); + strcpy(zr->video_dev.name, zr->name); + if (video_register_device(&zr->video_dev, VFL_TYPE_GRABBER, video_nr) < 0) { + i2c_unregister_bus(&zr->i2c); + kfree((void *) zr->stat_com); + return -1; + } + + /* Enable bus-mastering */ + pci_set_master(zr->pci_dev); + + if (zr->card == BUZ) + j = zr->params.input == 0 ? 3 : 7; + else + j = zr->params.input == 0 ? 0 : 7; + set_videobus_enable(zr, 0); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, + DECODER_SET_INPUT, &j); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, + DECODER_SET_NORM, &zr->params.norm); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, + ENCODER_SET_NORM, &zr->params.norm); + set_videobus_enable(zr, 1); + + /* toggle JPEG codec sleep to sync PLL */ + zr36060_sleep(zr, 1); + zr36060_sleep(zr, 0); + + /* set individual interrupt enables (without GIRQ1) + but don't global enable until zoran_open() */ + + //btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ1, ZR36057_ICR); // SW + // It looks like using only JPEGRepIRQEn is not always reliable, + // may be when JPEG codec crashes it won't generate IRQ? So, + btwrite(IRQ_MASK, ZR36057_ICR); // Enable Vsync interrupts too. SM + + zr36057_init_vfe(zr); + + zr->zoran_proc = NULL; + zr->initialized = 1; + + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); +#if (DEBUGLEVEL > 2) + detect_guest_activity(zr); +#endif + test_interrupts(zr); + btwrite(IRQ_MASK, ZR36057_ICR); // Enable Vsync interrupts too. SM + if (!pass_through) { + set_videobus_enable(zr, 0); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, + DECODER_ENABLE_OUTPUT, &zero); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, + ENCODER_SET_INPUT, &two); + set_videobus_enable(zr, 1); + } + return 0; +} + +#include "zoran_procfs.c" + +static void release_dc10(void) +{ + u8 command; + int i; + struct zoran *zr; + + for (i = 0; i < zoran_num; i++) { + zr = &zoran[i]; + + if (!zr->initialized) + continue; + + /* unregister i2c_bus */ + i2c_unregister_bus((&zr->i2c)); + + /* disable PCI bus-mastering */ + pci_read_config_byte(zr->pci_dev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MASTER; + pci_write_config_byte(zr->pci_dev, PCI_COMMAND, command); + + /* put chip into reset */ + btwrite(0, ZR36057_SPGPPCR); + + free_irq(zr->pci_dev->irq, zr); + + /* unmap and free memory */ + + kfree((void *) zr->stat_com); + + zoran_proc_cleanup(i); + iounmap(zr->zr36057_mem); + + video_unregister_device(&zr->video_dev); + } +} + +/* + * Scan for a Buz card (actually for the PCI contoler ZR36057), + * request the irq and map the io memory + */ + +static int find_zr36057(void) +{ + unsigned char latency, need_latency; + struct zoran *zr; + struct pci_dev *dev = NULL; + int result; + + zoran_num = 0; + + while (zoran_num < BUZ_MAX + && (dev = + pci_find_device(PCI_VENDOR_ID_ZORAN, + PCI_DEVICE_ID_ZORAN_36057, + dev)) != NULL) { + zr = &zoran[zoran_num]; + zr->pci_dev = dev; + zr->zr36057_mem = NULL; + zr->id = zoran_num; + sprintf(zr->name, "MJPEG[%u]", zr->id); + + spin_lock_init(&zr->lock); + init_MUTEX(&zr->sem); + + if (pci_enable_device(dev)) + continue; + + zr->zr36057_adr = pci_resource_start(zr->pci_dev, 0); + pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION, + &zr->revision); + if (zr->revision < 2) { + printk(KERN_INFO + "%s: Zoran ZR36057 (rev %d) irq: %d, memory: 0x%08x.\n", + zr->name, zr->revision, zr->pci_dev->irq, + zr->zr36057_adr); + } else { + unsigned short ss_vendor_id, ss_id; + ss_vendor_id = zr->pci_dev->subsystem_vendor; + ss_id = zr->pci_dev->subsystem_device; + printk(KERN_INFO + "%s: Zoran ZR36067 (rev %d) irq: %d, memory: 0x%08x\n", + zr->name, zr->revision, zr->pci_dev->irq, + zr->zr36057_adr); + printk(KERN_INFO + "%s: subsystem vendor=0x%04x id=0x%04x\n", + zr->name, ss_vendor_id, ss_id); + } + + zr->zr36057_mem = ioremap_nocache(zr->zr36057_adr, 0x1000); + if (!zr->zr36057_mem) { + printk(KERN_ERR "%s: ioremap failed\n", zr->name); + /* XXX handle error */ + } + + result = request_irq(zr->pci_dev->irq, zoran_irq, SA_SHIRQ | SA_INTERRUPT, zr->name, (void *) zr); + if (result < 0) { + if (result == -EINVAL) { + printk(KERN_ERR + "%s: Bad irq number or handler\n", + zr->name); + } else if (result == -EBUSY) { + printk(KERN_ERR + "%s: IRQ %d busy, change your PnP config in BIOS\n", + zr->name, zr->pci_dev->irq); + } else { + printk(KERN_ERR + "%s: Can't assign irq, error code %d\n", + zr->name, result); + } + iounmap(zr->zr36057_mem); + continue; + } + + /* set PCI latency timer */ + pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, &latency); + need_latency = zr->revision > 1 ? 32 : 48; + if (latency != need_latency) { + printk(KERN_INFO "%s: Changing PCI latency from %d to %d.\n", zr->name, latency, need_latency); + pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, need_latency); + } + + btwrite(0, ZR36057_SPGPPCR); + mdelay(1); + btwrite(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR); + mdelay(1); + + /* assert P_Reset */ + btwrite(0, ZR36057_JPC); + + /* set up GPIO direction - all output */ + btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR); + btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1); + + /* i2c */ + memcpy(&zr->i2c, &zoran_i2c_bus_template, + sizeof(struct i2c_bus)); + strcpy(zr->i2c.name, zr->name); + zr->i2c.data = zr; + printk(KERN_INFO "%s: Initializing i2c bus...\n", zr->name); + if (i2c_register_bus(&zr->i2c) < 0) { + /* put chip into reset */ + btwrite(0, ZR36057_SPGPPCR); + free_irq(zr->pci_dev->irq, zr); + iounmap(zr->zr36057_mem); + printk(KERN_ERR "%s: Can't initialize i2c bus\n", zr->name); + continue; + } + + if (zr->card != DC10 && zr->card != DC10plus + && zr->card != LML33 && zr->card != BUZ) { + /* unregister i2c_bus */ + i2c_unregister_bus((&zr->i2c)); + /* put chip into reset */ + btwrite(0, ZR36057_SPGPPCR); + free_irq(zr->pci_dev->irq, zr); + iounmap(zr->zr36057_mem); + printk(KERN_ERR "%s: Card not supported\n", + zr->name); + continue; + } + printk(KERN_INFO "%s card detected\n", zr->name); + if (zr->card == LML33) { + GPIO(zr, 2, 1); // Set Composite input/output + } + + /* reset JPEG codec */ + zr36060_sleep(zr, 1); + zr36060_reset(zr); + + /* video bus enabled */ + + /* display codec revision */ + if (zr36060_read_8(zr, 0x022) == 0x33) { + printk(KERN_INFO "%s: Zoran ZR36060 (rev %d)\n", + zr->name, zr36060_read_8(zr, 0x023)); + } else { + /* unregister i2c_bus */ + i2c_unregister_bus((&zr->i2c)); + /* put chip into reset */ + btwrite(0, ZR36057_SPGPPCR); + free_irq(zr->pci_dev->irq, zr); + iounmap(zr->zr36057_mem); + printk(KERN_ERR "%s: Zoran ZR36060 not found\n", + zr->name); + continue; + } + + zoran_num++; + } + if (zoran_num == 0) { + printk(KERN_INFO "No known MJPEG cards found.\n"); + } + return zoran_num; +} + +static void handle_chipset(void) +{ + if(pci_pci_problems & PCIPCI_FAIL) + printk(KERN_WARNING "Chipset may not support reliable PCI-PCI DMA.\n"); + + if(pci_pci_problems & PCIPCI_TRITON) + { + printk(KERN_WARNING "Enabling Triton support.\n"); + triton = 1; + } + + if(pci_pci_problems & PCIPCI_NATOMA) + { + printk(KERN_WARNING "Enabling Natoma workaround.\n"); + natoma = 1; + } +} + +static int init_dc10_cards(void) +{ + int i; + + memset(zoran, 0, sizeof(zoran)); + printk(KERN_INFO + "Zoran ZR36060 + ZR36057/67 MJPEG board driver version %d.%d\n", + MAJOR_VERSION, MINOR_VERSION); + + /* Look for cards */ + + if (find_zr36057() < 0) { + return -EIO; + } + if (zoran_num == 0) + return 0; //-ENXIO; + + printk(KERN_INFO "MJPEG: %d card(s) found\n", zoran_num); + + /* check the parameters we have been given, adjust if necessary */ + + if (v4l_nbufs < 0) + v4l_nbufs = 0; + if (v4l_nbufs > VIDEO_MAX_FRAME) + v4l_nbufs = VIDEO_MAX_FRAME; + /* The user specfies the in KB, we want them in byte (and page aligned) */ + v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024); + if (v4l_bufsize < 32768) + v4l_bufsize = 32768; + /* 2 MB is arbitrary but sufficient for the maximum possible images */ + if (v4l_bufsize > 2048 * 1024) + v4l_bufsize = 2048 * 1024; + + printk(KERN_INFO "MJPEG: using %d V4L buffers of size %d KB\n", + v4l_nbufs, v4l_bufsize >> 10); + + /* Use parameter for vidmem or try to find a video card */ + + if (vidmem) { + printk(KERN_INFO + "MJPEG: Using supplied video memory base address @ 0x%lx\n", + vidmem); + } + /* check if we have a Triton or Natome chipset */ + + handle_chipset(); + + /* take care of Natoma chipset and a revision 1 zr36057 */ + + for (i = 0; i < zoran_num; i++) { + if (natoma && zoran[i].revision <= 1) { + zoran[i].need_contiguous = 1; + printk(KERN_INFO + "%s: ZR36057/Natoma bug, max. buffer size is 128K\n", + zoran[i].name); + } else { + zoran[i].need_contiguous = 0; + } + } + + /* initialize the Buzs */ + + /* We have to know which ones must be released if an error occurs */ + for (i = 0; i < zoran_num; i++) + zoran[i].initialized = 0; + + for (i = 0; i < zoran_num; i++) { + if (zr36057_init(i) < 0) { + release_dc10(); + return -EIO; + } + zoran_proc_init(i); + } + + return 0; +} + +static void unload_dc10_cards(void) +{ + release_dc10(); +} + + +module_init(init_dc10_cards); +module_exit(unload_dc10_cards); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/Config.in linux.ac/drivers/message/fusion/Config.in --- linux.vanilla/drivers/message/fusion/Config.in Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/Config.in Tue Apr 3 23:12:52 2001 @@ -0,0 +1,39 @@ +mainmenu_option next_comment +comment 'Fusion MPT device support' + +dep_tristate "Fusion MPT (base + ScsiHost) drivers" CONFIG_FUSION $CONFIG_SCSI $CONFIG_BLK_DEV_SD + +if [ "$CONFIG_FUSION" = "y" -o "$CONFIG_FUSION" = "m" ]; then + + if [ "$CONFIG_BLK_DEV_SD" = "y" -a "$CONFIG_FUSION" = "y" ]; then + define_bool CONFIG_FUSION_BOOT y + comment "(ability to boot linux kernel from Fusion device is ENABLED!)" + else + define_bool CONFIG_FUSION_BOOT n + comment "(ability to boot linux kernel from Fusion device is DISABLED!)" + fi + + if [ "$CONFIG_MODULES" = "y" ]; then + # How can we force these options to module or nothing? + dep_tristate " Enhanced SCSI error reporting" CONFIG_FUSION_ISENSE $CONFIG_FUSION m + dep_tristate " Fusion MPT misc device (ioctl) driver" CONFIG_FUSION_CTL $CONFIG_FUSION m + fi + + dep_tristate " Fusion MPT LAN driver" CONFIG_FUSION_LAN $CONFIG_FUSION $CONFIG_NET + if [ "$CONFIG_FUSION_LAN" != "n" ]; then + define_bool CONFIG_NET_FC y + fi + +else + + define_bool CONFIG_FUSION_BOOT n + # These be define_tristate, but we leave them define_bool + # for backward compatibility with pre-linux-2.2.15 kernels. + # (Bugzilla:fibrebugs, #384) + define_bool CONFIG_FUSION_ISENSE n + define_bool CONFIG_FUSION_CTL n + define_bool CONFIG_FUSION_LAN n + +fi + +endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/Makefile linux.ac/drivers/message/fusion/Makefile --- linux.vanilla/drivers/message/fusion/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/Makefile Tue Apr 3 17:54:47 2001 @@ -0,0 +1,72 @@ +# +# Makefile for the LSI Logic Fusion MPT (Message Passing Technology) drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now inherited from the +# parent makefile. +# +# Note 3! If you want to turn on various debug defines for an extended period of +# time but don't want them lingering around in the Makefile when you pass it on +# to someone else, use the MPT_CFLAGS env variable (thanks Steve). -nromer + +#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-{ LSI_LOGIC + +# Architecture-specific... +# # intel +#EXTRA_CFLAGS += -g +# # sparc64 +#EXTRA_CFLAGS += -gstabs+ + +EXTRA_CFLAGS += -I. ${MPT_CFLAGS} + +# Fusion MPT drivers; recognized debug defines... +# MPT general: +#EXTRA_CFLAGS += -DDEBUG +#EXTRA_CFLAGS += -DMPT_DEBUG +#EXTRA_CFLAGS += -DMPT_DEBUG_MSG_FRAME +#EXTRA_CFLAGS += -DMPT_DEBUG_SPINLOCK +# driver/module specifics... +# For mptbase: +#CFLAGS_mptbase.o += -DMPT_DEBUG_HANDSHAKE +#CFLAGS_mptbase.o += -DMPT_DEBUG_IRQ +# For {mptscsih, mptctl}: +#CFLAGS_mptscsih.o += -DMPT_SCSI_USE_NEW_EH +#CFLAGS_mptscsih.o += -DMPT_SCSI_CACHE_AUTOSENSE +#CFLAGS_mptscsih.o += -DMPT_DEBUG_SG +#CFLAGS_mptctl.o += -DMPT_DEBUG_SG +# For mptlan: +#CFLAGS_mptlan.o += -DMPT_LAN_IO_DEBUG +# For isense: + +# EXP... +##mptscsih-objs := scsihost.o scsiherr.o + +#=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-} LSI_LOGIC + +O_TARGET := fusion.o + +export-objs := mptbase.o mptscsih.o mptlan.o mptctl.o isense.o + +# ? what's list-multi for? +#list-multi := fusion.o mptscsih.o + +obj-$(CONFIG_FUSION) += mptbase.o mptscsih.o +obj-$(CONFIG_FUSION_ISENSE) += isense.o +obj-$(CONFIG_FUSION_CTL) += mptctl.o +obj-$(CONFIG_FUSION_LAN) += mptlan.o + +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) + +include $(TOPDIR)/Rules.make + + +# EXP... +## Fusion MPT extra's... +##mptscsih.o: $(mptscsih-objs) +## $(LD) -r -o $@ $(mptscsih-objs) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/ascq_tbl.c linux.ac/drivers/message/fusion/ascq_tbl.c --- linux.vanilla/drivers/message/fusion/ascq_tbl.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/ascq_tbl.c Tue Apr 3 17:54:47 2001 @@ -0,0 +1,2416 @@ +#ifndef SCSI_ASCQ_TBL_C_INCLUDED +#define SCSI_ASCQ_TBL_C_INCLUDED + +/* AuToMaGiCaLlY generated from: "t10.org/asc-num.txt" + ******************************************************************************* + * File: ASC-NUM.TXT + * + * SCSI ASC/ASCQ Assignments + * Numeric Sorted Listing + * as of 5/18/00 + * + * D - DIRECT ACCESS DEVICE (SBC-2) device column key + * .T - SEQUENTIAL ACCESS DEVICE (SSC) ------------------- + * . L - PRINTER DEVICE (SSC) blank = reserved + * . P - PROCESSOR DEVICE (SPC) not blank = allowed + * . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC-2) + * . . R - CD DEVICE (MMC) + * . . S - SCANNER DEVICE (SCSI-2) + * . . .O - OPTICAL MEMORY DEVICE (SBC-2) + * . . . M - MEDIA CHANGER DEVICE (SMC) + * . . . C - COMMUNICATION DEVICE (SCSI-2) + * . . . .A - STORAGE ARRAY DEVICE (SCC) + * . . . . E - ENCLOSURE SERVICES DEVICE (SES) + * . . . . B - SIMPLIFIED DIRECT-ACCESS DEVICE (RBC) + * . . . . .K - OPTICAL CARD READER/WRITER DEVICE (OCRW) + * ASC/ASCQ DTLPWRSOMCAEBK Description + * ------- -------------- ---------------------------------------------------- + */ + +static char SenseDevTypes001[] = "DTLPWRSOMCAEBK"; +static char SenseDevTypes002[] = ".T............"; +static char SenseDevTypes003[] = ".T....S......."; +static char SenseDevTypes004[] = ".TL...S......."; +static char SenseDevTypes005[] = ".....R........"; +static char SenseDevTypes006[] = "DTL.WRSOM.AEBK"; +static char SenseDevTypes007[] = "D...W..O....BK"; +static char SenseDevTypes008[] = "D...WR.OM...BK"; +static char SenseDevTypes009[] = "DTL.W.SO....BK"; +static char SenseDevTypes010[] = "DTL..R.O....B."; +static char SenseDevTypes011[] = "DT..W..OMCA.BK"; +static char SenseDevTypes012[] = ".............."; +static char SenseDevTypes013[] = "DTL.WRSOMCAEBK"; +static char SenseDevTypes014[] = "DTL.WRSOM...BK"; +static char SenseDevTypes015[] = "DT...R.OM...BK"; +static char SenseDevTypes016[] = "DTLPWRSO.C...K"; +static char SenseDevTypes017[] = "DT..WR.O....B."; +static char SenseDevTypes018[] = "....WR.O.....K"; +static char SenseDevTypes019[] = "....WR.O......"; +static char SenseDevTypes020[] = ".T...RS......."; +static char SenseDevTypes021[] = ".............K"; +static char SenseDevTypes022[] = "DT..W..O....B."; +static char SenseDevTypes023[] = "DT..WRSO....BK"; +static char SenseDevTypes024[] = "DT..W.SO....BK"; +static char SenseDevTypes025[] = "....WR.O....B."; +static char SenseDevTypes026[] = "....W..O....B."; +static char SenseDevTypes027[] = "DT.....O....BK"; +static char SenseDevTypes028[] = "DTL.WRSO....BK"; +static char SenseDevTypes029[] = "DT..WR.O....BK"; +static char SenseDevTypes030[] = "DT..W..O....BK"; +static char SenseDevTypes031[] = "D...WR.O....BK"; +static char SenseDevTypes032[] = "D......O.....K"; +static char SenseDevTypes033[] = "D......O....BK"; +static char SenseDevTypes034[] = "DT..WR.OM...BK"; +static char SenseDevTypes035[] = "D............."; +static char SenseDevTypes036[] = "DTLPWRSOMCAE.K"; +static char SenseDevTypes037[] = "DTLPWRSOMCA.BK"; +static char SenseDevTypes038[] = ".T...R........"; +static char SenseDevTypes039[] = "DT..WR.OM...B."; +static char SenseDevTypes040[] = "DTL.WRSOMCAE.K"; +static char SenseDevTypes041[] = "DTLPWRSOMCAE.."; +static char SenseDevTypes042[] = "......S......."; +static char SenseDevTypes043[] = "............B."; +static char SenseDevTypes044[] = "DTLPWRSO.CA..K"; +static char SenseDevTypes045[] = "DT...R.......K"; +static char SenseDevTypes046[] = "D.L..R.O....B."; +static char SenseDevTypes047[] = "..L..........."; +static char SenseDevTypes048[] = ".TL..........."; +static char SenseDevTypes049[] = "DTLPWRSOMC..BK"; +static char SenseDevTypes050[] = "DT..WR.OMCAEBK"; +static char SenseDevTypes051[] = "DT..WR.OMCAEB."; +static char SenseDevTypes052[] = ".T...R.O......"; +static char SenseDevTypes053[] = "...P.........."; +static char SenseDevTypes054[] = "DTLPWRSOM.AE.K"; +static char SenseDevTypes055[] = "DTLPWRSOM.AE.."; +static char SenseDevTypes056[] = ".......O......"; +static char SenseDevTypes057[] = "DTLPWRSOM...BK"; +static char SenseDevTypes058[] = "DT..WR.O..A.BK"; +static char SenseDevTypes059[] = "DTLPWRSOM....K"; +static char SenseDevTypes060[] = "D......O......"; +static char SenseDevTypes061[] = ".....R......B."; +static char SenseDevTypes062[] = "D...........B."; +static char SenseDevTypes063[] = "............BK"; +static char SenseDevTypes064[] = "..........A..."; + +static ASCQ_Table_t ASCQ_Table[] = { + { + 0x00, 0x00, + SenseDevTypes001, + "NO ADDITIONAL SENSE INFORMATION" + }, + { + 0x00, 0x01, + SenseDevTypes002, + "FILEMARK DETECTED" + }, + { + 0x00, 0x02, + SenseDevTypes003, + "END-OF-PARTITION/MEDIUM DETECTED" + }, + { + 0x00, 0x03, + SenseDevTypes002, + "SETMARK DETECTED" + }, + { + 0x00, 0x04, + SenseDevTypes003, + "BEGINNING-OF-PARTITION/MEDIUM DETECTED" + }, + { + 0x00, 0x05, + SenseDevTypes004, + "END-OF-DATA DETECTED" + }, + { + 0x00, 0x06, + SenseDevTypes001, + "I/O PROCESS TERMINATED" + }, + { + 0x00, 0x11, + SenseDevTypes005, + "AUDIO PLAY OPERATION IN PROGRESS" + }, + { + 0x00, 0x12, + SenseDevTypes005, + "AUDIO PLAY OPERATION PAUSED" + }, + { + 0x00, 0x13, + SenseDevTypes005, + "AUDIO PLAY OPERATION SUCCESSFULLY COMPLETED" + }, + { + 0x00, 0x14, + SenseDevTypes005, + "AUDIO PLAY OPERATION STOPPED DUE TO ERROR" + }, + { + 0x00, 0x15, + SenseDevTypes005, + "NO CURRENT AUDIO STATUS TO RETURN" + }, + { + 0x00, 0x16, + SenseDevTypes001, + "OPERATION IN PROGRESS" + }, + { + 0x00, 0x17, + SenseDevTypes006, + "CLEANING REQUESTED" + }, + { + 0x01, 0x00, + SenseDevTypes007, + "NO INDEX/SECTOR SIGNAL" + }, + { + 0x02, 0x00, + SenseDevTypes008, + "NO SEEK COMPLETE" + }, + { + 0x03, 0x00, + SenseDevTypes009, + "PERIPHERAL DEVICE WRITE FAULT" + }, + { + 0x03, 0x01, + SenseDevTypes002, + "NO WRITE CURRENT" + }, + { + 0x03, 0x02, + SenseDevTypes002, + "EXCESSIVE WRITE ERRORS" + }, + { + 0x04, 0x00, + SenseDevTypes001, + "LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE" + }, + { + 0x04, 0x01, + SenseDevTypes001, + "LOGICAL UNIT IS IN PROCESS OF BECOMING READY" + }, + { + 0x04, 0x02, + SenseDevTypes001, + "LOGICAL UNIT NOT READY, INITIALIZING CMD. REQUIRED" + }, + { + 0x04, 0x03, + SenseDevTypes001, + "LOGICAL UNIT NOT READY, MANUAL INTERVENTION REQUIRED" + }, + { + 0x04, 0x04, + SenseDevTypes010, + "LOGICAL UNIT NOT READY, FORMAT IN PROGRESS" + }, + { + 0x04, 0x05, + SenseDevTypes011, + "LOGICAL UNIT NOT READY, REBUILD IN PROGRESS" + }, + { + 0x04, 0x06, + SenseDevTypes011, + "LOGICAL UNIT NOT READY, RECALCULATION IN PROGRESS" + }, + { + 0x04, 0x07, + SenseDevTypes001, + "LOGICAL UNIT NOT READY, OPERATION IN PROGRESS" + }, + { + 0x04, 0x08, + SenseDevTypes005, + "LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS" + }, + { + 0x04, 0x09, + SenseDevTypes001, + "LOGICAL UNIT NOT READY, SELF-TEST IN PROGRESS" + }, + { + 0x04, 0x10, + SenseDevTypes012, + "auxiliary memory code 2 (99-148) [proposed]" + }, + { + 0x05, 0x00, + SenseDevTypes013, + "LOGICAL UNIT DOES NOT RESPOND TO SELECTION" + }, + { + 0x06, 0x00, + SenseDevTypes008, + "NO REFERENCE POSITION FOUND" + }, + { + 0x07, 0x00, + SenseDevTypes014, + "MULTIPLE PERIPHERAL DEVICES SELECTED" + }, + { + 0x08, 0x00, + SenseDevTypes013, + "LOGICAL UNIT COMMUNICATION FAILURE" + }, + { + 0x08, 0x01, + SenseDevTypes013, + "LOGICAL UNIT COMMUNICATION TIME-OUT" + }, + { + 0x08, 0x02, + SenseDevTypes013, + "LOGICAL UNIT COMMUNICATION PARITY ERROR" + }, + { + 0x08, 0x03, + SenseDevTypes015, + "LOGICAL UNIT COMMUNICATION CRC ERROR (ULTRA-DMA/32)" + }, + { + 0x08, 0x04, + SenseDevTypes016, + "UNREACHABLE COPY TARGET" + }, + { + 0x09, 0x00, + SenseDevTypes017, + "TRACK FOLLOWING ERROR" + }, + { + 0x09, 0x01, + SenseDevTypes018, + "TRACKING SERVO FAILURE" + }, + { + 0x09, 0x02, + SenseDevTypes018, + "FOCUS SERVO FAILURE" + }, + { + 0x09, 0x03, + SenseDevTypes019, + "SPINDLE SERVO FAILURE" + }, + { + 0x09, 0x04, + SenseDevTypes017, + "HEAD SELECT FAULT" + }, + { + 0x0A, 0x00, + SenseDevTypes001, + "ERROR LOG OVERFLOW" + }, + { + 0x0B, 0x00, + SenseDevTypes001, + "WARNING" + }, + { + 0x0B, 0x01, + SenseDevTypes001, + "WARNING - SPECIFIED TEMPERATURE EXCEEDED" + }, + { + 0x0B, 0x02, + SenseDevTypes001, + "WARNING - ENCLOSURE DEGRADED" + }, + { + 0x0C, 0x00, + SenseDevTypes020, + "WRITE ERROR" + }, + { + 0x0C, 0x01, + SenseDevTypes021, + "WRITE ERROR - RECOVERED WITH AUTO REALLOCATION" + }, + { + 0x0C, 0x02, + SenseDevTypes007, + "WRITE ERROR - AUTO REALLOCATION FAILED" + }, + { + 0x0C, 0x03, + SenseDevTypes007, + "WRITE ERROR - RECOMMEND REASSIGNMENT" + }, + { + 0x0C, 0x04, + SenseDevTypes022, + "COMPRESSION CHECK MISCOMPARE ERROR" + }, + { + 0x0C, 0x05, + SenseDevTypes022, + "DATA EXPANSION OCCURRED DURING COMPRESSION" + }, + { + 0x0C, 0x06, + SenseDevTypes022, + "BLOCK NOT COMPRESSIBLE" + }, + { + 0x0C, 0x07, + SenseDevTypes005, + "WRITE ERROR - RECOVERY NEEDED" + }, + { + 0x0C, 0x08, + SenseDevTypes005, + "WRITE ERROR - RECOVERY FAILED" + }, + { + 0x0C, 0x09, + SenseDevTypes005, + "WRITE ERROR - LOSS OF STREAMING" + }, + { + 0x0C, 0x0A, + SenseDevTypes005, + "WRITE ERROR - PADDING BLOCKS ADDED" + }, + { + 0x0C, 0x0B, + SenseDevTypes012, + "auxiliary memory code 4 (99-148) [proposed]" + }, + { + 0x10, 0x00, + SenseDevTypes007, + "ID CRC OR ECC ERROR" + }, + { + 0x11, 0x00, + SenseDevTypes023, + "UNRECOVERED READ ERROR" + }, + { + 0x11, 0x01, + SenseDevTypes023, + "READ RETRIES EXHAUSTED" + }, + { + 0x11, 0x02, + SenseDevTypes023, + "ERROR TOO LONG TO CORRECT" + }, + { + 0x11, 0x03, + SenseDevTypes024, + "MULTIPLE READ ERRORS" + }, + { + 0x11, 0x04, + SenseDevTypes007, + "UNRECOVERED READ ERROR - AUTO REALLOCATE FAILED" + }, + { + 0x11, 0x05, + SenseDevTypes025, + "L-EC UNCORRECTABLE ERROR" + }, + { + 0x11, 0x06, + SenseDevTypes025, + "CIRC UNRECOVERED ERROR" + }, + { + 0x11, 0x07, + SenseDevTypes026, + "DATA RE-SYNCHRONIZATION ERROR" + }, + { + 0x11, 0x08, + SenseDevTypes002, + "INCOMPLETE BLOCK READ" + }, + { + 0x11, 0x09, + SenseDevTypes002, + "NO GAP FOUND" + }, + { + 0x11, 0x0A, + SenseDevTypes027, + "MISCORRECTED ERROR" + }, + { + 0x11, 0x0B, + SenseDevTypes007, + "UNRECOVERED READ ERROR - RECOMMEND REASSIGNMENT" + }, + { + 0x11, 0x0C, + SenseDevTypes007, + "UNRECOVERED READ ERROR - RECOMMEND REWRITE THE DATA" + }, + { + 0x11, 0x0D, + SenseDevTypes017, + "DE-COMPRESSION CRC ERROR" + }, + { + 0x11, 0x0E, + SenseDevTypes017, + "CANNOT DECOMPRESS USING DECLARED ALGORITHM" + }, + { + 0x11, 0x0F, + SenseDevTypes005, + "ERROR READING UPC/EAN NUMBER" + }, + { + 0x11, 0x10, + SenseDevTypes005, + "ERROR READING ISRC NUMBER" + }, + { + 0x11, 0x11, + SenseDevTypes005, + "READ ERROR - LOSS OF STREAMING" + }, + { + 0x11, 0x12, + SenseDevTypes012, + "auxiliary memory code 3 (99-148) [proposed]" + }, + { + 0x12, 0x00, + SenseDevTypes007, + "ADDRESS MARK NOT FOUND FOR ID FIELD" + }, + { + 0x13, 0x00, + SenseDevTypes007, + "ADDRESS MARK NOT FOUND FOR DATA FIELD" + }, + { + 0x14, 0x00, + SenseDevTypes028, + "RECORDED ENTITY NOT FOUND" + }, + { + 0x14, 0x01, + SenseDevTypes029, + "RECORD NOT FOUND" + }, + { + 0x14, 0x02, + SenseDevTypes002, + "FILEMARK OR SETMARK NOT FOUND" + }, + { + 0x14, 0x03, + SenseDevTypes002, + "END-OF-DATA NOT FOUND" + }, + { + 0x14, 0x04, + SenseDevTypes002, + "BLOCK SEQUENCE ERROR" + }, + { + 0x14, 0x05, + SenseDevTypes030, + "RECORD NOT FOUND - RECOMMEND REASSIGNMENT" + }, + { + 0x14, 0x06, + SenseDevTypes030, + "RECORD NOT FOUND - DATA AUTO-REALLOCATED" + }, + { + 0x15, 0x00, + SenseDevTypes014, + "RANDOM POSITIONING ERROR" + }, + { + 0x15, 0x01, + SenseDevTypes014, + "MECHANICAL POSITIONING ERROR" + }, + { + 0x15, 0x02, + SenseDevTypes029, + "POSITIONING ERROR DETECTED BY READ OF MEDIUM" + }, + { + 0x16, 0x00, + SenseDevTypes007, + "DATA SYNCHRONIZATION MARK ERROR" + }, + { + 0x16, 0x01, + SenseDevTypes007, + "DATA SYNC ERROR - DATA REWRITTEN" + }, + { + 0x16, 0x02, + SenseDevTypes007, + "DATA SYNC ERROR - RECOMMEND REWRITE" + }, + { + 0x16, 0x03, + SenseDevTypes007, + "DATA SYNC ERROR - DATA AUTO-REALLOCATED" + }, + { + 0x16, 0x04, + SenseDevTypes007, + "DATA SYNC ERROR - RECOMMEND REASSIGNMENT" + }, + { + 0x17, 0x00, + SenseDevTypes023, + "RECOVERED DATA WITH NO ERROR CORRECTION APPLIED" + }, + { + 0x17, 0x01, + SenseDevTypes023, + "RECOVERED DATA WITH RETRIES" + }, + { + 0x17, 0x02, + SenseDevTypes029, + "RECOVERED DATA WITH POSITIVE HEAD OFFSET" + }, + { + 0x17, 0x03, + SenseDevTypes029, + "RECOVERED DATA WITH NEGATIVE HEAD OFFSET" + }, + { + 0x17, 0x04, + SenseDevTypes025, + "RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED" + }, + { + 0x17, 0x05, + SenseDevTypes031, + "RECOVERED DATA USING PREVIOUS SECTOR ID" + }, + { + 0x17, 0x06, + SenseDevTypes007, + "RECOVERED DATA WITHOUT ECC - DATA AUTO-REALLOCATED" + }, + { + 0x17, 0x07, + SenseDevTypes031, + "RECOVERED DATA WITHOUT ECC - RECOMMEND REASSIGNMENT" + }, + { + 0x17, 0x08, + SenseDevTypes031, + "RECOVERED DATA WITHOUT ECC - RECOMMEND REWRITE" + }, + { + 0x17, 0x09, + SenseDevTypes031, + "RECOVERED DATA WITHOUT ECC - DATA REWRITTEN" + }, + { + 0x18, 0x00, + SenseDevTypes029, + "RECOVERED DATA WITH ERROR CORRECTION APPLIED" + }, + { + 0x18, 0x01, + SenseDevTypes031, + "RECOVERED DATA WITH ERROR CORR. & RETRIES APPLIED" + }, + { + 0x18, 0x02, + SenseDevTypes031, + "RECOVERED DATA - DATA AUTO-REALLOCATED" + }, + { + 0x18, 0x03, + SenseDevTypes005, + "RECOVERED DATA WITH CIRC" + }, + { + 0x18, 0x04, + SenseDevTypes005, + "RECOVERED DATA WITH L-EC" + }, + { + 0x18, 0x05, + SenseDevTypes031, + "RECOVERED DATA - RECOMMEND REASSIGNMENT" + }, + { + 0x18, 0x06, + SenseDevTypes031, + "RECOVERED DATA - RECOMMEND REWRITE" + }, + { + 0x18, 0x07, + SenseDevTypes007, + "RECOVERED DATA WITH ECC - DATA REWRITTEN" + }, + { + 0x19, 0x00, + SenseDevTypes032, + "DEFECT LIST ERROR" + }, + { + 0x19, 0x01, + SenseDevTypes032, + "DEFECT LIST NOT AVAILABLE" + }, + { + 0x19, 0x02, + SenseDevTypes032, + "DEFECT LIST ERROR IN PRIMARY LIST" + }, + { + 0x19, 0x03, + SenseDevTypes032, + "DEFECT LIST ERROR IN GROWN LIST" + }, + { + 0x1A, 0x00, + SenseDevTypes001, + "PARAMETER LIST LENGTH ERROR" + }, + { + 0x1B, 0x00, + SenseDevTypes001, + "SYNCHRONOUS DATA TRANSFER ERROR" + }, + { + 0x1C, 0x00, + SenseDevTypes033, + "DEFECT LIST NOT FOUND" + }, + { + 0x1C, 0x01, + SenseDevTypes033, + "PRIMARY DEFECT LIST NOT FOUND" + }, + { + 0x1C, 0x02, + SenseDevTypes033, + "GROWN DEFECT LIST NOT FOUND" + }, + { + 0x1D, 0x00, + SenseDevTypes029, + "MISCOMPARE DURING VERIFY OPERATION" + }, + { + 0x1E, 0x00, + SenseDevTypes007, + "RECOVERED ID WITH ECC CORRECTION" + }, + { + 0x1F, 0x00, + SenseDevTypes032, + "PARTIAL DEFECT LIST TRANSFER" + }, + { + 0x20, 0x00, + SenseDevTypes001, + "INVALID COMMAND OPERATION CODE" + }, + { + 0x20, 0x01, + SenseDevTypes012, + "access controls code 1 (99-314) [proposed]" + }, + { + 0x20, 0x02, + SenseDevTypes012, + "access controls code 2 (99-314) [proposed]" + }, + { + 0x20, 0x03, + SenseDevTypes012, + "access controls code 3 (99-314) [proposed]" + }, + { + 0x21, 0x00, + SenseDevTypes034, + "LOGICAL BLOCK ADDRESS OUT OF RANGE" + }, + { + 0x21, 0x01, + SenseDevTypes034, + "INVALID ELEMENT ADDRESS" + }, + { + 0x22, 0x00, + SenseDevTypes035, + "ILLEGAL FUNCTION (USE 20 00, 24 00, OR 26 00)" + }, + { + 0x24, 0x00, + SenseDevTypes001, + "INVALID FIELD IN CDB" + }, + { + 0x24, 0x01, + SenseDevTypes001, + "CDB DECRYPTION ERROR" + }, + { + 0x25, 0x00, + SenseDevTypes001, + "LOGICAL UNIT NOT SUPPORTED" + }, + { + 0x26, 0x00, + SenseDevTypes001, + "INVALID FIELD IN PARAMETER LIST" + }, + { + 0x26, 0x01, + SenseDevTypes001, + "PARAMETER NOT SUPPORTED" + }, + { + 0x26, 0x02, + SenseDevTypes001, + "PARAMETER VALUE INVALID" + }, + { + 0x26, 0x03, + SenseDevTypes036, + "THRESHOLD PARAMETERS NOT SUPPORTED" + }, + { + 0x26, 0x04, + SenseDevTypes001, + "INVALID RELEASE OF PERSISTENT RESERVATION" + }, + { + 0x26, 0x05, + SenseDevTypes037, + "DATA DECRYPTION ERROR" + }, + { + 0x26, 0x06, + SenseDevTypes016, + "TOO MANY TARGET DESCRIPTORS" + }, + { + 0x26, 0x07, + SenseDevTypes016, + "UNSUPPORTED TARGET DESCRIPTOR TYPE CODE" + }, + { + 0x26, 0x08, + SenseDevTypes016, + "TOO MANY SEGMENT DESCRIPTORS" + }, + { + 0x26, 0x09, + SenseDevTypes016, + "UNSUPPORTED SEGMENT DESCRIPTOR TYPE CODE" + }, + { + 0x26, 0x0A, + SenseDevTypes016, + "UNEXPECTED INEXACT SEGMENT" + }, + { + 0x26, 0x0B, + SenseDevTypes016, + "INLINE DATA LENGTH EXCEEDED" + }, + { + 0x26, 0x0C, + SenseDevTypes016, + "INVALID OPERATION FOR COPY SOURCE OR DESTINATION" + }, + { + 0x26, 0x0D, + SenseDevTypes016, + "COPY SEGMENT GRANULARITY VIOLATION" + }, + { + 0x27, 0x00, + SenseDevTypes029, + "WRITE PROTECTED" + }, + { + 0x27, 0x01, + SenseDevTypes029, + "HARDWARE WRITE PROTECTED" + }, + { + 0x27, 0x02, + SenseDevTypes029, + "LOGICAL UNIT SOFTWARE WRITE PROTECTED" + }, + { + 0x27, 0x03, + SenseDevTypes038, + "ASSOCIATED WRITE PROTECT" + }, + { + 0x27, 0x04, + SenseDevTypes038, + "PERSISTENT WRITE PROTECT" + }, + { + 0x27, 0x05, + SenseDevTypes038, + "PERMANENT WRITE PROTECT" + }, + { + 0x28, 0x00, + SenseDevTypes001, + "NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED" + }, + { + 0x28, 0x01, + SenseDevTypes039, + "IMPORT OR EXPORT ELEMENT ACCESSED" + }, + { + 0x29, 0x00, + SenseDevTypes001, + "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED" + }, + { + 0x29, 0x01, + SenseDevTypes001, + "POWER ON OCCURRED" + }, + { + 0x29, 0x02, + SenseDevTypes001, + "SCSI BUS RESET OCCURRED" + }, + { + 0x29, 0x03, + SenseDevTypes001, + "BUS DEVICE RESET FUNCTION OCCURRED" + }, + { + 0x29, 0x04, + SenseDevTypes001, + "DEVICE INTERNAL RESET" + }, + { + 0x29, 0x05, + SenseDevTypes001, + "TRANSCEIVER MODE CHANGED TO SINGLE-ENDED" + }, + { + 0x29, 0x06, + SenseDevTypes001, + "TRANSCEIVER MODE CHANGED TO LVD" + }, + { + 0x2A, 0x00, + SenseDevTypes013, + "PARAMETERS CHANGED" + }, + { + 0x2A, 0x01, + SenseDevTypes013, + "MODE PARAMETERS CHANGED" + }, + { + 0x2A, 0x02, + SenseDevTypes040, + "LOG PARAMETERS CHANGED" + }, + { + 0x2A, 0x03, + SenseDevTypes036, + "RESERVATIONS PREEMPTED" + }, + { + 0x2A, 0x04, + SenseDevTypes041, + "RESERVATIONS RELEASED" + }, + { + 0x2A, 0x05, + SenseDevTypes041, + "REGISTRATIONS PREEMPTED" + }, + { + 0x2B, 0x00, + SenseDevTypes016, + "COPY CANNOT EXECUTE SINCE HOST CANNOT DISCONNECT" + }, + { + 0x2C, 0x00, + SenseDevTypes001, + "COMMAND SEQUENCE ERROR" + }, + { + 0x2C, 0x01, + SenseDevTypes042, + "TOO MANY WINDOWS SPECIFIED" + }, + { + 0x2C, 0x02, + SenseDevTypes042, + "INVALID COMBINATION OF WINDOWS SPECIFIED" + }, + { + 0x2C, 0x03, + SenseDevTypes005, + "CURRENT PROGRAM AREA IS NOT EMPTY" + }, + { + 0x2C, 0x04, + SenseDevTypes005, + "CURRENT PROGRAM AREA IS EMPTY" + }, + { + 0x2C, 0x05, + SenseDevTypes043, + "ILLEGAL POWER CONDITION REQUEST" + }, + { + 0x2D, 0x00, + SenseDevTypes002, + "OVERWRITE ERROR ON UPDATE IN PLACE" + }, + { + 0x2E, 0x00, + SenseDevTypes044, + "ERROR DETECTED BY THIRD PARTY TEMPORARY INITIATOR" + }, + { + 0x2E, 0x01, + SenseDevTypes044, + "THIRD PARTY DEVICE FAILURE" + }, + { + 0x2E, 0x02, + SenseDevTypes044, + "COPY TARGET DEVICE NOT REACHABLE" + }, + { + 0x2E, 0x03, + SenseDevTypes044, + "INCORRECT COPY TARGET DEVICE TYPE" + }, + { + 0x2E, 0x04, + SenseDevTypes044, + "COPY TARGET DEVICE DATA UNDERRUN" + }, + { + 0x2E, 0x05, + SenseDevTypes044, + "COPY TARGET DEVICE DATA OVERRUN" + }, + { + 0x2F, 0x00, + SenseDevTypes001, + "COMMANDS CLEARED BY ANOTHER INITIATOR" + }, + { + 0x30, 0x00, + SenseDevTypes034, + "INCOMPATIBLE MEDIUM INSTALLED" + }, + { + 0x30, 0x01, + SenseDevTypes029, + "CANNOT READ MEDIUM - UNKNOWN FORMAT" + }, + { + 0x30, 0x02, + SenseDevTypes029, + "CANNOT READ MEDIUM - INCOMPATIBLE FORMAT" + }, + { + 0x30, 0x03, + SenseDevTypes045, + "CLEANING CARTRIDGE INSTALLED" + }, + { + 0x30, 0x04, + SenseDevTypes029, + "CANNOT WRITE MEDIUM - UNKNOWN FORMAT" + }, + { + 0x30, 0x05, + SenseDevTypes029, + "CANNOT WRITE MEDIUM - INCOMPATIBLE FORMAT" + }, + { + 0x30, 0x06, + SenseDevTypes017, + "CANNOT FORMAT MEDIUM - INCOMPATIBLE MEDIUM" + }, + { + 0x30, 0x07, + SenseDevTypes006, + "CLEANING FAILURE" + }, + { + 0x30, 0x08, + SenseDevTypes005, + "CANNOT WRITE - APPLICATION CODE MISMATCH" + }, + { + 0x30, 0x09, + SenseDevTypes005, + "CURRENT SESSION NOT FIXATED FOR APPEND" + }, + { + 0x31, 0x00, + SenseDevTypes029, + "MEDIUM FORMAT CORRUPTED" + }, + { + 0x31, 0x01, + SenseDevTypes046, + "FORMAT COMMAND FAILED" + }, + { + 0x32, 0x00, + SenseDevTypes007, + "NO DEFECT SPARE LOCATION AVAILABLE" + }, + { + 0x32, 0x01, + SenseDevTypes007, + "DEFECT LIST UPDATE FAILURE" + }, + { + 0x33, 0x00, + SenseDevTypes002, + "TAPE LENGTH ERROR" + }, + { + 0x34, 0x00, + SenseDevTypes001, + "ENCLOSURE FAILURE" + }, + { + 0x35, 0x00, + SenseDevTypes001, + "ENCLOSURE SERVICES FAILURE" + }, + { + 0x35, 0x01, + SenseDevTypes001, + "UNSUPPORTED ENCLOSURE FUNCTION" + }, + { + 0x35, 0x02, + SenseDevTypes001, + "ENCLOSURE SERVICES UNAVAILABLE" + }, + { + 0x35, 0x03, + SenseDevTypes001, + "ENCLOSURE SERVICES TRANSFER FAILURE" + }, + { + 0x35, 0x04, + SenseDevTypes001, + "ENCLOSURE SERVICES TRANSFER REFUSED" + }, + { + 0x36, 0x00, + SenseDevTypes047, + "RIBBON, INK, OR TONER FAILURE" + }, + { + 0x37, 0x00, + SenseDevTypes013, + "ROUNDED PARAMETER" + }, + { + 0x38, 0x00, + SenseDevTypes043, + "EVENT STATUS NOTIFICATION" + }, + { + 0x38, 0x02, + SenseDevTypes043, + "ESN - POWER MANAGEMENT CLASS EVENT" + }, + { + 0x38, 0x04, + SenseDevTypes043, + "ESN - MEDIA CLASS EVENT" + }, + { + 0x38, 0x06, + SenseDevTypes043, + "ESN - DEVICE BUSY CLASS EVENT" + }, + { + 0x39, 0x00, + SenseDevTypes040, + "SAVING PARAMETERS NOT SUPPORTED" + }, + { + 0x3A, 0x00, + SenseDevTypes014, + "MEDIUM NOT PRESENT" + }, + { + 0x3A, 0x01, + SenseDevTypes034, + "MEDIUM NOT PRESENT - TRAY CLOSED" + }, + { + 0x3A, 0x02, + SenseDevTypes034, + "MEDIUM NOT PRESENT - TRAY OPEN" + }, + { + 0x3A, 0x03, + SenseDevTypes039, + "MEDIUM NOT PRESENT - LOADABLE" + }, + { + 0x3A, 0x04, + SenseDevTypes039, + "MEDIUM NOT PRESENT - MEDIUM AUXILIARY MEMORY ACCESSIBLE" + }, + { + 0x3B, 0x00, + SenseDevTypes048, + "SEQUENTIAL POSITIONING ERROR" + }, + { + 0x3B, 0x01, + SenseDevTypes002, + "TAPE POSITION ERROR AT BEGINNING-OF-MEDIUM" + }, + { + 0x3B, 0x02, + SenseDevTypes002, + "TAPE POSITION ERROR AT END-OF-MEDIUM" + }, + { + 0x3B, 0x03, + SenseDevTypes047, + "TAPE OR ELECTRONIC VERTICAL FORMS UNIT NOT READY" + }, + { + 0x3B, 0x04, + SenseDevTypes047, + "SLEW FAILURE" + }, + { + 0x3B, 0x05, + SenseDevTypes047, + "PAPER JAM" + }, + { + 0x3B, 0x06, + SenseDevTypes047, + "FAILED TO SENSE TOP-OF-FORM" + }, + { + 0x3B, 0x07, + SenseDevTypes047, + "FAILED TO SENSE BOTTOM-OF-FORM" + }, + { + 0x3B, 0x08, + SenseDevTypes002, + "REPOSITION ERROR" + }, + { + 0x3B, 0x09, + SenseDevTypes042, + "READ PAST END OF MEDIUM" + }, + { + 0x3B, 0x0A, + SenseDevTypes042, + "READ PAST BEGINNING OF MEDIUM" + }, + { + 0x3B, 0x0B, + SenseDevTypes042, + "POSITION PAST END OF MEDIUM" + }, + { + 0x3B, 0x0C, + SenseDevTypes003, + "POSITION PAST BEGINNING OF MEDIUM" + }, + { + 0x3B, 0x0D, + SenseDevTypes034, + "MEDIUM DESTINATION ELEMENT FULL" + }, + { + 0x3B, 0x0E, + SenseDevTypes034, + "MEDIUM SOURCE ELEMENT EMPTY" + }, + { + 0x3B, 0x0F, + SenseDevTypes005, + "END OF MEDIUM REACHED" + }, + { + 0x3B, 0x11, + SenseDevTypes034, + "MEDIUM MAGAZINE NOT ACCESSIBLE" + }, + { + 0x3B, 0x12, + SenseDevTypes034, + "MEDIUM MAGAZINE REMOVED" + }, + { + 0x3B, 0x13, + SenseDevTypes034, + "MEDIUM MAGAZINE INSERTED" + }, + { + 0x3B, 0x14, + SenseDevTypes034, + "MEDIUM MAGAZINE LOCKED" + }, + { + 0x3B, 0x15, + SenseDevTypes034, + "MEDIUM MAGAZINE UNLOCKED" + }, + { + 0x3B, 0x16, + SenseDevTypes005, + "MECHANICAL POSITIONING OR CHANGER ERROR" + }, + { + 0x3D, 0x00, + SenseDevTypes036, + "INVALID BITS IN IDENTIFY MESSAGE" + }, + { + 0x3E, 0x00, + SenseDevTypes001, + "LOGICAL UNIT HAS NOT SELF-CONFIGURED YET" + }, + { + 0x3E, 0x01, + SenseDevTypes001, + "LOGICAL UNIT FAILURE" + }, + { + 0x3E, 0x02, + SenseDevTypes001, + "TIMEOUT ON LOGICAL UNIT" + }, + { + 0x3E, 0x03, + SenseDevTypes001, + "LOGICAL UNIT FAILED SELF-TEST" + }, + { + 0x3E, 0x04, + SenseDevTypes001, + "LOGICAL UNIT UNABLE TO UPDATE SELF-TEST LOG" + }, + { + 0x3F, 0x00, + SenseDevTypes001, + "TARGET OPERATING CONDITIONS HAVE CHANGED" + }, + { + 0x3F, 0x01, + SenseDevTypes001, + "MICROCODE HAS BEEN CHANGED" + }, + { + 0x3F, 0x02, + SenseDevTypes049, + "CHANGED OPERATING DEFINITION" + }, + { + 0x3F, 0x03, + SenseDevTypes001, + "INQUIRY DATA HAS CHANGED" + }, + { + 0x3F, 0x04, + SenseDevTypes050, + "COMPONENT DEVICE ATTACHED" + }, + { + 0x3F, 0x05, + SenseDevTypes050, + "DEVICE IDENTIFIER CHANGED" + }, + { + 0x3F, 0x06, + SenseDevTypes051, + "REDUNDANCY GROUP CREATED OR MODIFIED" + }, + { + 0x3F, 0x07, + SenseDevTypes051, + "REDUNDANCY GROUP DELETED" + }, + { + 0x3F, 0x08, + SenseDevTypes051, + "SPARE CREATED OR MODIFIED" + }, + { + 0x3F, 0x09, + SenseDevTypes051, + "SPARE DELETED" + }, + { + 0x3F, 0x0A, + SenseDevTypes050, + "VOLUME SET CREATED OR MODIFIED" + }, + { + 0x3F, 0x0B, + SenseDevTypes050, + "VOLUME SET DELETED" + }, + { + 0x3F, 0x0C, + SenseDevTypes050, + "VOLUME SET DEASSIGNED" + }, + { + 0x3F, 0x0D, + SenseDevTypes050, + "VOLUME SET REASSIGNED" + }, + { + 0x3F, 0x0E, + SenseDevTypes041, + "REPORTED LUNS DATA HAS CHANGED" + }, + { + 0x3F, 0x0F, + SenseDevTypes001, + "ECHO BUFFER OVERWRITTEN" + }, + { + 0x3F, 0x10, + SenseDevTypes039, + "MEDIUM LOADABLE" + }, + { + 0x3F, 0x11, + SenseDevTypes039, + "MEDIUM AUXILIARY MEMORY ACCESSIBLE" + }, + { + 0x40, 0x00, + SenseDevTypes035, + "RAM FAILURE (SHOULD USE 40 NN)" + }, + { + 0x40, 0xFF, + SenseDevTypes001, + "DIAGNOSTIC FAILURE ON COMPONENT NN (80H-FFH)" + }, + { + 0x41, 0x00, + SenseDevTypes035, + "DATA PATH FAILURE (SHOULD USE 40 NN)" + }, + { + 0x42, 0x00, + SenseDevTypes035, + "POWER-ON OR SELF-TEST FAILURE (SHOULD USE 40 NN)" + }, + { + 0x43, 0x00, + SenseDevTypes001, + "MESSAGE ERROR" + }, + { + 0x44, 0x00, + SenseDevTypes001, + "INTERNAL TARGET FAILURE" + }, + { + 0x45, 0x00, + SenseDevTypes001, + "SELECT OR RESELECT FAILURE" + }, + { + 0x46, 0x00, + SenseDevTypes049, + "UNSUCCESSFUL SOFT RESET" + }, + { + 0x47, 0x00, + SenseDevTypes001, + "SCSI PARITY ERROR" + }, + { + 0x47, 0x01, + SenseDevTypes001, + "DATA PHASE CRC ERROR DETECTED" + }, + { + 0x47, 0x02, + SenseDevTypes001, + "SCSI PARITY ERROR DETECTED DURING ST DATA PHASE" + }, + { + 0x47, 0x03, + SenseDevTypes001, + "INFORMATION UNIT CRC ERROR DETECTED" + }, + { + 0x47, 0x04, + SenseDevTypes001, + "ASYNCHRONOUS INFORMATION PROTECTION ERROR DETECTED" + }, + { + 0x48, 0x00, + SenseDevTypes001, + "INITIATOR DETECTED ERROR MESSAGE RECEIVED" + }, + { + 0x49, 0x00, + SenseDevTypes001, + "INVALID MESSAGE ERROR" + }, + { + 0x4A, 0x00, + SenseDevTypes001, + "COMMAND PHASE ERROR" + }, + { + 0x4B, 0x00, + SenseDevTypes001, + "DATA PHASE ERROR" + }, + { + 0x4C, 0x00, + SenseDevTypes001, + "LOGICAL UNIT FAILED SELF-CONFIGURATION" + }, + { + 0x4D, 0xFF, + SenseDevTypes001, + "TAGGED OVERLAPPED COMMANDS (NN = QUEUE TAG)" + }, + { + 0x4E, 0x00, + SenseDevTypes001, + "OVERLAPPED COMMANDS ATTEMPTED" + }, + { + 0x50, 0x00, + SenseDevTypes002, + "WRITE APPEND ERROR" + }, + { + 0x50, 0x01, + SenseDevTypes002, + "WRITE APPEND POSITION ERROR" + }, + { + 0x50, 0x02, + SenseDevTypes002, + "POSITION ERROR RELATED TO TIMING" + }, + { + 0x51, 0x00, + SenseDevTypes052, + "ERASE FAILURE" + }, + { + 0x52, 0x00, + SenseDevTypes002, + "CARTRIDGE FAULT" + }, + { + 0x53, 0x00, + SenseDevTypes014, + "MEDIA LOAD OR EJECT FAILED" + }, + { + 0x53, 0x01, + SenseDevTypes002, + "UNLOAD TAPE FAILURE" + }, + { + 0x53, 0x02, + SenseDevTypes034, + "MEDIUM REMOVAL PREVENTED" + }, + { + 0x54, 0x00, + SenseDevTypes053, + "SCSI TO HOST SYSTEM INTERFACE FAILURE" + }, + { + 0x55, 0x00, + SenseDevTypes053, + "SYSTEM RESOURCE FAILURE" + }, + { + 0x55, 0x01, + SenseDevTypes033, + "SYSTEM BUFFER FULL" + }, + { + 0x55, 0x02, + SenseDevTypes054, + "INSUFFICIENT RESERVATION RESOURCES" + }, + { + 0x55, 0x03, + SenseDevTypes041, + "INSUFFICIENT RESOURCES" + }, + { + 0x55, 0x04, + SenseDevTypes055, + "INSUFFICIENT REGISTRATION RESOURCES" + }, + { + 0x55, 0x05, + SenseDevTypes012, + "access controls code 4 (99-314) [proposed]" + }, + { + 0x55, 0x06, + SenseDevTypes012, + "auxiliary memory code 1 (99-148) [proposed]" + }, + { + 0x57, 0x00, + SenseDevTypes005, + "UNABLE TO RECOVER TABLE-OF-CONTENTS" + }, + { + 0x58, 0x00, + SenseDevTypes056, + "GENERATION DOES NOT EXIST" + }, + { + 0x59, 0x00, + SenseDevTypes056, + "UPDATED BLOCK READ" + }, + { + 0x5A, 0x00, + SenseDevTypes057, + "OPERATOR REQUEST OR STATE CHANGE INPUT" + }, + { + 0x5A, 0x01, + SenseDevTypes034, + "OPERATOR MEDIUM REMOVAL REQUEST" + }, + { + 0x5A, 0x02, + SenseDevTypes058, + "OPERATOR SELECTED WRITE PROTECT" + }, + { + 0x5A, 0x03, + SenseDevTypes058, + "OPERATOR SELECTED WRITE PERMIT" + }, + { + 0x5B, 0x00, + SenseDevTypes059, + "LOG EXCEPTION" + }, + { + 0x5B, 0x01, + SenseDevTypes059, + "THRESHOLD CONDITION MET" + }, + { + 0x5B, 0x02, + SenseDevTypes059, + "LOG COUNTER AT MAXIMUM" + }, + { + 0x5B, 0x03, + SenseDevTypes059, + "LOG LIST CODES EXHAUSTED" + }, + { + 0x5C, 0x00, + SenseDevTypes060, + "RPL STATUS CHANGE" + }, + { + 0x5C, 0x01, + SenseDevTypes060, + "SPINDLES SYNCHRONIZED" + }, + { + 0x5C, 0x02, + SenseDevTypes060, + "SPINDLES NOT SYNCHRONIZED" + }, + { + 0x5D, 0x00, + SenseDevTypes001, + "FAILURE PREDICTION THRESHOLD EXCEEDED" + }, + { + 0x5D, 0x01, + SenseDevTypes061, + "MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED" + }, + { + 0x5D, 0x02, + SenseDevTypes005, + "LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED" + }, + { + 0x5D, 0x10, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" + }, + { + 0x5D, 0x11, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x12, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x13, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x14, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" + }, + { + 0x5D, 0x15, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH" + }, + { + 0x5D, 0x16, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH" + }, + { + 0x5D, 0x17, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS" + }, + { + 0x5D, 0x18, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE CONTROLLER DETECTED" + }, + { + 0x5D, 0x19, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE" + }, + { + 0x5D, 0x1A, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE" + }, + { + 0x5D, 0x1B, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT" + }, + { + 0x5D, 0x1C, + SenseDevTypes062, + "HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" + }, + { + 0x5D, 0x20, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" + }, + { + 0x5D, 0x21, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x22, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x23, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x24, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" + }, + { + 0x5D, 0x25, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH" + }, + { + 0x5D, 0x26, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH" + }, + { + 0x5D, 0x27, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS" + }, + { + 0x5D, 0x28, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED" + }, + { + 0x5D, 0x29, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE" + }, + { + 0x5D, 0x2A, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE" + }, + { + 0x5D, 0x2B, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT" + }, + { + 0x5D, 0x2C, + SenseDevTypes062, + "CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" + }, + { + 0x5D, 0x30, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" + }, + { + 0x5D, 0x31, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x32, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x33, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x34, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" + }, + { + 0x5D, 0x35, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH" + }, + { + 0x5D, 0x36, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH" + }, + { + 0x5D, 0x37, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS" + }, + { + 0x5D, 0x38, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED" + }, + { + 0x5D, 0x39, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE" + }, + { + 0x5D, 0x3A, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE" + }, + { + 0x5D, 0x3B, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT" + }, + { + 0x5D, 0x3C, + SenseDevTypes062, + "DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" + }, + { + 0x5D, 0x40, + SenseDevTypes062, + "SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" + }, + { + 0x5D, 0x41, + SenseDevTypes062, + "SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x42, + SenseDevTypes062, + "SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x43, + SenseDevTypes062, + "SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x44, + SenseDevTypes062, + "SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" + }, + { + 0x5D, 0x45, + SenseDevTypes062, + "SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH" + }, + { + 0x5D, 0x46, + SenseDevTypes062, + "SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH" + }, + { + 0x5D, 0x47, + SenseDevTypes062, + "SERVO IMPENDING FAILURE CHANNEL PARAMETRICS" + }, + { + 0x5D, 0x48, + SenseDevTypes062, + "SERVO IMPENDING FAILURE CONTROLLER DETECTED" + }, + { + 0x5D, 0x49, + SenseDevTypes062, + "SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE" + }, + { + 0x5D, 0x4A, + SenseDevTypes062, + "SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE" + }, + { + 0x5D, 0x4B, + SenseDevTypes062, + "SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT" + }, + { + 0x5D, 0x4C, + SenseDevTypes062, + "SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" + }, + { + 0x5D, 0x50, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" + }, + { + 0x5D, 0x51, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x52, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x53, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x54, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" + }, + { + 0x5D, 0x55, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH" + }, + { + 0x5D, 0x56, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH" + }, + { + 0x5D, 0x57, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS" + }, + { + 0x5D, 0x58, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE CONTROLLER DETECTED" + }, + { + 0x5D, 0x59, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE" + }, + { + 0x5D, 0x5A, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE" + }, + { + 0x5D, 0x5B, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT" + }, + { + 0x5D, 0x5C, + SenseDevTypes062, + "SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" + }, + { + 0x5D, 0x60, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" + }, + { + 0x5D, 0x61, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x62, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x63, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" + }, + { + 0x5D, 0x64, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" + }, + { + 0x5D, 0x65, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH" + }, + { + 0x5D, 0x66, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH" + }, + { + 0x5D, 0x67, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS" + }, + { + 0x5D, 0x68, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED" + }, + { + 0x5D, 0x69, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE" + }, + { + 0x5D, 0x6A, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE" + }, + { + 0x5D, 0x6B, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT" + }, + { + 0x5D, 0x6C, + SenseDevTypes062, + "FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" + }, + { + 0x5D, 0xFF, + SenseDevTypes001, + "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)" + }, + { + 0x5E, 0x00, + SenseDevTypes044, + "LOW POWER CONDITION ON" + }, + { + 0x5E, 0x01, + SenseDevTypes044, + "IDLE CONDITION ACTIVATED BY TIMER" + }, + { + 0x5E, 0x02, + SenseDevTypes044, + "STANDBY CONDITION ACTIVATED BY TIMER" + }, + { + 0x5E, 0x03, + SenseDevTypes044, + "IDLE CONDITION ACTIVATED BY COMMAND" + }, + { + 0x5E, 0x04, + SenseDevTypes044, + "STANDBY CONDITION ACTIVATED BY COMMAND" + }, + { + 0x5E, 0x41, + SenseDevTypes043, + "POWER STATE CHANGE TO ACTIVE" + }, + { + 0x5E, 0x42, + SenseDevTypes043, + "POWER STATE CHANGE TO IDLE" + }, + { + 0x5E, 0x43, + SenseDevTypes043, + "POWER STATE CHANGE TO STANDBY" + }, + { + 0x5E, 0x45, + SenseDevTypes043, + "POWER STATE CHANGE TO SLEEP" + }, + { + 0x5E, 0x47, + SenseDevTypes063, + "POWER STATE CHANGE TO DEVICE CONTROL" + }, + { + 0x60, 0x00, + SenseDevTypes042, + "LAMP FAILURE" + }, + { + 0x61, 0x00, + SenseDevTypes042, + "VIDEO ACQUISITION ERROR" + }, + { + 0x61, 0x01, + SenseDevTypes042, + "UNABLE TO ACQUIRE VIDEO" + }, + { + 0x61, 0x02, + SenseDevTypes042, + "OUT OF FOCUS" + }, + { + 0x62, 0x00, + SenseDevTypes042, + "SCAN HEAD POSITIONING ERROR" + }, + { + 0x63, 0x00, + SenseDevTypes005, + "END OF USER AREA ENCOUNTERED ON THIS TRACK" + }, + { + 0x63, 0x01, + SenseDevTypes005, + "PACKET DOES NOT FIT IN AVAILABLE SPACE" + }, + { + 0x64, 0x00, + SenseDevTypes005, + "ILLEGAL MODE FOR THIS TRACK" + }, + { + 0x64, 0x01, + SenseDevTypes005, + "INVALID PACKET SIZE" + }, + { + 0x65, 0x00, + SenseDevTypes001, + "VOLTAGE FAULT" + }, + { + 0x66, 0x00, + SenseDevTypes042, + "AUTOMATIC DOCUMENT FEEDER COVER UP" + }, + { + 0x66, 0x01, + SenseDevTypes042, + "AUTOMATIC DOCUMENT FEEDER LIFT UP" + }, + { + 0x66, 0x02, + SenseDevTypes042, + "DOCUMENT JAM IN AUTOMATIC DOCUMENT FEEDER" + }, + { + 0x66, 0x03, + SenseDevTypes042, + "DOCUMENT MISS FEED AUTOMATIC IN DOCUMENT FEEDER" + }, + { + 0x67, 0x00, + SenseDevTypes064, + "CONFIGURATION FAILURE" + }, + { + 0x67, 0x01, + SenseDevTypes064, + "CONFIGURATION OF INCAPABLE LOGICAL UNITS FAILED" + }, + { + 0x67, 0x02, + SenseDevTypes064, + "ADD LOGICAL UNIT FAILED" + }, + { + 0x67, 0x03, + SenseDevTypes064, + "MODIFICATION OF LOGICAL UNIT FAILED" + }, + { + 0x67, 0x04, + SenseDevTypes064, + "EXCHANGE OF LOGICAL UNIT FAILED" + }, + { + 0x67, 0x05, + SenseDevTypes064, + "REMOVE OF LOGICAL UNIT FAILED" + }, + { + 0x67, 0x06, + SenseDevTypes064, + "ATTACHMENT OF LOGICAL UNIT FAILED" + }, + { + 0x67, 0x07, + SenseDevTypes064, + "CREATION OF LOGICAL UNIT FAILED" + }, + { + 0x67, 0x08, + SenseDevTypes064, + "ASSIGN FAILURE OCCURRED" + }, + { + 0x67, 0x09, + SenseDevTypes064, + "MULTIPLY ASSIGNED LOGICAL UNIT" + }, + { + 0x68, 0x00, + SenseDevTypes064, + "LOGICAL UNIT NOT CONFIGURED" + }, + { + 0x69, 0x00, + SenseDevTypes064, + "DATA LOSS ON LOGICAL UNIT" + }, + { + 0x69, 0x01, + SenseDevTypes064, + "MULTIPLE LOGICAL UNIT FAILURES" + }, + { + 0x69, 0x02, + SenseDevTypes064, + "PARITY/DATA MISMATCH" + }, + { + 0x6A, 0x00, + SenseDevTypes064, + "INFORMATIONAL, REFER TO LOG" + }, + { + 0x6B, 0x00, + SenseDevTypes064, + "STATE CHANGE HAS OCCURRED" + }, + { + 0x6B, 0x01, + SenseDevTypes064, + "REDUNDANCY LEVEL GOT BETTER" + }, + { + 0x6B, 0x02, + SenseDevTypes064, + "REDUNDANCY LEVEL GOT WORSE" + }, + { + 0x6C, 0x00, + SenseDevTypes064, + "REBUILD FAILURE OCCURRED" + }, + { + 0x6D, 0x00, + SenseDevTypes064, + "RECALCULATE FAILURE OCCURRED" + }, + { + 0x6E, 0x00, + SenseDevTypes064, + "COMMAND TO LOGICAL UNIT FAILED" + }, + { + 0x6F, 0x00, + SenseDevTypes005, + "COPY PROTECTION KEY EXCHANGE FAILURE - AUTHENTICATION FAILURE" + }, + { + 0x6F, 0x01, + SenseDevTypes005, + "COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT PRESENT" + }, + { + 0x6F, 0x02, + SenseDevTypes005, + "COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT ESTABLISHED" + }, + { + 0x6F, 0x03, + SenseDevTypes005, + "READ OF SCRAMBLED SECTOR WITHOUT AUTHENTICATION" + }, + { + 0x6F, 0x04, + SenseDevTypes005, + "MEDIA REGION CODE IS MISMATCHED TO LOGICAL UNIT REGION" + }, + { + 0x6F, 0x05, + SenseDevTypes005, + "DRIVE REGION MUST BE PERMANENT/REGION RESET COUNT ERROR" + }, + { + 0x70, 0xFF, + SenseDevTypes002, + "DECOMPRESSION EXCEPTION SHORT ALGORITHM ID OF NN" + }, + { + 0x71, 0x00, + SenseDevTypes002, + "DECOMPRESSION EXCEPTION LONG ALGORITHM ID" + }, + { + 0x72, 0x00, + SenseDevTypes005, + "SESSION FIXATION ERROR" + }, + { + 0x72, 0x01, + SenseDevTypes005, + "SESSION FIXATION ERROR WRITING LEAD-IN" + }, + { + 0x72, 0x02, + SenseDevTypes005, + "SESSION FIXATION ERROR WRITING LEAD-OUT" + }, + { + 0x72, 0x03, + SenseDevTypes005, + "SESSION FIXATION ERROR - INCOMPLETE TRACK IN SESSION" + }, + { + 0x72, 0x04, + SenseDevTypes005, + "EMPTY OR PARTIALLY WRITTEN RESERVED TRACK" + }, + { + 0x72, 0x05, + SenseDevTypes005, + "NO MORE TRACK RESERVATIONS ALLOWED" + }, + { + 0x73, 0x00, + SenseDevTypes005, + "CD CONTROL ERROR" + }, + { + 0x73, 0x01, + SenseDevTypes005, + "POWER CALIBRATION AREA ALMOST FULL" + }, + { + 0x73, 0x02, + SenseDevTypes005, + "POWER CALIBRATION AREA IS FULL" + }, + { + 0x73, 0x03, + SenseDevTypes005, + "POWER CALIBRATION AREA ERROR" + }, + { + 0x73, 0x04, + SenseDevTypes005, + "PROGRAM MEMORY AREA UPDATE FAILURE" + }, + { + 0x73, 0x05, + SenseDevTypes005, + "PROGRAM MEMORY AREA IS FULL" + }, + { + 0x73, 0x06, + SenseDevTypes005, + "RMA/PMA IS FULL" + }, +}; + +static int ASCQ_TableSize = 463; + + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/ascq_tbl.sh linux.ac/drivers/message/fusion/ascq_tbl.sh --- linux.vanilla/drivers/message/fusion/ascq_tbl.sh Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/ascq_tbl.sh Tue Apr 3 17:54:47 2001 @@ -0,0 +1,109 @@ +#!/bin/sh +# +# ascq_tbl.sh - Translate SCSI t10.org's "asc-num.txt" file of +# SCSI Additional Sense Code & Qualifiers (ASC/ASCQ's) +# into something useful in C, creating "ascq_tbl.c" file. +# +#*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*# + +PREF_INFILE="t10.org/asc-num.txt" # From SCSI t10.org +PREF_OUTFILE="ascq_tbl.c" + +#*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*# + +xlate_ascq() { + cat | awk ' + BEGIN { + DQ = "\042"; + OUTFILE = "'"${PREF_OUTFILE}"'"; + TRUE = 1; + FALSE = 0; + #debug = TRUE; + + # read and discard all lines up to and including the one that begins + # with the "magic token" of "------- -------------- ---"... + headers_gone = FALSE; + while (!headers_gone) { + if (getline <= 0) + exit 1; + header_line[++hdrs] = $0; + if (debug) + printf("header_line[%d] = :%s:\n", ++hdrs, $0); + if ($0 ~ /^------- -------------- ---/) { + headers_gone = TRUE; + } + } + outcount = 0; + } + + (NF > 1) { + ++outcount; + if (debug) + printf( "DBG: %s\n", $0 ); + ASC[outcount] = substr($0,1,2); + ASCQ[outcount] = substr($0,5,2); + devtypes = substr($0,10,14); + gsub(/ /, ".", devtypes); + DESCRIP[outcount] = substr($0,26); + + if (!(devtypes in DevTypesVoodoo)) { + DevTypesVoodoo[devtypes] = ++voodoo; + DevTypesIdx[voodoo] = devtypes; + } + DEVTYPES[outcount] = DevTypesVoodoo[devtypes]; + + # Handle 0xNN exception stuff... + if (ASCQ[outcount] == "NN" || ASCQ[outcount] == "nn") + ASCQ[outcount] = "FF"; + } + + END { + printf("#ifndef SCSI_ASCQ_TBL_C_INCLUDED\n") > OUTFILE; + printf("#define SCSI_ASCQ_TBL_C_INCLUDED\n") >> OUTFILE; + + printf("\n/* AuToMaGiCaLlY generated from: %s'"${FIN}"'%s\n", DQ, DQ) >> OUTFILE; + printf(" *******************************************************************************\n") >> OUTFILE; + for (i=1; i<=hdrs; i++) { + printf(" * %s\n", header_line[i]) >> OUTFILE; + } + printf(" */\n") >> OUTFILE; + + printf("\n") >> OUTFILE; + for (i=1; i<=voodoo; i++) { + printf("static char SenseDevTypes%03d[] = %s%s%s;\n", i, DQ, DevTypesIdx[i], DQ) >> OUTFILE; + } + + printf("\nstatic ASCQ_Table_t ASCQ_Table[] = {\n") >> OUTFILE; + for (i=1; i<=outcount; i++) { + printf(" {\n") >> OUTFILE; + printf(" 0x%s, 0x%s,\n", ASC[i], ASCQ[i]) >> OUTFILE; + printf(" SenseDevTypes%03d,\n", DEVTYPES[i]) >> OUTFILE; + printf(" %s%s%s\n", DQ, DESCRIP[i], DQ) >> OUTFILE; + printf(" },\n") >> OUTFILE; + } + printf( "};\n\n" ) >> OUTFILE; + + printf( "static int ASCQ_TableSize = %d;\n\n", outcount ) >> OUTFILE; + printf( "Total of %d ASC/ASCQ records generated\n", outcount ); + printf("\n#endif\n") >> OUTFILE; + close(OUTFILE); + }' + return +} + +#*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*# + +# main() +if [ $# -lt 1 ]; then + echo "INFO: No input filename supplied - using: $PREF_INFILE" >&2 + FIN=$PREF_INFILE +else + FIN="$1" + if [ "$FIN" != "$PREF_INFILE" ]; then + echo "INFO: Ok, I'll try chewing on '$FIN' for SCSI ASC/ASCQ combos..." >&2 + fi + shift +fi + +cat $FIN | xlate_ascq +exit 0 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/isense.c linux.ac/drivers/message/fusion/isense.c --- linux.vanilla/drivers/message/fusion/isense.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/isense.c Tue Apr 3 17:54:47 2001 @@ -0,0 +1,122 @@ +/* + * linux/drivers/message/fusion/isense.c + * Little linux driver / shim that interfaces with the Fusion MPT + * Linux base driver to provide english readable strings in SCSI + * Error Report logging output. This module implements SCSI-3 + * Opcode lookup and a sorted table of SCSI-3 ASC/ASCQ strings. + * + * Copyright (c) 1991-2001 Steven J. Ralston + * Written By: Steven J. Ralston + * (yes I wrote some of the orig. code back in 1991!) + * (mailto:Steve.Ralston@lsil.com) + * + * $Id: isense.c,v 1.28 2001/01/14 23:11:09 sralston Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + 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; version 2 of the License. + + 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. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include +#include +#include +#include +#include + +/* Hmmm, avoid undefined spinlock_t on lk-2.2.14-5.0 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#include +#endif + +#define MODULEAUTHOR "Steven J. Ralston" +#define COPYRIGHT "Copyright (c) 2000 " MODULEAUTHOR +#include "mptbase.h" + +#include "isense.h" + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Private data... + */ + +/* + * YIKES! I don't usually #include C source files, but.. + * The following #include's pulls in our needed ASCQ_Table[] array, + * ASCQ_TableSz integer, and ScsiOpcodeString[] array! + */ +#include "ascq_tbl.c" +#include "scsiops.c" + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#define my_NAME "SCSI-3 Opcodes & ASC/ASCQ Strings" +#define my_VERSION MPT_LINUX_VERSION_COMMON +#define MYNAM "isense" + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR(MODULEAUTHOR); +MODULE_DESCRIPTION(my_NAME); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +int __init isense_init(void) +{ + show_mptmod_ver(my_NAME, my_VERSION); + + /* + * Install our handler + */ + if (mpt_register_ascqops_strings(&ASCQ_Table[0], ASCQ_TableSize, ScsiOpcodeString) != 1) + { + printk(KERN_ERR MYNAM ": ERROR: Can't register with Fusion MPT base driver!\n"); + return -EBUSY; + } + printk(KERN_INFO MYNAM ": Registered SCSI-3 Opcodes & ASC/ASCQ Strings\n"); + return 0; +} + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static void isense_exit(void) +{ +#ifdef MODULE + mpt_deregister_ascqops_strings(); +#endif + printk(KERN_INFO MYNAM ": Deregistered SCSI-3 Opcodes & ASC/ASCQ Strings\n"); +} + +module_init(isense_init); +module_exit(isense_exit); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/isense.h linux.ac/drivers/message/fusion/isense.h --- linux.vanilla/drivers/message/fusion/isense.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/isense.h Tue May 8 19:21:31 2001 @@ -0,0 +1,95 @@ +#ifndef ISENSE_H_INCLUDED +#define ISENSE_H_INCLUDED +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#ifdef __KERNEL__ +#include /* needed for u8, etc. */ +#include /* needed for strcat */ +#include /* needed for sprintf */ +#else + #ifndef U_STUFF_DEFINED + #define U_STUFF_DEFINED + typedef unsigned char u8; + typedef unsigned short u16; + typedef unsigned int u32; + #endif +#endif + +#include "scsi3.h" /* needed for all things SCSI */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Defines and typedefs... + */ + +#ifdef __KERNEL__ +#define PrintF(x) printk x +#else +#define PrintF(x) printf x +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#define RETRY_STATUS ((int) 1) +#define PUT_STATUS ((int) 0) + +/* + * A generic structure to hold info about IO request that caused + * a Request Sense to be performed, and the resulting Sense Data. + */ +typedef struct IO_Info +{ + char *DevIDStr; /* String of chars which identifies the device. */ + u8 *cdbPtr; /* Pointer (Virtual/Logical addr) to CDB bytes of + IO request that caused ContAllegianceCond. */ + u8 *sensePtr; /* Pointer (Virtual/Logical addr) to Sense Data + returned by Request Sense operation. */ + u8 *dataPtr; /* Pointer (Virtual/Logical addr) to Data buffer + of IO request caused ContAllegianceCondition. */ + u8 *inqPtr; /* Pointer (Virtual/Logical addr) to Inquiry Data for + IO *Device* that caused ContAllegianceCondition. */ + u8 SCSIStatus; /* SCSI status byte of IO request that caused + Contingent Allegiance Condition. */ + u8 DoDisplay; /* Shall we display any messages? */ + u16 rsvd_align1; + u32 ComplCode; /* Four-byte OS-dependent completion code. */ + u32 NotifyL; /* Four-byte OS-dependent notification field. */ +} IO_Info_t; + +/* + * SCSI Additional Sense Code and Additional Sense Code Qualifier table. + */ +typedef struct ASCQ_Table +{ + u8 ASC; + u8 ASCQ; + char *DevTypes; + char *Description; +} ASCQ_Table_t; + +#if 0 +/* + * SCSI Opcodes table. + */ +typedef struct SCSI_OPS_Table +{ + u8 OpCode; + char *DevTypes; + char *ScsiCmndStr; +} SCSI_OPS_Table_t; +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Public entry point prototypes + */ + +/* in scsiherr.c, needed by mptscsih.c */ +extern int mpt_ScsiHost_ErrorReport(IO_Info_t *ioop); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/linux_compat.h linux.ac/drivers/message/fusion/linux_compat.h --- linux.vanilla/drivers/message/fusion/linux_compat.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/linux_compat.h Sat May 26 17:58:28 2001 @@ -0,0 +1,199 @@ +/* drivers/message/fusion/linux_compat.h */ + +#ifndef FUSION_LINUX_COMPAT_H +#define FUSION_LINUX_COMPAT_H +/*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include +#include +#include +#include + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) + typedef unsigned int dma_addr_t; +# endif +#else +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,42) + typedef unsigned int dma_addr_t; +# endif +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) +/*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/* This block snipped from lk-2.2.18/include/linux/init.h { */ +/* + * Used for initialization calls.. + */ +typedef int (*initcall_t)(void); +typedef void (*exitcall_t)(void); + +#define __init_call __attribute__ ((unused,__section__ (".initcall.init"))) +#define __exit_call __attribute__ ((unused,__section__ (".exitcall.exit"))) + +extern initcall_t __initcall_start, __initcall_end; + +#define __initcall(fn) \ + static initcall_t __initcall_##fn __init_call = fn +#define __exitcall(fn) \ + static exitcall_t __exitcall_##fn __exit_call = fn + +#ifdef MODULE +/* These macros create a dummy inline: gcc 2.9x does not count alias + as usage, hence the `unused function' warning when __init functions + are declared static. We use the dummy __*_module_inline functions + both to kill the warning and check the type of the init/cleanup + function. */ +typedef int (*__init_module_func_t)(void); +typedef void (*__cleanup_module_func_t)(void); +#define module_init(x) \ + int init_module(void) __attribute__((alias(#x))); \ + extern inline __init_module_func_t __init_module_inline(void) \ + { return x; } +#define module_exit(x) \ + void cleanup_module(void) __attribute__((alias(#x))); \ + extern inline __cleanup_module_func_t __cleanup_module_inline(void) \ + { return x; } + +#else +#define module_init(x) __initcall(x); +#define module_exit(x) __exitcall(x); +#endif +/* } block snipped from lk-2.2.18/include/linux/init.h */ + +/* Wait queues. */ +#define DECLARE_WAIT_QUEUE_HEAD(name) \ + struct wait_queue * (name) = NULL +#define DECLARE_WAITQUEUE(name, task) \ + struct wait_queue (name) = { (task), NULL } + +#if defined(__sparc__) && defined(__sparc_v9__) +/* The sparc64 ioremap implementation is wrong in 2.2.x, + * but fixing it would break all of the drivers which + * workaround it. Fixed in 2.3.x onward. -DaveM + */ +#define ARCH_IOREMAP(base) ((unsigned long) (base)) +#else +#define ARCH_IOREMAP(base) ioremap(base) +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#else /* LINUX_VERSION_CODE must be >= KERNEL_VERSION(2,2,18) */ + +/* No ioremap bugs in >2.3.x kernels. */ +#define ARCH_IOREMAP(base) ioremap(base) + +/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) */ + + +/* PCI/driver subsystem { */ +#ifndef pci_for_each_dev +#define pci_for_each_dev(dev) for((dev)=pci_devices; (dev)!=NULL; (dev)=(dev)->next) +#define pci_peek_next_dev(dev) ((dev)->next ? (dev)->next : NULL) +#define DEVICE_COUNT_RESOURCE 6 +#define PCI_BASEADDR_FLAGS(idx) base_address[idx] +#define PCI_BASEADDR_START(idx) base_address[idx] & ~0xFUL +/* + * We have to keep track of the original value using + * a temporary, and not by just sticking pdev->base_address[x] + * back. pdev->base_address[x] is an opaque cookie that can + * be used by the PCI implementation on a given Linux port + * for any purpose. -DaveM + */ +#define PCI_BASEADDR_SIZE(__pdev, __idx) \ +({ unsigned int size, tmp; \ + pci_read_config_dword(__pdev, PCI_BASE_ADDRESS_0 + (4*(__idx)), &tmp); \ + pci_write_config_dword(__pdev, PCI_BASE_ADDRESS_0 + (4*(__idx)), 0xffffffff); \ + pci_read_config_dword(__pdev, PCI_BASE_ADDRESS_0 + (4*(__idx)), &size); \ + pci_write_config_dword(__pdev, PCI_BASE_ADDRESS_0 + (4*(__idx)), tmp); \ + (4 - size); \ +}) +#else +#define pci_peek_next_dev(dev) ((dev) != pci_dev_g(&pci_devices) ? pci_dev_g((dev)->global_list.next) : NULL) +#define PCI_BASEADDR_FLAGS(idx) resource[idx].flags +#define PCI_BASEADDR_START(idx) resource[idx].start +#define PCI_BASEADDR_SIZE(dev,idx) (dev)->resource[idx].end - (dev)->resource[idx].start + 1 +#endif /* } ifndef pci_for_each_dev */ + + +/* procfs compat stuff... */ +#ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,28) +#define CREATE_PROCDIR_ENTRY(x,y) create_proc_entry(x, S_IFDIR, y) +/* This is a macro so we don't need to pull all the procfs + * headers into this file. -DaveM + */ +#define create_proc_read_entry(name, mode, base, __read_proc, __data) \ +({ struct proc_dir_entry *__res=create_proc_entry(name,mode,base); \ + if (__res) { \ + __res->read_proc=(__read_proc); \ + __res->data=(__data); \ + } \ + __res; \ +}) +#else +#define CREATE_PROCDIR_ENTRY(x,y) proc_mkdir(x, y) +#endif +#endif + +/* Compatability for the 2.3.x PCI DMA API. */ +#ifndef PCI_DMA_BIDIRECTIONAL +/*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#define PCI_DMA_BIDIRECTIONAL 0 +#define PCI_DMA_TODEVICE 1 +#define PCI_DMA_FROMDEVICE 2 +#define PCI_DMA_NONE 3 + +#ifdef __KERNEL__ +#include +/* Pure 2^n version of get_order */ +static __inline__ int __get_order(unsigned long size) +{ + int order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} +#endif + +#define pci_alloc_consistent(hwdev, size, dma_handle) \ +({ void *__ret = (void *)__get_free_pages(GFP_ATOMIC, __get_order(size)); \ + if (__ret != NULL) { \ + memset(__ret, 0, size); \ + *(dma_handle) = virt_to_bus(__ret); \ + } \ + __ret; \ +}) + +#define pci_free_consistent(hwdev, size, vaddr, dma_handle) \ + free_pages((unsigned long)vaddr, __get_order(size)) + +#define pci_map_single(hwdev, ptr, size, direction) \ + virt_to_bus(ptr); + +#define pci_unmap_single(hwdev, dma_addr, size, direction) \ + do { /* Nothing to do */ } while (0) + +#define pci_map_sg(hwdev, sg, nents, direction) (nents) +#define pci_unmap_sg(hwdev, sg, nents, direction) \ + do { /* Nothing to do */ } while(0) + +#define sg_dma_address(sg) (virt_to_bus((sg)->address)) +#define sg_dma_len(sg) ((sg)->length) + +/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#endif /* PCI_DMA_BIDIRECTIONAL */ + +/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#endif /* _LINUX_COMPAT_H */ + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/lsi/fc_log.h linux.ac/drivers/message/fusion/lsi/fc_log.h --- linux.vanilla/drivers/message/fusion/lsi/fc_log.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/lsi/fc_log.h Tue Apr 3 17:54:47 2001 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2000-2001 LSI Logic Corporation. All rights reserved. + * + * NAME: fc_log.h + * SUMMARY: MPI IocLogInfo definitions for the SYMFC9xx chips + * DESCRIPTION: Contains the enumerated list of values that may be returned + * in the IOCLogInfo field of a MPI Default Reply Message. + * + * CREATION DATE: 6/02/2000 + * ID: $Id: fc_log.h,v 4.2 2001/03/01 18:28:59 fibre Exp $ + */ + + +/* + * MpiIocLogInfo_t enum + * + * These 32 bit values are used in the IOCLogInfo field of the MPI reply + * messages. + * The value is 0xabcccccc where + * a = The type of log info as per the MPI spec. Since these codes are + * all for Fibre Channel this value will always be 2. + * b = Specifies a subclass of the firmware where + * 0 = FCP Initiator + * 1 = FCP Target + * 2 = LAN + * 3 = MPI Message Layer + * 4 = FC Link + * 5 = Context Manager + * 6 = Invalid Field Offset + * 7 = State Change Info + * all others are reserved for future use + * c = A specific value within the subclass. + * + * NOTE: Any new values should be added to the end of each subclass so that the + * codes remain consistent across firmware releases. + */ +typedef enum _MpiIocLogInfoFc +{ + MPI_IOCLOGINFO_FC_INIT_BASE = 0x20000000, + MPI_IOCLOGINFO_FC_INIT_ERROR_OUT_OF_ORDER_FRAME = 0x20000001, /* received an out of order frame - unsupported */ + MPI_IOCLOGINFO_FC_INIT_ERROR_BAD_START_OF_FRAME = 0x20000002, /* bad start of frame primative */ + MPI_IOCLOGINFO_FC_INIT_ERROR_BAD_END_OF_FRAME = 0x20000003, /* bad end of frame primative */ + MPI_IOCLOGINFO_FC_INIT_ERROR_OVER_RUN = 0x20000004, /* Receiver hardware detected overrun */ + MPI_IOCLOGINFO_FC_INIT_ERROR_RX_OTHER = 0x20000005, /* Other errors caught by IOC which require retries */ + MPI_IOCLOGINFO_FC_INIT_ERROR_SUBPROC_DEAD = 0x20000006, /* Main processor could not initialize sub-processor */ + + MPI_IOCLOGINFO_FC_TARGET_BASE = 0x21000000, + MPI_IOCLOGINFO_FC_TARGET_NO_PDISC = 0x21000001, /* not sent because we are waiting for a PDISC from the initiator */ + MPI_IOCLOGINFO_FC_TARGET_NO_LOGIN = 0x21000002, /* not sent because we are not logged in to the remote node */ + MPI_IOCLOGINFO_FC_TARGET_DOAR_KILLED_BY_LIP = 0x21000003, /* Data Out, Auto Response, not sent due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_DIAR_KILLED_BY_LIP = 0x21000004, /* Data In, Auto Response, not sent due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_DIAR_MISSING_DATA = 0x21000005, /* Data In, Auto Response, missing data frames */ + MPI_IOCLOGINFO_FC_TARGET_DONR_KILLED_BY_LIP = 0x21000006, /* Data Out, No Response, not sent due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_WRSP_KILLED_BY_LIP = 0x21000007, /* Auto-response after a write not sent due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_DINR_KILLED_BY_LIP = 0x21000008, /* Data In, No Response, not completed due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_DINR_MISSING_DATA = 0x21000009, /* Data In, No Response, missing data frames */ + MPI_IOCLOGINFO_FC_TARGET_MRSP_KILLED_BY_LIP = 0x2100000a, /* Manual Response not sent due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_NO_CLASS_3 = 0x2100000b, /* not sent because remote node does not support Class 3 */ + MPI_IOCLOGINFO_FC_TARGET_LOGIN_NOT_VALID = 0x2100000c, /* not sent because login to remote node not validated */ + MPI_IOCLOGINFO_FC_TARGET_FROM_OUTBOUND = 0x2100000e, /* cleared from the outbound after a logout */ + MPI_IOCLOGINFO_FC_TARGET_WAITING_FOR_DATA_IN = 0x2100000f, /* cleared waiting for data after a logout */ + + MPI_IOCLOGINFO_FC_LAN_BASE = 0x22000000, + MPI_IOCLOGINFO_FC_LAN_TRANS_SGL_MISSING = 0x22000001, /* Transaction Context Sgl Missing */ + MPI_IOCLOGINFO_FC_LAN_TRANS_WRONG_PLACE = 0x22000002, /* Transaction Context found before an EOB */ + MPI_IOCLOGINFO_FC_LAN_TRANS_RES_BITS_SET = 0x22000003, /* Transaction Context value has reserved bits set */ + MPI_IOCLOGINFO_FC_LAN_WRONG_SGL_FLAG = 0x22000004, /* Invalid SGL Flags */ + + MPI_IOCLOGINFO_FC_MSG_BASE = 0x23000000, + + MPI_IOCLOGINFO_FC_LINK_BASE = 0x24000000, + MPI_IOCLOGINFO_FC_LINK_LOOP_INIT_TIMEOUT = 0x24000001, /* Loop initialization timed out */ + MPI_IOCLOGINFO_FC_LINK_ALREADY_INITIALIZED = 0x24000002, /* Another system controller already initialized the loop */ + MPI_IOCLOGINFO_FC_LINK_LINK_NOT_ESTABLISHED = 0x24000003, /* Not synchronized to signal or still negotiating (possible cable problem) */ + + MPI_IOCLOGINFO_FC_CTX_BASE = 0x25000000, + + MPI_IOCLOGINFO_FC_INVALID_FIELD_BYTE_OFFSET = 0x26000000, /* The lower 24 bits give the byte offset of the field in the request message that is invalid. */ + MPI_IOCLOGINFO_FC_INVALID_FIELD_MAX_OFFSET = 0x26ffffff, + + MPI_IOCLOGINFO_FC_STATE_CHANGE = 0x27000000 /* The lower 24 bits give additional information concerning state change */ + +} MpiIocLogInfoFc_t; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/lsi/mpi.h linux.ac/drivers/message/fusion/lsi/mpi.h --- linux.vanilla/drivers/message/fusion/lsi/mpi.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/lsi/mpi.h Tue Apr 3 17:54:47 2001 @@ -0,0 +1,643 @@ +/* + * Copyright (c) 2000-2001 LSI Logic Corporation. + * + * + * Name: MPI.H + * Title: MPI Message independent structures and definitions + * Creation Date: July 27, 2000 + * + * MPI Version: 01.01.06 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH definition. + * 06-06-00 01.00.01 Update MPI_VERSION_MAJOR and MPI_VERSION_MINOR. + * 06-22-00 01.00.02 Added MPI_IOCSTATUS_LAN_ definitions. + * Removed LAN_SUSPEND function definition. + * Added MPI_MSGFLAGS_CONTINUATION_REPLY definition. + * 06-30-00 01.00.03 Added MPI_CONTEXT_REPLY_TYPE_LAN definition. + * Added MPI_GET/SET_CONTEXT_REPLY_TYPE macros. + * 07-27-00 01.00.04 Added MPI_FAULT_ definitions. + * Removed MPI_IOCSTATUS_MSG/DATA_XFER_ERROR definitions. + * Added MPI_IOCSTATUS_INTERNAL_ERROR definition. + * Added MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH. + * 11-02-00 01.01.01 Original release for post 1.0 work. + * 12-04-00 01.01.02 Added new function codes. + * 01-09-01 01.01.03 Added more definitions to the system interface section + * Added MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT. + * 01-25-01 01.01.04 Changed MPI_VERSION_MINOR from 0x00 to 0x01. + * 02-20-01 01.01.05 Started using MPI_POINTER. + * Fixed value for MPI_DIAG_RW_ENABLE. + * Added defines for MPI_DIAG_PREVENT_IOC_BOOT and + * MPI_DIAG_CLEAR_FLASH_BAD_SIG. + * Obsoleted MPI_IOCSTATUS_TARGET_FC_ defines. + * 02-27-01 01.01.06 Removed MPI_HOST_INDEX_REGISTER define. + * Added function codes for RAID. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_H +#define MPI_H + + +/***************************************************************************** +* +* M P I V e r s i o n D e f i n i t i o n s +* +*****************************************************************************/ + +#define MPI_VERSION_MAJOR (0x01) +#define MPI_VERSION_MINOR (0x01) +#define MPI_VERSION ((MPI_VERSION_MAJOR << 8) | MPI_VERSION_MINOR) + +/* Note: The major versions of 0xe0 through 0xff are reserved */ + +/***************************************************************************** +* +* I O C S t a t e D e f i n i t i o n s +* +*****************************************************************************/ + +#define MPI_IOC_STATE_RESET (0x00000000) +#define MPI_IOC_STATE_READY (0x10000000) +#define MPI_IOC_STATE_OPERATIONAL (0x20000000) +#define MPI_IOC_STATE_FAULT (0x40000000) + +#define MPI_IOC_STATE_MASK (0xF0000000) +#define MPI_IOC_STATE_SHIFT (28) + +/* Fault state codes (product independent range 0x8000-0xFFFF) */ + +#define MPI_FAULT_REQUEST_MESSAGE_PCI_PARITY_ERROR (0x8111) +#define MPI_FAULT_REQUEST_MESSAGE_PCI_BUS_FAULT (0x8112) +#define MPI_FAULT_REPLY_MESSAGE_PCI_PARITY_ERROR (0x8113) +#define MPI_FAULT_REPLY_MESSAGE_PCI_BUS_FAULT (0x8114) +#define MPI_FAULT_DATA_SEND_PCI_PARITY_ERROR (0x8115) +#define MPI_FAULT_DATA_SEND_PCI_BUS_FAULT (0x8116) +#define MPI_FAULT_DATA_RECEIVE_PCI_PARITY_ERROR (0x8117) +#define MPI_FAULT_DATA_RECEIVE_PCI_BUS_FAULT (0x8118) + + +/***************************************************************************** +* +* P C I S y s t e m I n t e r f a c e R e g i s t e r s +* +*****************************************************************************/ + +/* S y s t e m D o o r b e l l */ +#define MPI_DOORBELL_OFFSET (0x00000000) +#define MPI_DOORBELL_ACTIVE (0x08000000) +#define MPI_DOORBELL_ACTIVE_SHIFT (27) +#define MPI_DOORBELL_WHO_INIT_MASK (0x07000000) +#define MPI_DOORBELL_WHO_INIT_SHIFT (24) +#define MPI_DOORBELL_FUNCTION_MASK (0xFF000000) +#define MPI_DOORBELL_FUNCTION_SHIFT (24) +#define MPI_DOORBELL_ADD_DWORDS_MASK (0x00FF0000) +#define MPI_DOORBELL_ADD_DWORDS_SHIFT (16) +#define MPI_DOORBELL_DATA_MASK (0x0000FFFF) + + +#define MPI_WRITE_SEQUENCE_OFFSET (0x00000004) +#define MPI_WRSEQ_KEY_VALUE_MASK (0x0000000F) +#define MPI_WRSEQ_1ST_KEY_VALUE (0x04) +#define MPI_WRSEQ_2ND_KEY_VALUE (0x0B) +#define MPI_WRSEQ_3RD_KEY_VALUE (0x02) +#define MPI_WRSEQ_4TH_KEY_VALUE (0x07) +#define MPI_WRSEQ_5TH_KEY_VALUE (0x0D) + +#define MPI_DIAGNOSTIC_OFFSET (0x00000008) +#define MPI_DIAG_CLEAR_FLASH_BAD_SIG (0x00000400) +#define MPI_DIAG_PREVENT_IOC_BOOT (0x00000200) +#define MPI_DIAG_DRWE (0x00000080) +#define MPI_DIAG_FLASH_BAD_SIG (0x00000040) +#define MPI_DIAG_RESET_HISTORY (0x00000020) +#define MPI_DIAG_RW_ENABLE (0x00000010) +#define MPI_DIAG_RESET_ADAPTER (0x00000004) +#define MPI_DIAG_DISABLE_ARM (0x00000002) +#define MPI_DIAG_MEM_ENABLE (0x00000001) + +#define MPI_TEST_BASE_ADDRESS_OFFSET (0x0000000C) + +#define MPI_DIAG_RW_DATA_OFFSET (0x00000010) + +#define MPI_DIAG_RW_ADDRESS_OFFSET (0x00000014) + +#define MPI_HOST_INTERRUPT_STATUS_OFFSET (0x00000030) +#define MPI_HIS_IOP_DOORBELL_STATUS (0x80000000) +#define MPI_HIS_REPLY_MESSAGE_INTERRUPT (0x00000008) +#define MPI_HIS_DOORBELL_INTERRUPT (0x00000001) + +#define MPI_HOST_INTERRUPT_MASK_OFFSET (0x00000034) +#define MPI_HIM_RIM (0x00000008) +#define MPI_HIM_DIM (0x00000001) + +#define MPI_REQUEST_QUEUE_OFFSET (0x00000040) +#define MPI_REQUEST_POST_FIFO_OFFSET (0x00000040) + +#define MPI_REPLY_QUEUE_OFFSET (0x00000044) +#define MPI_REPLY_POST_FIFO_OFFSET (0x00000044) +#define MPI_REPLY_FREE_FIFO_OFFSET (0x00000044) + + + +/***************************************************************************** +* +* M e s s a g e F r a m e D e s c r i p t o r s +* +*****************************************************************************/ + +#define MPI_REQ_MF_DESCRIPTOR_NB_MASK (0x00000003) +#define MPI_REQ_MF_DESCRIPTOR_F_BIT (0x00000004) +#define MPI_REQ_MF_DESCRIPTOR_ADDRESS_MASK (0xFFFFFFF8) + +#define MPI_ADDRESS_REPLY_A_BIT (0x80000000) +#define MPI_ADDRESS_REPLY_ADDRESS_MASK (0x7FFFFFFF) + +#define MPI_CONTEXT_REPLY_A_BIT (0x80000000) +#define MPI_CONTEXT_REPLY_TYPE_MASK (0x60000000) +#define MPI_CONTEXT_REPLY_TYPE_SCSI_INIT (0x00) +#define MPI_CONTEXT_REPLY_TYPE_SCSI_TARGET (0x01) +#define MPI_CONTEXT_REPLY_TYPE_LAN (0x02) +#define MPI_CONTEXT_REPLY_TYPE_SHIFT (29) +#define MPI_CONTEXT_REPLY_CONTEXT_MASK (0x1FFFFFFF) + + +/****************************************************************************/ +/* Context Reply macros */ +/****************************************************************************/ + +#define MPI_GET_CONTEXT_REPLY_TYPE(x) (((x) & MPI_CONTEXT_REPLY_TYPE_MASK) \ + >> MPI_CONTEXT_REPLY_TYPE_SHIFT) + +#define MPI_SET_CONTEXT_REPLY_TYPE(x, typ) \ + ((x) = ((x) & ~MPI_CONTEXT_REPLY_TYPE_MASK) | \ + (((typ) << MPI_CONTEXT_REPLY_TYPE_SHIFT) & \ + MPI_CONTEXT_REPLY_TYPE_MASK)) + + +/***************************************************************************** +* +* M e s s a g e F u n c t i o n s +* 0x80 -> 0x8F reserved for private message use per product +* +* +*****************************************************************************/ + +#define MPI_FUNCTION_SCSI_IO_REQUEST (0x00) +#define MPI_FUNCTION_SCSI_TASK_MGMT (0x01) +#define MPI_FUNCTION_IOC_INIT (0x02) +#define MPI_FUNCTION_IOC_FACTS (0x03) +#define MPI_FUNCTION_CONFIG (0x04) +#define MPI_FUNCTION_PORT_FACTS (0x05) +#define MPI_FUNCTION_PORT_ENABLE (0x06) +#define MPI_FUNCTION_EVENT_NOTIFICATION (0x07) +#define MPI_FUNCTION_EVENT_ACK (0x08) +#define MPI_FUNCTION_FW_DOWNLOAD (0x09) +#define MPI_FUNCTION_TARGET_CMD_BUFFER_POST (0x0A) +#define MPI_FUNCTION_TARGET_ASSIST (0x0B) +#define MPI_FUNCTION_TARGET_STATUS_SEND (0x0C) +#define MPI_FUNCTION_TARGET_MODE_ABORT (0x0D) +#define MPI_FUNCTION_TARGET_FC_BUF_POST_LINK_SRVC (0x0E) /* obsolete name */ +#define MPI_FUNCTION_TARGET_FC_RSP_LINK_SRVC (0x0F) /* obsolete name */ +#define MPI_FUNCTION_TARGET_FC_EX_SEND_LINK_SRVC (0x10) /* obsolete name */ +#define MPI_FUNCTION_TARGET_FC_ABORT (0x11) /* obsolete name */ +#define MPI_FUNCTION_FC_LINK_SRVC_BUF_POST (0x0E) +#define MPI_FUNCTION_FC_LINK_SRVC_RSP (0x0F) +#define MPI_FUNCTION_FC_EX_LINK_SRVC_SEND (0x10) +#define MPI_FUNCTION_FC_ABORT (0x11) +#define MPI_FUNCTION_FW_UPLOAD (0x12) +#define MPI_FUNCTION_FC_COMMON_TRANSPORT_SEND (0x13) +#define MPI_FUNCTION_FC_PRIMITIVE_SEND (0x14) + +#define MPI_FUNCTION_RAID_VOLUME (0x15) +#define MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) + +#define MPI_FUNCTION_LAN_SEND (0x20) +#define MPI_FUNCTION_LAN_RECEIVE (0x21) +#define MPI_FUNCTION_LAN_RESET (0x22) + +#define MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40) +#define MPI_FUNCTION_IO_UNIT_RESET (0x41) +#define MPI_FUNCTION_HANDSHAKE (0x42) +#define MPI_FUNCTION_REPLY_FRAME_REMOVAL (0x43) + + + +/***************************************************************************** +* +* S c a t t e r G a t h e r E l e m e n t s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Simple element structures */ +/****************************************************************************/ + +typedef struct _SGE_SIMPLE32 +{ + U32 FlagsLength; + U32 Address; +} SGE_SIMPLE32, MPI_POINTER PTR_SGE_SIMPLE32, + SGESimple32_t, MPI_POINTER pSGESimple32_t; + +typedef struct _SGE_SIMPLE64 +{ + U32 FlagsLength; + U64 Address; +} SGE_SIMPLE64, MPI_POINTER PTR_SGE_SIMPLE64, + SGESimple64_t, MPI_POINTER pSGESimple64_t; + +typedef struct _SGE_SIMPLE_UNION +{ + U32 FlagsLength; + union + { + U32 Address32; + U64 Address64; + }u; +} SGESimpleUnion_t, MPI_POINTER pSGESimpleUnion_t, + SGE_SIMPLE_UNION, MPI_POINTER PTR_SGE_SIMPLE_UNION; + +/****************************************************************************/ +/* Chain element structures */ +/****************************************************************************/ + +typedef struct _SGE_CHAIN32 +{ + U16 Length; + U8 NextChainOffset; + U8 Flags; + U32 Address; +} SGE_CHAIN32, MPI_POINTER PTR_SGE_CHAIN32, + SGEChain32_t, MPI_POINTER pSGEChain32_t; + +typedef struct _SGE_CHAIN64 +{ + U16 Length; + U8 NextChainOffset; + U8 Flags; + U64 Address; +} SGE_CHAIN64, MPI_POINTER PTR_SGE_CHAIN64, + SGEChain64_t, MPI_POINTER pSGEChain64_t; + +typedef struct _SGE_CHAIN_UNION +{ + U16 Length; + U8 NextChainOffset; + U8 Flags; + union + { + U32 Address32; + U64 Address64; + }u; +} SGE_CHAIN_UNION, MPI_POINTER PTR_SGE_CHAIN_UNION, + SGEChainUnion_t, MPI_POINTER pSGEChainUnion_t; + +/****************************************************************************/ +/* Transaction Context element */ +/****************************************************************************/ + +typedef struct _SGE_TRANSACTION32 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[1]; + U32 TransactionDetails[1]; +} SGE_TRANSACTION32, MPI_POINTER PTR_SGE_TRANSACTION32, + SGETransaction32_t, MPI_POINTER pSGETransaction32_t; + +typedef struct _SGE_TRANSACTION64 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[2]; + U32 TransactionDetails[1]; +} SGE_TRANSACTION64, MPI_POINTER PTR_SGE_TRANSACTION64, + SGETransaction64_t, MPI_POINTER pSGETransaction64_t; + +typedef struct _SGE_TRANSACTION96 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[3]; + U32 TransactionDetails[1]; +} SGE_TRANSACTION96, MPI_POINTER PTR_SGE_TRANSACTION96, + SGETransaction96_t, MPI_POINTER pSGETransaction96_t; + +typedef struct _SGE_TRANSACTION128 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[4]; + U32 TransactionDetails[1]; +} SGE_TRANSACTION128, MPI_POINTER PTR_SGE_TRANSACTION128, + SGETransaction_t128, MPI_POINTER pSGETransaction_t128; + +typedef struct _SGE_TRANSACTION_UNION +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + union + { + U32 TransactionContext32[1]; + U32 TransactionContext64[2]; + U32 TransactionContext96[3]; + U32 TransactionContext128[4]; + }u; + U32 TransactionDetails[1]; +} SGE_TRANSACTION_UNION, MPI_POINTER PTR_SGE_TRANSACTION_UNION, + SGETransactionUnion_t, MPI_POINTER pSGETransactionUnion_t; + + +/****************************************************************************/ +/* SGE IO types union for IO SGL's */ +/****************************************************************************/ + +typedef struct _SGE_IO_UNION +{ + union + { + SGE_SIMPLE_UNION Simple; + SGE_CHAIN_UNION Chain; + } u; +} SGE_IO_UNION, MPI_POINTER PTR_SGE_IO_UNION, + SGEIOUnion_t, MPI_POINTER pSGEIOUnion_t; + +/****************************************************************************/ +/* SGE union for SGL's with Simple and Transaction elements */ +/****************************************************************************/ + +typedef struct _SGE_TRANS_SIMPLE_UNION +{ + union + { + SGE_SIMPLE_UNION Simple; + SGE_TRANSACTION_UNION Transaction; + } u; +} SGE_TRANS_SIMPLE_UNION, MPI_POINTER PTR_SGE_TRANS_SIMPLE_UNION, + SGETransSimpleUnion_t, MPI_POINTER pSGETransSimpleUnion_t; + +/****************************************************************************/ +/* All SGE types union */ +/****************************************************************************/ + +typedef struct _SGE_MPI_UNION +{ + union + { + SGE_SIMPLE_UNION Simple; + SGE_CHAIN_UNION Chain; + SGE_TRANSACTION_UNION Transaction; + } u; +} SGE_MPI_UNION, MPI_POINTER PTR_SGE_MPI_UNION, + MPI_SGE_UNION_t, MPI_POINTER pMPI_SGE_UNION_t, + SGEAllUnion_t, MPI_POINTER pSGEAllUnion_t; + + +/****************************************************************************/ +/* SGE field definition and masks */ +/****************************************************************************/ + +/* Flags field bit definitions */ + +#define MPI_SGE_FLAGS_LAST_ELEMENT (0x80) +#define MPI_SGE_FLAGS_END_OF_BUFFER (0x40) +#define MPI_SGE_FLAGS_ELEMENT_TYPE_MASK (0x30) +#define MPI_SGE_FLAGS_LOCAL_ADDRESS (0x08) +#define MPI_SGE_FLAGS_DIRECTION (0x04) +#define MPI_SGE_FLAGS_ADDRESS_SIZE (0x02) +#define MPI_SGE_FLAGS_END_OF_LIST (0x01) + +#define MPI_SGE_FLAGS_SHIFT (24) + +#define MPI_SGE_LENGTH_MASK (0x00FFFFFF) +#define MPI_SGE_CHAIN_LENGTH_MASK (0x0000FFFF) + +/* Element Type */ + +#define MPI_SGE_FLAGS_TRANSACTION_ELEMENT (0x00) +#define MPI_SGE_FLAGS_SIMPLE_ELEMENT (0x10) +#define MPI_SGE_FLAGS_CHAIN_ELEMENT (0x30) +#define MPI_SGE_FLAGS_ELEMENT_MASK (0x30) + +/* Address location */ + +#define MPI_SGE_FLAGS_SYSTEM_ADDRESS (0x00) + +/* Direction */ + +#define MPI_SGE_FLAGS_IOC_TO_HOST (0x00) +#define MPI_SGE_FLAGS_HOST_TO_IOC (0x04) + +/* Address Size */ + +#define MPI_SGE_FLAGS_32_BIT_ADDRESSING (0x00) +#define MPI_SGE_FLAGS_64_BIT_ADDRESSING (0x02) + +/* Context Size */ + +#define MPI_SGE_FLAGS_32_BIT_CONTEXT (0x00) +#define MPI_SGE_FLAGS_64_BIT_CONTEXT (0x02) +#define MPI_SGE_FLAGS_96_BIT_CONTEXT (0x04) +#define MPI_SGE_FLAGS_128_BIT_CONTEXT (0x06) + +#define MPI_SGE_CHAIN_OFFSET_MASK (0x00FF0000) +#define MPI_SGE_CHAIN_OFFSET_SHIFT (16) + + +/****************************************************************************/ +/* SGE operation Macros */ +/****************************************************************************/ + + /* SIMPLE FlagsLength manipulations... */ +#define MPI_SGE_SET_FLAGS(f) ((U32)(f) << MPI_SGE_FLAGS_SHIFT) +#define MPI_SGE_GET_FLAGS(fl) (((fl) & ~MPI_SGE_LENGTH_MASK) >> MPI_SGE_FLAGS_SHIFT) +#define MPI_SGE_LENGTH(fl) ((fl) & MPI_SGE_LENGTH_MASK) +#define MPI_SGE_CHAIN_LENGTH(fl) ((fl) & MPI_SGE_CHAIN_LENGTH_MASK) + +#define MPI_SGE_SET_FLAGS_LENGTH(f,l) (MPI_SGE_SET_FLAGS(f) | MPI_SGE_LENGTH(l)) + +#define MPI_pSGE_GET_FLAGS(psg) MPI_SGE_GET_FLAGS((psg)->FlagsLength) +#define MPI_pSGE_GET_LENGTH(psg) MPI_SGE_LENGTH((psg)->FlagsLength) +#define MPI_pSGE_SET_FLAGS_LENGTH(psg,f,l) (psg)->FlagsLength = MPI_SGE_SET_FLAGS_LENGTH(f,l) + /* CAUTION - The following are READ-MODIFY-WRITE! */ +#define MPI_pSGE_SET_FLAGS(psg,f) (psg)->FlagsLength |= MPI_SGE_SET_FLAGS(f) +#define MPI_pSGE_SET_LENGTH(psg,l) (psg)->FlagsLength |= MPI_SGE_LENGTH(l) + +#define MPI_GET_CHAIN_OFFSET(x) ((x&MPI_SGE_CHAIN_OFFSET_MASK)>>MPI_SGE_CHAIN_OFFSET_SHIFT) + + + +/***************************************************************************** +* +* S t a n d a r d M e s s a g e S t r u c t u r e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Standard message request header for all request messages */ +/****************************************************************************/ + +typedef struct _MSG_REQUEST_HEADER +{ + U8 Reserved[2]; /* function specific */ + U8 ChainOffset; + U8 Function; + U8 Reserved1[3]; /* function specific */ + U8 MsgFlags; + U32 MsgContext; +} MSG_REQUEST_HEADER, MPI_POINTER PTR_MSG_REQUEST_HEADER, + MPIHeader_t, MPI_POINTER pMPIHeader_t; + + +/****************************************************************************/ +/* Default Reply */ +/****************************************************************************/ + +typedef struct _MSG_DEFAULT_REPLY +{ + U8 Reserved[2]; /* function specific */ + U8 MsgLength; + U8 Function; + U8 Reserved1[3]; /* function specific */ + U8 MsgFlags; + U32 MsgContext; + U8 Reserved2[2]; /* function specific */ + U16 IOCStatus; + U32 IOCLogInfo; +} MSG_DEFAULT_REPLY, MPI_POINTER PTR_MSG_DEFAULT_REPLY, + MPIDefaultReply_t, MPI_POINTER pMPIDefaultReply_t; + + +/* MsgFlags definition for all replies */ + +#define MPI_MSGFLAGS_CONTINUATION_REPLY (0x80) + + +/***************************************************************************** +* +* I O C S t a t u s V a l u e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Common IOCStatus values for all replies */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_SUCCESS (0x0000) +#define MPI_IOCSTATUS_INVALID_FUNCTION (0x0001) +#define MPI_IOCSTATUS_BUSY (0x0002) +#define MPI_IOCSTATUS_INVALID_SGL (0x0003) +#define MPI_IOCSTATUS_INTERNAL_ERROR (0x0004) +#define MPI_IOCSTATUS_RESERVED (0x0005) +#define MPI_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) +#define MPI_IOCSTATUS_INVALID_FIELD (0x0007) +#define MPI_IOCSTATUS_INVALID_STATE (0x0008) + +/****************************************************************************/ +/* Config IOCStatus values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) +#define MPI_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) +#define MPI_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) +#define MPI_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) +#define MPI_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) +#define MPI_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) + +/****************************************************************************/ +/* SCSIIO Reply (SPI & FCP) initiator values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) +#define MPI_IOCSTATUS_SCSI_INVALID_BUS (0x0041) +#define MPI_IOCSTATUS_SCSI_INVALID_TARGETID (0x0042) +#define MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) +#define MPI_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) +#define MPI_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) +#define MPI_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) +#define MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) +#define MPI_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) +#define MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) +#define MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A) +#define MPI_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B) +#define MPI_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C) + +/****************************************************************************/ +/* SCSI (SPI & FCP) target values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_TARGET_PRIORITY_IO (0x0060) +#define MPI_IOCSTATUS_TARGET_INVALID_PORT (0x0061) +#define MPI_IOCSTATUS_TARGET_INVALID_IOCINDEX (0x0062) +#define MPI_IOCSTATUS_TARGET_ABORTED (0x0063) +#define MPI_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064) +#define MPI_IOCSTATUS_TARGET_NO_CONNECTION (0x0065) +#define MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A) +#define MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT (0x006B) + +/****************************************************************************/ +/* Additional FCP target values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_TARGET_FC_ABORTED (0x0066) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_RX_ID_INVALID (0x0067) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_DID_INVALID (0x0068) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_NODE_LOGGED_OUT (0x0069) /* obsolete */ + +/****************************************************************************/ +/* Fibre Channel Direct Access values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_FC_ABORTED (0x0066) +#define MPI_IOCSTATUS_FC_RX_ID_INVALID (0x0067) +#define MPI_IOCSTATUS_FC_DID_INVALID (0x0068) +#define MPI_IOCSTATUS_FC_NODE_LOGGED_OUT (0x0069) + +/****************************************************************************/ +/* LAN values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_LAN_DEVICE_NOT_FOUND (0x0080) +#define MPI_IOCSTATUS_LAN_DEVICE_FAILURE (0x0081) +#define MPI_IOCSTATUS_LAN_TRANSMIT_ERROR (0x0082) +#define MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED (0x0083) +#define MPI_IOCSTATUS_LAN_RECEIVE_ERROR (0x0084) +#define MPI_IOCSTATUS_LAN_RECEIVE_ABORTED (0x0085) +#define MPI_IOCSTATUS_LAN_PARTIAL_PACKET (0x0086) +#define MPI_IOCSTATUS_LAN_CANCELED (0x0087) + + +/****************************************************************************/ +/* IOCStatus flag to indicate that log info is available */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000) +#define MPI_IOCSTATUS_MASK (0x7FFF) + +/****************************************************************************/ +/* LogInfo Types */ +/****************************************************************************/ + +#define MPI_IOCLOGINFO_TYPE_MASK (0xF0000000) +#define MPI_IOCLOGINFO_TYPE_NONE (0x00) +#define MPI_IOCLOGINFO_TYPE_SCSI (0x01) +#define MPI_IOCLOGINFO_TYPE_FC (0x02) +#define MPI_IOCLOGINFO_LOG_DATA_MASK (0x0FFFFFFF) + + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/lsi/mpi_cnfg.h linux.ac/drivers/message/fusion/lsi/mpi_cnfg.h --- linux.vanilla/drivers/message/fusion/lsi/mpi_cnfg.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/lsi/mpi_cnfg.h Tue Apr 3 17:54:47 2001 @@ -0,0 +1,971 @@ +/* + * Copyright (c) 2000-2001 LSI Logic Corporation. + * + * + * Name: MPI_CNFG.H + * Title: MPI Config message, structures, and Pages + * Creation Date: July 27, 2000 + * + * MPI Version: 01.01.09 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-08-00 01.00.02 Added _PAGEVERSION definitions for all pages. + * Added FcPhLowestVersion, FcPhHighestVersion, Reserved2 + * fields to FC_DEVICE_0 page, updated the page version. + * Changed _FREE_RUNNING_CLOCK to _PACING_TRANSFERS in + * SCSI_PORT_0, SCSI_DEVICE_0 and SCSI_DEVICE_1 pages + * and updated the page versions. + * Added _RESPONSE_ID_MASK definition to SCSI_PORT_1 + * page and updated the page version. + * Added Information field and _INFO_PARAMS_NEGOTIATED + * definitionto SCSI_DEVICE_0 page. + * 06-22-00 01.00.03 Removed batch controls from LAN_0 page and updated the + * page version. + * Added BucketsRemaining to LAN_1 page, redefined the + * state values, and updated the page version. + * Revised bus width definitions in SCSI_PORT_0, + * SCSI_DEVICE_0 and SCSI_DEVICE_1 pages. + * 06-30-00 01.00.04 Added MaxReplySize to LAN_1 page and updated the page + * version. + * Moved FC_DEVICE_0 PageAddress description to spec. + * 07-27-00 01.00.05 Corrected the SubsystemVendorID and SubsystemID field + * widths in IOC_0 page and updated the page version. + * 11-02-00 01.01.01 Original release for post 1.0 work + * Added Manufacturing pages, IO Unit Page 2, SCSI SPI + * Port Page 2, FC Port Page 4, FC Port Page 5 + * 11-15-00 01.01.02 Interim changes to match proposals + * 12-04-00 01.01.03 Config page changes to match MPI rev 1.00.01. + * 12-05-00 01.01.04 Modified config page actions. + * 01-09-01 01.01.05 Added defines for page address formats. + * Data size for Manufacturing pages 2 and 3 no longer + * defined here. + * Io Unit Page 2 size is fixed at 4 adapters and some + * flags were changed. + * SCSI Port Page 2 Device Settings modified. + * New fields added to FC Port Page 0 and some flags + * cleaned up. + * Removed impedance flash from FC Port Page 1. + * Added FC Port pages 6 and 7. + * 01-25-01 01.01.06 Added MaxInitiators field to FcPortPage0. + * 01-29-01 01.01.07 Changed some defines to make them 32 character unique. + * Added some LinkType defines for FcPortPage0. + * 02-20-01 01.01.08 Started using MPI_POINTER. + * 02-27-01 01.01.09 Replaced MPI_CONFIG_PAGETYPE_SCSI_LUN with + * MPI_CONFIG_PAGETYPE_RAID_VOLUME. + * Added definitions and structures for IOC Page 2 and + * RAID Volume Page 2. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_CNFG_H +#define MPI_CNFG_H + + +/***************************************************************************** +* +* C o n f i g M e s s a g e a n d S t r u c t u r e s +* +*****************************************************************************/ + +typedef struct _CONFIG_PAGE_HEADER +{ + U8 PageVersion; + U8 PageLength; + U8 PageNumber; + U8 PageType; +} fCONFIG_PAGE_HEADER, MPI_POINTER PTR_CONFIG_PAGE_HEADER, + ConfigPageHeader_t, MPI_POINTER pConfigPageHeader_t; + +typedef union _CONFIG_PAGE_HEADER_UNION +{ + ConfigPageHeader_t Struct; + U8 Bytes[4]; + U16 Word16[2]; + U32 Word32; +} ConfigPageHeaderUnion, MPI_POINTER pConfigPageHeaderUnion, + fCONFIG_PAGE_HEADER_UNION, MPI_POINTER PTR_CONFIG_PAGE_HEADER_UNION; + + +/****************************************************************************/ +/* PageType field values */ +/****************************************************************************/ +#define MPI_CONFIG_PAGEATTR_READ_ONLY (0x00) +#define MPI_CONFIG_PAGEATTR_CHANGEABLE (0x10) +#define MPI_CONFIG_PAGEATTR_PERSISTENT (0x20) +#define MPI_CONFIG_PAGEATTR_MASK (0xF0) + +#define MPI_CONFIG_PAGETYPE_IO_UNIT (0x00) +#define MPI_CONFIG_PAGETYPE_IOC (0x01) +#define MPI_CONFIG_PAGETYPE_BIOS (0x02) +#define MPI_CONFIG_PAGETYPE_SCSI_PORT (0x03) +#define MPI_CONFIG_PAGETYPE_SCSI_DEVICE (0x04) +#define MPI_CONFIG_PAGETYPE_FC_PORT (0x05) +#define MPI_CONFIG_PAGETYPE_FC_DEVICE (0x06) +#define MPI_CONFIG_PAGETYPE_LAN (0x07) +#define MPI_CONFIG_PAGETYPE_RAID_VOLUME (0x08) +#define MPI_CONFIG_PAGETYPE_MANUFACTURING (0x09) +#define MPI_CONFIG_PAGETYPE_MASK (0x0F) + +#define MPI_CONFIG_TYPENUM_MASK (0x0FFF) + + +/**************************************************************************** + * PageAddres field values + ****************************************************************************/ +#define MPI_SCSI_PORT_PGAD_PORT_MASK (0x000000FF) + +#define MPI_SCSI_DEVICE_TARGET_ID_MASK (0x000000FF) +#define MPI_SCSI_DEVICE_TARGET_ID_SHIFT (0) +#define MPI_SCSI_DEVICE_BUS_MASK (0x0000FF00) +#define MPI_SCSI_DEVICE_BUS_SHIFT (8) + +#define MPI_SCSI_LUN_TARGET_ID_MASK (0x000000FF) +#define MPI_SCSI_LUN_TARGET_ID_SHIFT (0) +#define MPI_SCSI_LUN_BUS_MASK (0x0000FF00) +#define MPI_SCSI_LUN_BUS_SHIFT (8) +#define MPI_SCSI_LUN_LUN_MASK (0x00FF0000) +#define MPI_SCSI_LUN_LUN_SHIFT (16) + +#define MPI_FC_PORT_PGAD_PORT_MASK (0xF0000000) +#define MPI_FC_PORT_PGAD_PORT_SHIFT (28) +#define MPI_FC_PORT_PGAD_FORM_MASK (0x0F000000) +#define MPI_FC_PORT_PGAD_FORM_INDEX (0x01000000) +#define MPI_FC_PORT_PGAD_INDEX_MASK (0x0000FFFF) +#define MPI_FC_PORT_PGAD_INDEX_SHIFT (0) + +#define MPI_FC_DEVICE_PGAD_PORT_MASK (0xF0000000) +#define MPI_FC_DEVICE_PGAD_PORT_SHIFT (28) +#define MPI_FC_DEVICE_PGAD_FORM_MASK (0x0F000000) +#define MPI_FC_DEVICE_PGAD_FORM_NEXT_DID (0x00000000) +#define MPI_FC_DEVICE_PGAD_ND_PORT_MASK (0xF0000000) +#define MPI_FC_DEVICE_PGAD_ND_PORT_SHIFT (28) +#define MPI_FC_DEVICE_PGAD_ND_DID_MASK (0x00FFFFFF) +#define MPI_FC_DEVICE_PGAD_ND_DID_SHIFT (0) +#define MPI_FC_DEVICE_PGAD_FORM_BUS_TID (0x01000000) +#define MPI_FC_DEVICE_PGAD_BT_BUS_MASK (0x0000FF00) +#define MPI_FC_DEVICE_PGAD_BT_BUS_SHIFT (8) +#define MPI_FC_DEVICE_PGAD_BT_TID_MASK (0x000000FF) +#define MPI_FC_DEVICE_PGAD_BT_TID_SHIFT (0) + + +/****************************************************************************/ +/* Config Request Message */ +/****************************************************************************/ +typedef struct _MSG_CONFIG +{ + U8 Action; + U8 Reserved; + U8 ChainOffset; + U8 Function; + U8 Reserved1[3]; + U8 MsgFlags; + U32 MsgContext; + U8 Reserved2[8]; + fCONFIG_PAGE_HEADER Header; + U32 PageAddress; + SGE_IO_UNION PageBufferSGE; +} MSG_CONFIG, MPI_POINTER PTR_MSG_CONFIG, + Config_t, MPI_POINTER pConfig_t; + + +/****************************************************************************/ +/* Action field values */ +/****************************************************************************/ +#define MPI_CONFIG_ACTION_PAGE_HEADER (0x00) +/*#define MPI_CONFIG_ACTION_PAGE_READ (0x01) *//* obsolete */ +#define MPI_CONFIG_ACTION_PAGE_READ_CURRENT (0x01) +/*#define MPI_CONFIG_ACTION_PAGE_WRITE (0x02) *//* obsolete */ +#define MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT (0x02) +#define MPI_CONFIG_ACTION_PAGE_DEFAULT (0x03) +/*#define MPI_CONFIG_ACTION_PAGE_WRITE_COMMIT (0x04) */ /* obsolete */ +#define MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM (0x04) +#define MPI_CONFIG_ACTION_PAGE_READ_DEFAULT (0x05) +#define MPI_CONFIG_ACTION_PAGE_READ_NVRAM (0x06) + + +/* Config Reply Message */ +typedef struct _MSG_CONFIG_REPLY +{ + U8 Action; + U8 Reserved; + U8 MsgLength; + U8 Function; + U8 Reserved1[3]; + U8 MsgFlags; + U32 MsgContext; + U8 Reserved2[2]; + U16 IOCStatus; + U32 IOCLogInfo; + fCONFIG_PAGE_HEADER Header; +} MSG_CONFIG_REPLY, MPI_POINTER PTR_MSG_CONFIG_REPLY, + ConfigReply_t, MPI_POINTER pConfigReply_t; + + + +/***************************************************************************** +* +* C o n f i g u r a t i o n P a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Manufacturing Config pages */ +/****************************************************************************/ +#define MPI_MANUFACTPAGE_DEVICEID_FC909 (0x0621) +#define MPI_MANUFACTPAGE_DEVICEID_FC919 (0x0624) +#define MPI_MANUFACTPAGE_DEVICEID_FC929 (0x0622) +#define MPI_MANUFACTPAGE_DEVID_53C1030 (0x0030) +#define MPI_MANUFACTPAGE_DEVID_53C1030ZC (0x0031) +#define MPI_MANUFACTPAGE_DEVID_53C1035 (0x0035) + +typedef struct _CONFIG_PAGE_MANUFACTURING_0 +{ + fCONFIG_PAGE_HEADER Header; + U8 ChipName[16]; + U8 ChipRevision[8]; + U8 BoardName[16]; + U8 BoardAssembly[16]; + U8 BoardTracerNumber[16]; + +} fCONFIG_PAGE_MANUFACTURING_0, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_0, + ManufacturingPage0_t, MPI_POINTER pManufacturingPage0_t; + +#define MPI_MANUFACTURING0_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_MANUFACTURING_1 +{ + fCONFIG_PAGE_HEADER Header; + U8 VPD[256]; +} fCONFIG_PAGE_MANUFACTURING_1, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_1, + ManufacturingPage1_t, MPI_POINTER pManufacturingPage1_t; + +#define MPI_MANUFACTURING1_PAGEVERSION (0x00) + + +typedef struct _MPI_CHIP_REVISION_ID +{ + U16 DeviceID; + U8 PCIRevisionID; + U8 Reserved; +} MPI_CHIP_REVISION_ID, MPI_POINTER PTR_MPI_CHIP_REVISION_ID, + MpiChipRevisionId_t, MPI_POINTER pMpiChipRevisionId_t; + + +typedef struct _CONFIG_PAGE_MANUFACTURING_2 +{ + fCONFIG_PAGE_HEADER Header; + MPI_CHIP_REVISION_ID ChipId; + U32 HwSettings[1]; +} fCONFIG_PAGE_MANUFACTURING_2, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_2, + ManufacturingPage2_t, MPI_POINTER pManufacturingPage2_t; + +#define MPI_MANUFACTURING2_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_MANUFACTURING_3 +{ + fCONFIG_PAGE_HEADER Header; + MPI_CHIP_REVISION_ID ChipId; + U32 Info[1]; +} fCONFIG_PAGE_MANUFACTURING_3, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_3, + ManufacturingPage3_t, MPI_POINTER pManufacturingPage3_t; + +#define MPI_MANUFACTURING3_PAGEVERSION (0x00) + + +/****************************************************************************/ +/* IO Unit Config Pages */ +/****************************************************************************/ + +typedef struct _CONFIG_PAGE_IO_UNIT_0 +{ + fCONFIG_PAGE_HEADER Header; + U64 UniqueValue; +} fCONFIG_PAGE_IO_UNIT_0, MPI_POINTER PTR_CONFIG_PAGE_IO_UNIT_0, + IOUnitPage0_t, MPI_POINTER pIOUnitPage0_t; + +#define MPI_IOUNITPAGE0_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_IO_UNIT_1 +{ + fCONFIG_PAGE_HEADER Header; + U32 Flags; +} fCONFIG_PAGE_IO_UNIT_1, MPI_POINTER PTR_CONFIG_PAGE_IO_UNIT_1, + IOUnitPage1_t, MPI_POINTER pIOUnitPage1_t; + +#define MPI_IOUNITPAGE1_PAGEVERSION (0x00) + +#define MPI_IOUNITPAGE1_MULTI_FUNCTION (0x00000000) +#define MPI_IOUNITPAGE1_SINGLE_FUNCTION (0x00000001) +#define MPI_IOUNITPAGE1_MULTI_PATHING (0x00000002) +#define MPI_IOUNITPAGE1_SINGLE_PATHING (0x00000000) + +#define MPI_IOUNITPAGE1_FORCE_32 (0x00000080) + + +typedef struct _MPI_ADAPTER_INFO +{ + U8 PciBusNumber; + U8 PciDeviceAndFunctionNumber; + U16 AdapterFlags; +} MPI_ADAPTER_INFO, MPI_POINTER PTR_MPI_ADAPTER_INFO, + MpiAdapterInfo_t, MPI_POINTER pMpiAdapterInfo_t; + +#define MPI_ADAPTER_INFO_FLAGS_EMBEDDED (0x0001) +#define MPI_ADAPTER_INFO_FLAGS_INIT_STATUS (0x0002) + +typedef struct _CONFIG_PAGE_IO_UNIT_2 +{ + fCONFIG_PAGE_HEADER Header; + U32 Flags; + U32 BiosVersion; + MPI_ADAPTER_INFO AdapterOrder[4]; +} fCONFIG_PAGE_IO_UNIT_2, MPI_POINTER PTR_CONFIG_PAGE_IO_UNIT_2, + IOUnitPage2_t, MPI_POINTER pIOUnitPage2_t; + +#define MPI_IOUNITPAGE2_PAGEVERSION (0x00) + +#define MPI_IOUNITPAGE2_FLAGS_RAID_DISABLE (0x00000001) +#define MPI_IOUNITPAGE2_FLAGS_PAUSE_ON_ERROR (0x00000002) +#define MPI_IOUNITPAGE2_FLAGS_VERBOSE_ENABLE (0x00000004) +#define MPI_IOUNITPAGE2_FLAGS_COLOR_VIDEO_DISABLE (0x00000008) +#define MPI_IOUNITPAGE2_FLAGS_DONT_HOOK_INT_40 (0x00000010) + + +/****************************************************************************/ +/* IOC Config Pages */ +/****************************************************************************/ + +typedef struct _CONFIG_PAGE_IOC_0 +{ + fCONFIG_PAGE_HEADER Header; + U32 TotalNVStore; + U32 FreeNVStore; + U16 VendorID; + U16 DeviceID; + U8 RevisionID; + U8 Reserved[3]; + U32 ClassCode; + U16 SubsystemVendorID; + U16 SubsystemID; +} fCONFIG_PAGE_IOC_0, MPI_POINTER PTR_CONFIG_PAGE_IOC_0, + IOCPage0_t, MPI_POINTER pIOCPage0_t; + +#define MPI_IOCPAGE0_PAGEVERSION (0x01) + +typedef struct _CONFIG_PAGE_IOC_1 +{ + fCONFIG_PAGE_HEADER Header; + U32 Flags; + U32 CoalescingTimeout; + U8 CoalescingDepth; + U8 Reserved[3]; +} fCONFIG_PAGE_IOC_1, MPI_POINTER PTR_CONFIG_PAGE_IOC_1, + IOCPage1_t, MPI_POINTER pIOCPage1_t; + +#define MPI_IOCPAGE1_PAGEVERSION (0x00) + +#define MPI_IOCPAGE1_REPLY_COALESCING (0x00000001) + +typedef struct _CONFIG_PAGE_IOC_2_RAID_VOL +{ + U8 VolumeTargetID; + U8 VolumeBus; + U16 Reserved; + U8 VolumeVersionMinor; + U8 VolumeVersionMajor; + U8 VolumeRaidType; + U8 Reserved1; +} fCONFIG_PAGE_IOC_2_RAID_VOL, MPI_POINTER PTR_CONFIG_PAGE_IOC_2_RAID_VOL, + ConfigPageIoc2RaidVol_t, MPI_POINTER pConfigPageIoc2RaidVol_t; + +typedef struct _CONFIG_PAGE_IOC_2 +{ + fCONFIG_PAGE_HEADER Header; + U32 CapabilitiesFlags; + U8 NumActiveVolumes; + U8 MaxVolumes; + U16 Reserved; + fCONFIG_PAGE_IOC_2_RAID_VOL RaidVolume[1]; +} fCONFIG_PAGE_IOC_2, MPI_POINTER PTR_CONFIG_PAGE_IOC_2, + IOCPage2_t, MPI_POINTER pIOCPage2_t; + +#define MPI_IOCPAGE2_PAGEVERSION (0x00) + +/* IOC Page 2 Capabilities flags */ + +#define MPI_IOCPAGE2_CAP_FLAGS_RAID_0_SUPPORT (0x00000001) +#define MPI_IOCPAGE2_CAP_FLAGS_RAID_1_SUPPORT (0x00000002) +#define MPI_IOCPAGE2_CAP_FLAGS_LSI_MIRROR_SUPPORT (0x00000004) +#define MPI_IOCPAGE2_CAP_FLAGS_RAID_5_SUPPORT (0x00000008) +#define MPI_IOCPAGE2_CAP_FLAGS_RAID_10_SUPPORT (0x00000010) + +/* IOC Page 2 Volume RAID Type values */ + +#define MPI_IOCPAGE2_VOL_TYPE_RAID_0 (0x00) +#define MPI_IOCPAGE2_VOL_TYPE_RAID_1 (0x01) +#define MPI_IOCPAGE2_VOL_TYPE_LSI_MIRROR (0x02) +#define MPI_IOCPAGE2_VOL_TYPE_RAID_5 (0x05) +#define MPI_IOCPAGE2_VOL_TYPE_RAID_10 (0x0A) + + +/****************************************************************************/ +/* SCSI Port Config Pages */ +/****************************************************************************/ + +typedef struct _CONFIG_PAGE_SCSI_PORT_0 +{ + fCONFIG_PAGE_HEADER Header; + U32 Capabilities; + U32 PhysicalInterface; +} fCONFIG_PAGE_SCSI_PORT_0, MPI_POINTER PTR_CONFIG_PAGE_SCSI_PORT_0, + SCSIPortPage0_t, MPI_POINTER pSCSIPortPage0_t; + +#define MPI_SCSIPORTPAGE0_PAGEVERSION (0x01) + +#define MPI_SCSIPORTPAGE0_CAP_IU (0x00000001) +#define MPI_SCSIPORTPAGE0_CAP_DT (0x00000002) +#define MPI_SCSIPORTPAGE0_CAP_QAS (0x00000004) +#define MPI_SCSIPORTPAGE0_CAP_PACING_TRANSFERS (0x00000008) +#define MPI_SCSIPORTPAGE0_CAP_MIN_SYNC_PERIOD_MASK (0x0000FF00) +#define MPI_SCSIPORTPAGE0_CAP_MAX_SYNC_OFFSET_MASK (0x00FF0000) +#define MPI_SCSIPORTPAGE0_CAP_WIDE (0x20000000) +#define MPI_SCSIPORTPAGE0_CAP_AIP (0x80000000) + +#define MPI_SCSIPORTPAGE0_PHY_SIGNAL_TYPE_MASK (0x00000003) +#define MPI_SCSIPORTPAGE0_PHY_SIGNAL_HVD (0x01) +#define MPI_SCSIPORTPAGE0_PHY_SIGNAL_SE (0x02) +#define MPI_SCSIPORTPAGE0_PHY_SIGNAL_LVD (0x03) + +typedef struct _CONFIG_PAGE_SCSI_PORT_1 +{ + fCONFIG_PAGE_HEADER Header; + U32 Configuration; +} fCONFIG_PAGE_SCSI_PORT_1, MPI_POINTER PTR_CONFIG_PAGE_SCSI_PORT_1, + SCSIPortPage1_t, MPI_POINTER pSCSIPortPage1_t; + +#define MPI_SCSIPORTPAGE1_PAGEVERSION (0x01) + +#define MPI_SCSIPORTPAGE1_CFG_PORT_SCSI_ID_MASK (0x000000FF) +#define MPI_SCSIPORTPAGE1_CFG_PORT_RESPONSE_ID_MASK (0xFFFF0000) + +typedef struct _MPI_DEVICE_INFO +{ + U8 Timeout; + U8 SyncFactor; + U16 DeviceFlags; +} MPI_DEVICE_INFO, MPI_POINTER PTR_MPI_DEVICE_INFO, + MpiDeviceInfo_t, MPI_POINTER pMpiDeviceInfo_t; + +typedef struct _CONFIG_PAGE_SCSI_PORT_2 +{ + fCONFIG_PAGE_HEADER Header; + U32 PortFlags; + U32 PortSettings; + MPI_DEVICE_INFO DeviceSettings[16]; +} fCONFIG_PAGE_SCSI_PORT_2, MPI_POINTER PTR_CONFIG_PAGE_SCSI_PORT_2, + SCSIPortPage2_t, MPI_POINTER pSCSIPortPage2_t; + +#define MPI_SCSIPORTPAGE2_PAGEVERSION (0x01) + +#define MPI_SCSIPORTPAGE2_PORT_FLAGS_SCAN_HIGH_TO_LOW (0x00000001) +#define MPI_SCSIPORTPAGE2_PORT_FLAGS_PARITY_ENABLE (0x00000002) +#define MPI_SCSIPORTPAGE2_PORT_FLAGS_AVOID_SCSI_RESET (0x00000004) +#define MPI_SCSIPORTPAGE2_PORT_FLAGS_ALTERNATE_CHS (0x00000008) +#define MPI_SCSIPORTPAGE2_PORT_FLAGS_TERMINATION_DISABLE (0x00000010) + +#define MPI_SCSIPORTPAGE2_PORT_HOST_ID_MASK (0x0000000F) +#define MPI_SCSIPORTPAGE2_PORT_MASK_INIT_HBA (0x00000030) +#define MPI_SCSIPORTPAGE2_PORT_DISABLE_INIT_HBA (0x00000000) +#define MPI_SCSIPORTPAGE2_PORT_BIOS_INIT_HBA (0x00000010) +#define MPI_SCSIPORTPAGE2_PORT_OS_INIT_HBA (0x00000020) +#define MPI_SCSIPORTPAGE2_PORT_BIOS_OS_INIT_HBA (0x00000030) +#define MPI_SCSIPORTPAGE2_PORT_REMOVABLE_MEDIA (0x000000C0) +#define MPI_SCSIPORTPAGE2_PORT_SPINUP_DELAY_MASK (0x00000F00) +#define MPI_SCSIPORTPAGE2_PORT_MASK_NEGO_MASTER_SETTINGS (0x00003000) +#define MPI_SCSIPORTPAGE2_PORT_NEGO_MASTER_SETTINGS (0x00000000) +#define MPI_SCSIPORTPAGE2_PORT_NONE_MASTER_SETTINGS (0x00000001) +#define MPI_SCSIPORTPAGE2_PORT_ALL_MASTER_SETTINGS (0x00000003) + +#define MPI_SCSIPORTPAGE2_DEVICE_DISCONNECT_ENABLE (0x00000001) +#define MPI_SCSIPORTPAGE2_DEVICE_ID_SCAN_ENABLE (0x00000002) +#define MPI_SCSIPORTPAGE2_DEVICE_LUN_SCAN_ENABLE (0x00000004) +#define MPI_SCSIPORTPAGE2_DEVICE_TAG_QUEUE_ENABLE (0x00000008) +#define MPI_SCSIPORTPAGE2_DEVICE_WIDE_DISABLE (0x00000010) +#define MPI_SCSIPORTPAGE2_DEVICE_BOOT_CHOICE (0x00000020) + + +/****************************************************************************/ +/* SCSI Target Device Config Pages */ +/****************************************************************************/ + +typedef struct _CONFIG_PAGE_SCSI_DEVICE_0 +{ + fCONFIG_PAGE_HEADER Header; + U32 NegotiatedParameters; + U32 Information; +} fCONFIG_PAGE_SCSI_DEVICE_0, MPI_POINTER PTR_CONFIG_PAGE_SCSI_DEVICE_0, + SCSIDevicePage0_t, MPI_POINTER pSCSIDevicePage0_t; + +#define MPI_SCSIDEVPAGE0_PAGEVERSION (0x01) + +#define MPI_SCSIDEVPAGE0_NP_IU (0x00000001) +#define MPI_SCSIDEVPAGE0_NP_DT (0x00000002) +#define MPI_SCSIDEVPAGE0_NP_QAS (0x00000004) +#define MPI_SCSIDEVPAGE0_NP_PACING_TRANSFERS (0x00000008) +#define MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK (0x0000FF00) +#define MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK (0x00FF0000) +#define MPI_SCSIDEVPAGE0_NP_WIDE (0x20000000) +#define MPI_SCSIDEVPAGE0_NP_AIP (0x80000000) + +#define MPI_SCSIDEVPAGE0_INFO_PARAMS_NEGOTIATED (0x00000001) + +typedef struct _CONFIG_PAGE_SCSI_DEVICE_1 +{ + fCONFIG_PAGE_HEADER Header; + U32 RequestedParameters; + U32 DomainValidation; + U32 Configuration; +} fCONFIG_PAGE_SCSI_DEVICE_1, MPI_POINTER PTR_CONFIG_PAGE_SCSI_DEVICE_1, + SCSIDevicePage1_t, MPI_POINTER pSCSIDevicePage1_t; + +#define MPI_SCSIDEVPAGE1_PAGEVERSION (0x01) + +#define MPI_SCSIDEVPAGE1_RP_IU (0x00000001) +#define MPI_SCSIDEVPAGE1_RP_DT (0x00000002) +#define MPI_SCSIDEVPAGE1_RP_QAS (0x00000004) +#define MPI_SCSIDEVPAGE1_RP_PACING_TRANSFERS (0x00000008) +#define MPI_SCSIDEVPAGE1_RP_MIN_SYNC_PERIOD_MASK (0x0000FF00) +#define MPI_SCSIDEVPAGE1_RP_MAX_SYNC_OFFSET_MASK (0x00FF0000) +#define MPI_SCSIDEVPAGE1_RP_WIDE (0x20000000) +#define MPI_SCSIDEVPAGE1_RP_AIP (0x80000000) + +#define MPI_SCSIDEVPAGE1_DV_LVD_DRIVE_STRENGTH_MASK (0x00000003) +#define MPI_SCSIDEVPAGE1_DV_SE_SLEW_RATE_MASK (0x00000300) + +#define MPI_SCSIDEVPAGE1_CONF_PPR_ALLOWED (0x00000001) + +/****************************************************************************/ +/* FC Port Config Pages */ +/****************************************************************************/ + +typedef struct _CONFIG_PAGE_FC_PORT_0 +{ + fCONFIG_PAGE_HEADER Header; + U32 Flags; + U8 MPIPortNumber; + U8 LinkType; + U8 PortState; + U8 Reserved; + U32 PortIdentifier; + U64 WWNN; + U64 WWPN; + U32 SupportedServiceClass; + U32 SupportedSpeeds; + U32 CurrentSpeed; + U32 MaxFrameSize; + U64 FabricWWNN; + U64 FabricWWPN; + U32 DiscoveredPortsCount; + U32 MaxInitiators; +} fCONFIG_PAGE_FC_PORT_0, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_0, + FCPortPage0_t, MPI_POINTER pFCPortPage0_t; + +#define MPI_FCPORTPAGE0_PAGEVERSION (0x01) + +#define MPI_FCPORTPAGE0_FLAGS_PROT_MASK (0x0000000F) +#define MPI_FCPORTPAGE0_FLAGS_PROT_FCP_INIT (MPI_PORTFACTS_PROTOCOL_INITIATOR) +#define MPI_FCPORTPAGE0_FLAGS_PROT_FCP_TARG (MPI_PORTFACTS_PROTOCOL_TARGET) +#define MPI_FCPORTPAGE0_FLAGS_PROT_LAN (MPI_PORTFACTS_PROTOCOL_LAN) +#define MPI_FCPORTPAGE0_FLAGS_PROT_LOGBUSADDR (MPI_PORTFACTS_PROTOCOL_LOGBUSADDR) + +#define MPI_FCPORTPAGE0_FLAGS_ALIAS_ALPA_SUPPORTED (0x00000010) +#define MPI_FCPORTPAGE0_FLAGS_ALIAS_WWN_SUPPORTED (0x00000020) +#define MPI_FCPORTPAGE0_FLAGS_FABRIC_WWN_VALID (0x00000030) + +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_TYPE_MASK (0x00000700) +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_POINT_TO_POINT (0x00000000) +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_PRIVATE_LOOP (0x00000100) +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_FABRIC_DIRECT (0x00000200) +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_PUBLIC_LOOP (0x00000300) +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_NO_INIT (0x00000700) + +#define MPI_FCPORTPAGE0_LTYPE_RESERVED (0x00) +#define MPI_FCPORTPAGE0_LTYPE_OTHER (0x01) +#define MPI_FCPORTPAGE0_LTYPE_UNKNOWN (0x02) +#define MPI_FCPORTPAGE0_LTYPE_COPPER (0x03) +#define MPI_FCPORTPAGE0_LTYPE_SINGLE_1300 (0x04) +#define MPI_FCPORTPAGE0_LTYPE_SINGLE_1500 (0x05) +#define MPI_FCPORTPAGE0_LTYPE_50_LASER_MULTI (0x06) +#define MPI_FCPORTPAGE0_LTYPE_50_LED_MULTI (0x07) +#define MPI_FCPORTPAGE0_LTYPE_62_LASER_MULTI (0x08) +#define MPI_FCPORTPAGE0_LTYPE_62_LED_MULTI (0x09) +#define MPI_FCPORTPAGE0_LTYPE_MULTI_LONG_WAVE (0x0A) +#define MPI_FCPORTPAGE0_LTYPE_MULTI_SHORT_WAVE (0x0B) +#define MPI_FCPORTPAGE0_LTYPE_LASER_SHORT_WAVE (0x0C) +#define MPI_FCPORTPAGE0_LTYPE_LED_SHORT_WAVE (0x0D) +#define MPI_FCPORTPAGE0_LTYPE_1300_LONG_WAVE (0x0E) +#define MPI_FCPORTPAGE0_LTYPE_1500_LONG_WAVE (0x0F) + +#define MPI_FCPORTPAGE0_PORTSTATE_UNKNOWN (0x01) /*(SNIA)HBA_PORTSTATE_UNKNOWN 1 Unknown */ +#define MPI_FCPORTPAGE0_PORTSTATE_ONLINE (0x02) /*(SNIA)HBA_PORTSTATE_ONLINE 2 Operational */ +#define MPI_FCPORTPAGE0_PORTSTATE_OFFLINE (0x03) /*(SNIA)HBA_PORTSTATE_OFFLINE 3 User Offline */ +#define MPI_FCPORTPAGE0_PORTSTATE_BYPASSED (0x04) /*(SNIA)HBA_PORTSTATE_BYPASSED 4 Bypassed */ +#define MPI_FCPORTPAGE0_PORTSTATE_DIAGNOST (0x05) /*(SNIA)HBA_PORTSTATE_DIAGNOSTICS 5 In diagnostics mode */ +#define MPI_FCPORTPAGE0_PORTSTATE_LINKDOWN (0x06) /*(SNIA)HBA_PORTSTATE_LINKDOWN 6 Link Down */ +#define MPI_FCPORTPAGE0_PORTSTATE_ERROR (0x07) /*(SNIA)HBA_PORTSTATE_ERROR 7 Port Error */ +#define MPI_FCPORTPAGE0_PORTSTATE_LOOPBACK (0x08) /*(SNIA)HBA_PORTSTATE_LOOPBACK 8 Loopback */ + +#define MPI_FCPORTPAGE0_SUPPORT_CLASS_1 (0x00000001) +#define MPI_FCPORTPAGE0_SUPPORT_CLASS_2 (0x00000002) +#define MPI_FCPORTPAGE0_SUPPORT_CLASS_3 (0x00000004) + +#define MPI_FCPORTPAGE0_SUPPORT_1GBIT_SPEED (0x00000001) /* (SNIA)HBA_PORTSPEED_1GBIT 1 1 GBit/sec */ +#define MPI_FCPORTPAGE0_SUPPORT_2GBIT_SPEED (0x00000002) /* (SNIA)HBA_PORTSPEED_2GBIT 2 2 GBit/sec */ +#define MPI_FCPORTPAGE0_SUPPORT_10GBIT_SPEED (0x00000004) /* (SNIA)HBA_PORTSPEED_10GBIT 4 10 GBit/sec */ + +#define MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT MPI_FCPORTPAGE0_SUPPORT_1GBIT_SPEED +#define MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT MPI_FCPORTPAGE0_SUPPORT_2GBIT_SPEED +#define MPI_FCPORTPAGE0_CURRENT_SPEED_10GBIT MPI_FCPORTPAGE0_SUPPORT_10GBIT_SPEED + + +typedef struct _CONFIG_PAGE_FC_PORT_1 +{ + fCONFIG_PAGE_HEADER Header; + U32 Flags; + U64 NoSEEPROMWWNN; + U64 NoSEEPROMWWPN; + U8 HardALPA; + U8 LinkConfig; + U8 TopologyConfig; + U8 Reserved; +} fCONFIG_PAGE_FC_PORT_1, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_1, + FCPortPage1_t, MPI_POINTER pFCPortPage1_t; + +#define MPI_FCPORTPAGE1_PAGEVERSION (0x01) + +#define MPI_FCPORTPAGE1_FLAGS_SORT_BY_DID (0x00000001) +#define MPI_FCPORTPAGE1_FLAGS_SORT_BY_WWN (0x00000000) + +#define MPI_FCPORTPAGE1_FLAGS_PROT_MASK (0xF0000000) +#define MPI_FCPORTPAGE1_FLAGS_PROT_SHIFT (28) +#define MPI_FCPORTPAGE1_FLAGS_PROT_FCP_INIT ((U32)MPI_PORTFACTS_PROTOCOL_INITIATOR << MPI_FCPORTPAGE1_FLAGS_PROT_SHIFT) +#define MPI_FCPORTPAGE1_FLAGS_PROT_FCP_TARG ((U32)MPI_PORTFACTS_PROTOCOL_TARGET << MPI_FCPORTPAGE1_FLAGS_PROT_SHIFT) +#define MPI_FCPORTPAGE1_FLAGS_PROT_LAN ((U32)MPI_PORTFACTS_PROTOCOL_LAN << MPI_FCPORTPAGE1_FLAGS_PROT_SHIFT) +#define MPI_FCPORTPAGE1_FLAGS_PROT_LOGBUSADDR ((U32)MPI_PORTFACTS_PROTOCOL_LOGBUSADDR << MPI_FCPORTPAGE1_FLAGS_PROT_SHIFT) + +#define MPI_FCPORTPAGE1_HARD_ALPA_NOT_USED (0xFF) + +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_MASK (0x0F) +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_1GIG (0x00) +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_2GIG (0x01) +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_4GIG (0x02) +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_10GIG (0x03) +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_AUTO (0x0F) + +#define MPI_FCPORTPAGE1_TOPOLGY_MASK (0x0F) +#define MPI_FCPORTPAGE1_TOPOLGY_NLPORT (0x01) +#define MPI_FCPORTPAGE1_TOPOLGY_NPORT (0x02) +#define MPI_FCPORTPAGE1_TOPOLGY_AUTO (0x0F) + + +typedef struct _CONFIG_PAGE_FC_PORT_2 +{ + fCONFIG_PAGE_HEADER Header; + U8 NumberActive; + U8 ALPA[126]; + U8 Reserved; +} fCONFIG_PAGE_FC_PORT_2, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_2, + FCPortPage2_t, MPI_POINTER pFCPortPage2_t; + +#define MPI_FCPORTPAGE2_PAGEVERSION (0x00) + + +typedef struct _FC_PORT_PERSISTENT +{ + U64 WWNN; + U64 WWPN; + U8 TargetID; + U8 Bus; + U16 Flags; +} FC_PORT_PERSISTENT, MPI_POINTER PTR_FC_PORT_PERSISTENT, + PersistentData_t, MPI_POINTER pPersistentData_t; + +#define MPI_PERSISTENT_FLAGS_SHIFT (16) +#define MPI_PERSISTENT_FLAGS_ENTRY_VALID (0x0001) +#define MPI_PERSISTENT_FLAGS_SCAN_ID (0x0002) +#define MPI_PERSISTENT_FLAGS_SCAN_LUNS (0x0004) +#define MPI_PERSISTENT_FLAGS_BOOT_DEVICE (0x0008) + +typedef struct _CONFIG_PAGE_FC_PORT_3 +{ + fCONFIG_PAGE_HEADER Header; + FC_PORT_PERSISTENT Entry[1]; +} fCONFIG_PAGE_FC_PORT_3, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_3, + FCPortPage3_t, MPI_POINTER pFCPortPage3_t; + +#define MPI_FCPORTPAGE3_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_FC_PORT_4 +{ + fCONFIG_PAGE_HEADER Header; + U32 PortFlags; + U32 PortSettings; +} fCONFIG_PAGE_FC_PORT_4, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_4, + FCPortPage4_t, MPI_POINTER pFCPortPage4_t; + +#define MPI_FCPORTPAGE4_PAGEVERSION (0x00) + +#define MPI_FCPORTPAGE4_PORT_FLAGS_ALTERNATE_CHS (0x00000008) + +#define MPI_FCPORTPAGE4_PORT_MASK_INIT_HBA (0x00000030) +#define MPI_FCPORTPAGE4_PORT_DISABLE_INIT_HBA (0x00000000) +#define MPI_FCPORTPAGE4_PORT_BIOS_INIT_HBA (0x00000010) +#define MPI_FCPORTPAGE4_PORT_OS_INIT_HBA (0x00000020) +#define MPI_FCPORTPAGE4_PORT_BIOS_OS_INIT_HBA (0x00000030) +#define MPI_FCPORTPAGE4_PORT_REMOVABLE_MEDIA (0x000000C0) +#define MPI_FCPORTPAGE4_PORT_SPINUP_DELAY_MASK (0x00000F00) + + +typedef struct _CONFIG_PAGE_FC_PORT_5_ALIAS_INFO +{ + U8 Flags; + U8 AliasAlpa; + U16 Reserved; + U64 AliasWWNN; + U64 AliasWWPN; +} fCONFIG_PAGE_FC_PORT_5_ALIAS_INFO, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_5_ALIAS_INFO, + FcPortPage5AliasInfo_t, MPI_POINTER pFcPortPage5AliasInfo_t; + +typedef struct _CONFIG_PAGE_FC_PORT_5 +{ + fCONFIG_PAGE_HEADER Header; + fCONFIG_PAGE_FC_PORT_5_ALIAS_INFO AliasInfo[1]; +} fCONFIG_PAGE_FC_PORT_5, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_5, + FCPortPage5_t, MPI_POINTER pFCPortPage5_t; + +#define MPI_FCPORTPAGE5_PAGEVERSION (0x00) + +#define MPI_FCPORTPAGE5_FLAGS_ALIAS_ALPA_VALID (0x01) +#define MPI_FCPORTPAGE5_FLAGS_ALIAS_WWN_VALID (0x02) + + +typedef struct _CONFIG_PAGE_FC_PORT_6 +{ + fCONFIG_PAGE_HEADER Header; + U32 Reserved; + U64 TimeSinceReset; + U64 TxFrames; + U64 RxFrames; + U64 TxWords; + U64 RxWords; + U64 LipCount; + U64 NosCount; + U64 ErrorFrames; + U64 DumpedFrames; + U64 LinkFailureCount; + U64 LossOfSyncCount; + U64 LossOfSignalCount; + U64 PrimativeSeqErrCount; + U64 InvalidTxWordCount; + U64 InvalidCrcCount; + U64 FcpInitiatorIoCount; +} fCONFIG_PAGE_FC_PORT_6, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_6, + FCPortPage6_t, MPI_POINTER pFCPortPage6_t; + +#define MPI_FCPORTPAGE6_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_FC_PORT_7 +{ + fCONFIG_PAGE_HEADER Header; + U32 Reserved; + U8 PortSymbolicName[256]; +} fCONFIG_PAGE_FC_PORT_7, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_7, + FCPortPage7_t, MPI_POINTER pFCPortPage7_t; + +#define MPI_FCPORTPAGE7_PAGEVERSION (0x00) + + +/****************************************************************************/ +/* FC Device Config Pages */ +/****************************************************************************/ + +typedef struct _CONFIG_PAGE_FC_DEVICE_0 +{ + fCONFIG_PAGE_HEADER Header; + U64 WWNN; + U64 WWPN; + U32 PortIdentifier; + U8 Protocol; + U8 Flags; + U16 BBCredit; + U16 MaxRxFrameSize; + U8 Reserved1; + U8 PortNumber; + U8 FcPhLowestVersion; + U8 FcPhHighestVersion; + U8 CurrentTargetID; + U8 CurrentBus; +} fCONFIG_PAGE_FC_DEVICE_0, MPI_POINTER PTR_CONFIG_PAGE_FC_DEVICE_0, + FCDevicePage0_t, MPI_POINTER pFCDevicePage0_t; + +#define MPI_FC_DEVICE_PAGE0_PAGEVERSION (0x02) + +#define MPI_FC_DEVICE_PAGE0_FLAGS_TARGETID_BUS_VALID (0x01) + +#define MPI_FC_DEVICE_PAGE0_PROT_IP (0x01) +#define MPI_FC_DEVICE_PAGE0_PROT_FCP_TARGET (0x02) +#define MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR (0x04) + +#define MPI_FC_DEVICE_PAGE0_PGAD_PORT_MASK (MPI_FC_DEVICE_PGAD_PORT_MASK) +#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_MASK (MPI_FC_DEVICE_PGAD_FORM_MASK) +#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_NEXT_DID (MPI_FC_DEVICE_PGAD_FORM_NEXT_DID) +#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_BUS_TID (MPI_FC_DEVICE_PGAD_FORM_BUS_TID) +#define MPI_FC_DEVICE_PAGE0_PGAD_DID_MASK (MPI_FC_DEVICE_PGAD_ND_DID_MASK) +#define MPI_FC_DEVICE_PAGE0_PGAD_BUS_MASK (MPI_FC_DEVICE_PGAD_BT_BUS_MASK) +#define MPI_FC_DEVICE_PAGE0_PGAD_BUS_SHIFT (MPI_FC_DEVICE_PGAD_BT_BUS_SHIFT) +#define MPI_FC_DEVICE_PAGE0_PGAD_TID_MASK (MPI_FC_DEVICE_PGAD_BT_TID_MASK) + + +/****************************************************************************/ +/* RAID Volume Config Pages */ +/****************************************************************************/ + +typedef struct _RAIDVOL2_EM_PHYS_ID +{ + U8 TargetID; + U8 Bus; + U8 IocNumber; + U8 PhysDiskNumber; + U8 Reserved[8]; + U8 PhysicalDiskIdentifier[16]; + U8 ProductId[16]; + U8 InfoOffset0; + U8 InfoSize0; + U8 InfoOffset1; + U8 InfoSize1; + U8 Info[32]; +} RAIDVOL2_EM_PHYS_ID, MPI_POINTER PTR_RAIDVOL2_EM_PHYS_ID, + RaidVol2EmPhysicalID_t, MPI_POINTER pRaidVol2EmPhysicalID_t; + +typedef struct _RAIDVOL2_EM_DISK_INFO +{ + U32 DiskStatus; + U32 DeviceSettings; + U16 ErrorCount; + U16 Reserved; + U8 ErrorCdbByte; + U8 ErrorSenseKey; + U8 ErrorASC; + U8 ErrorASCQ; + U16 SmartCount; + U8 SmartASC; + U8 SmartASCQ; +} RAIDVOL2_EM_DISK_INFO, MPI_POINTER PTR_RAIDVOL2_EM_DISK_INFO, + RaidVol2EmDiskInfo_t, MPI_POINTER pRaidVol2EmDiskInfo_t; + +/* RAID Volume 2 EM Physical Disk DiskStatus flags */ + +#define MPI_RAIDVOLPAGE2_PHYS_DISK_PRIMARY (0x00000001) +#define MPI_RAIDVOLPAGE2_PHYS_DISK_SECONDARY (0x00000002) +#define MPI_RAIDVOLPAGE2_PHYS_DISK_HOT_SPARE (0x00000004) +#define MPI_RAIDVOLPAGE2_PHYS_DISK_OUT_OF_SYNC (0x00000008) +#define MPI_RAIDVOLPAGE2_PHYS_DISK_OFFLINE (0x00000010) +#define MPI_RAIDVOLPAGE2_PHYS_DISK_NOT_RESPONDING (0x00000020) + +typedef struct _RAIDVOL2_EM_PHYSICAL_DISK +{ + RAIDVOL2_EM_PHYS_ID Id; + RAIDVOL2_EM_DISK_INFO Info; +} RAIDVOL2_EM_PHYSICAL_DISK, MPI_POINTER PTR_RAIDVOL2_EM_PHYSICAL_DISK, + RaidVol2EmPhysicalDisk_t, MPI_POINTER pRaidVol2EmPhysicalDisk_t; + +#define MPI_RAIDVOLPAGE2_MAX_DISKS (3) + +typedef struct _CONFIG_PAGE_RAID_VOL_2 +{ + fCONFIG_PAGE_HEADER Header; + U32 VolumeStatus; + U32 VolumeSettings; + U32 Reserved; + U64 MaxLba; + U32 BlockSize; + U8 InquirySize; + U8 NumPhysicalDisks; + U16 Reserved1; + U8 InquiryData[56]; + RAIDVOL2_EM_PHYSICAL_DISK EMPhysicalDisk[MPI_RAIDVOLPAGE2_MAX_DISKS]; +} fCONFIG_PAGE_RAID_VOL_2, MPI_POINTER PTR_CONFIG_PAGE_RAID_VOL_2, + RaidVolumePage2_t, MPI_POINTER pRaidVolumePage2_t; + +#define MPI_RAIDVOLPAGE2_PAGEVERSION (0x00) + +/* RAID Volume Page 2 VolumeStatus defines */ + +#define MPI_RAIDVOLPAGE2_STATUS_ENABLED (0x00000001) +#define MPI_RAIDVOLPAGE2_STATUS_QUIESCED (0x00000002) +#define MPI_RAIDVOLPAGE2_STATUS_RESYNC_IN_PROGRESS (0x00000004) +#define MPI_RAIDVOLPAGE2_STATUS_DEGRADED (0x00000008) + +/* RAID Volume Page 2 VolumeSettings defines */ + +#define MPI_RAIDVOLPAGE2_SETTING_WRITE_CACHING_ENABLE (0x00000001) +#define MPI_RAIDVOLPAGE2_SETTING_OFFLINE_ON_SMART (0x00000002) +#define MPI_RAIDVOLPAGE2_SETTING_AUTO_CONFIGURE (0x00000004) + + +/****************************************************************************/ +/* LAN Config Pages */ +/****************************************************************************/ + +typedef struct _CONFIG_PAGE_LAN_0 +{ + ConfigPageHeader_t Header; + U16 TxRxModes; + U16 Reserved; + U32 PacketPrePad; +} fCONFIG_PAGE_LAN_0, MPI_POINTER PTR_CONFIG_PAGE_LAN_0, + LANPage0_t, MPI_POINTER pLANPage0_t; + +#define MPI_LAN_PAGE0_PAGEVERSION (0x01) + +#define MPI_LAN_PAGE0_RETURN_LOOPBACK (0x0000) +#define MPI_LAN_PAGE0_SUPPRESS_LOOPBACK (0x0001) +#define MPI_LAN_PAGE0_LOOPBACK_MASK (0x0001) + +typedef struct _CONFIG_PAGE_LAN_1 +{ + ConfigPageHeader_t Header; + U16 Reserved; + U8 CurrentDeviceState; + U8 Reserved1; + U32 MinPacketSize; + U32 MaxPacketSize; + U32 HardwareAddressLow; + U32 HardwareAddressHigh; + U32 MaxWireSpeedLow; + U32 MaxWireSpeedHigh; + U32 BucketsRemaining; + U32 MaxReplySize; + U32 NegWireSpeedHigh; + U32 NegWireSpeedLow; +} fCONFIG_PAGE_LAN_1, MPI_POINTER PTR_CONFIG_PAGE_LAN_1, + LANPage1_t, MPI_POINTER pLANPage1_t; + +#define MPI_LAN_PAGE1_PAGEVERSION (0x03) + +#define MPI_LAN_PAGE1_DEV_STATE_RESET (0x00) +#define MPI_LAN_PAGE1_DEV_STATE_OPERATIONAL (0x01) + +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/lsi/mpi_fc.h linux.ac/drivers/message/fusion/lsi/mpi_fc.h --- linux.vanilla/drivers/message/fusion/lsi/mpi_fc.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/lsi/mpi_fc.h Tue Apr 3 23:12:52 2001 @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2000-2001 LSI Logic Corporation. + * + * + * Name: MPI_FC.H + * Title: MPI Fibre Channel messages and structures + * Creation Date: June 12, 2000 + * + * MPI Version: 01.01.05 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-12-00 01.00.02 Added _MSG_FC_ABORT_REPLY structure. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 12-04-00 01.01.02 Added messages for Common Transport Send and + * Primitive Send. + * 01-09-01 01.01.03 Modifed some of the new flags to have an MPI prefix + * and modified the FcPrimitiveSend flags. + * 01-25-01 01.01.04 Move InitiatorIndex in LinkServiceRsp reply to a larger + * field. + * Added FC_ABORT_TYPE_CT_SEND_REQUEST and + * FC_ABORT_TYPE_EXLINKSEND_REQUEST for FcAbort request. + * Added MPI_FC_PRIM_SEND_FLAGS_STOP_SEND. + * 02-20-01 01.01.05 Started using MPI_POINTER. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_FC_H +#define MPI_FC_H + + +/***************************************************************************** +* +* F C T a r g e t M o d e M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Link Service Buffer Post messages */ +/****************************************************************************/ + +typedef struct _MSG_LINK_SERVICE_BUFFER_POST_REQUEST +{ + U8 BufferPostFlags; /* 00h */ + U8 BufferCount; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved; /* 04h */ + U8 Reserved1; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + SGE_TRANS_SIMPLE_UNION SGL; +} MSG_LINK_SERVICE_BUFFER_POST_REQUEST, + MPI_POINTER PTR_MSG_LINK_SERVICE_BUFFER_POST_REQUEST, + LinkServiceBufferPostRequest_t, MPI_POINTER pLinkServiceBufferPostRequest_t; + +#define LINK_SERVICE_BUFFER_POST_FLAGS_PORT_MASK (0x01) + +typedef struct _WWNFORMAT +{ + U32 PortNameHigh; /* 00h */ + U32 PortNameLow; /* 04h */ + U32 NodeNameHigh; /* 08h */ + U32 NodeNameLow; /* 0Ch */ +} WWNFORMAT, + WwnFormat_t; + +/* Link Service Buffer Post Reply */ +typedef struct _MSG_LINK_SERVICE_BUFFER_POST_REPLY +{ + U8 Flags; /* 00h */ + U8 Reserved; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 PortNumber; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved2; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 TransferLength; /* 14h */ + U32 TransactionContext; /* 18h */ + U32 Rctl_Did; /* 1Ch */ + U32 Csctl_Sid; /* 20h */ + U32 Type_Fctl; /* 24h */ + U16 SeqCnt; /* 28h */ + U8 Dfctl; /* 2Ah */ + U8 SeqId; /* 2Bh */ + U16 Rxid; /* 2Ch */ + U16 Oxid; /* 2Eh */ + U32 Parameter; /* 30h */ + WWNFORMAT Wwn; /* 34h */ +} MSG_LINK_SERVICE_BUFFER_POST_REPLY, MPI_POINTER PTR_MSG_LINK_SERVICE_BUFFER_POST_REPLY, + LinkServiceBufferPostReply_t, MPI_POINTER pLinkServiceBufferPostReply_t; + +#define MPI_LS_BUF_POST_REPLY_FLAG_NO_RSP_NEEDED (0x80) + +#define MPI_FC_DID_MASK (0x00FFFFFF) +#define MPI_FC_DID_SHIFT (0) +#define MPI_FC_RCTL_MASK (0xFF000000) +#define MPI_FC_RCTL_SHIFT (24) +#define MPI_FC_SID_MASK (0x00FFFFFF) +#define MPI_FC_SID_SHIFT (0) +#define MPI_FC_CSCTL_MASK (0xFF000000) +#define MPI_FC_CSCTL_SHIFT (24) +#define MPI_FC_FCTL_MASK (0x00FFFFFF) +#define MPI_FC_FCTL_SHIFT (0) +#define MPI_FC_TYPE_MASK (0xFF000000) +#define MPI_FC_TYPE_SHIFT (24) + +/* obsolete name for the above */ +#define FCP_TARGET_DID_MASK (0x00FFFFFF) +#define FCP_TARGET_DID_SHIFT (0) +#define FCP_TARGET_RCTL_MASK (0xFF000000) +#define FCP_TARGET_RCTL_SHIFT (24) +#define FCP_TARGET_SID_MASK (0x00FFFFFF) +#define FCP_TARGET_SID_SHIFT (0) +#define FCP_TARGET_CSCTL_MASK (0xFF000000) +#define FCP_TARGET_CSCTL_SHIFT (24) +#define FCP_TARGET_FCTL_MASK (0x00FFFFFF) +#define FCP_TARGET_FCTL_SHIFT (0) +#define FCP_TARGET_TYPE_MASK (0xFF000000) +#define FCP_TARGET_TYPE_SHIFT (24) + + +/****************************************************************************/ +/* Link Service Response messages */ +/****************************************************************************/ + +typedef struct _MSG_LINK_SERVICE_RSP_REQUEST +{ + U8 RspFlags; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 Rctl_Did; /* 0Ch */ + U32 Csctl_Sid; /* 10h */ + U32 Type_Fctl; /* 14h */ + U16 SeqCnt; /* 18h */ + U8 Dfctl; /* 1Ah */ + U8 SeqId; /* 1Bh */ + U16 Rxid; /* 1Ch */ + U16 Oxid; /* 1Eh */ + U32 Parameter; /* 20h */ + SGE_SIMPLE_UNION SGL; /* 24h */ +} MSG_LINK_SERVICE_RSP_REQUEST, MPI_POINTER PTR_MSG_LINK_SERVICE_RSP_REQUEST, + LinkServiceRspRequest_t, MPI_POINTER pLinkServiceRspRequest_t; + +#define LINK_SERVICE_RSP_FLAGS_IMMEDIATE (0x80) +#define LINK_SERVICE_RSP_FLAGS_PORT_MASK (0x01) + + +/* Link Service Response Reply */ +typedef struct _MSG_LINK_SERVICE_RSP_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 InitiatorIndex; /* 14h */ +} MSG_LINK_SERVICE_RSP_REPLY, MPI_POINTER PTR_MSG_LINK_SERVICE_RSP_REPLY, + LinkServiceRspReply_t, MPI_POINTER pLinkServiceRspReply_t; + + +/****************************************************************************/ +/* Extended Link Service Send messages */ +/****************************************************************************/ + +typedef struct _MSG_EXLINK_SERVICE_SEND_REQUEST +{ + U8 SendFlags; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U32 MsgFlags_Did; /* 04h */ + U32 MsgContext; /* 08h */ + U32 ElsCommandCode; /* 0Ch */ + SGE_SIMPLE_UNION SGL; /* 10h */ +} MSG_EXLINK_SERVICE_SEND_REQUEST, MPI_POINTER PTR_MSG_EXLINK_SERVICE_SEND_REQUEST, + ExLinkServiceSendRequest_t, MPI_POINTER pExLinkServiceSendRequest_t; + +#define EX_LINK_SERVICE_SEND_DID_MASK (0x00FFFFFF) +#define EX_LINK_SERVICE_SEND_DID_SHIFT (0) +#define EX_LINK_SERVICE_SEND_MSGFLAGS_MASK (0xFF000000) +#define EX_LINK_SERVICE_SEND_MSGFLAGS_SHIFT (24) + + +/* Extended Link Service Send Reply */ +typedef struct _MSG_EXLINK_SERVICE_SEND_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 ResponseLength; /* 14h */ +} MSG_EXLINK_SERVICE_SEND_REPLY, MPI_POINTER PTR_MSG_EXLINK_SERVICE_SEND_REPLY, + ExLinkServiceSendReply_t, MPI_POINTER pExLinkServiceSendReply_t; + +/****************************************************************************/ +/* FC Abort messages */ +/****************************************************************************/ + +typedef struct _MSG_FC_ABORT_REQUEST +{ + U8 AbortFlags; /* 00h */ + U8 AbortType; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 TransactionContextToAbort; /* 0Ch */ +} MSG_FC_ABORT_REQUEST, MPI_POINTER PTR_MSG_FC_ABORT_REQUEST, + FcAbortRequest_t, MPI_POINTER pFcAbortRequest_t; + +#define FC_ABORT_FLAG_PORT_MASK (0x01) + +#define FC_ABORT_TYPE_ALL_FC_BUFFERS (0x00) +#define FC_ABORT_TYPE_EXACT_FC_BUFFER (0x01) +#define FC_ABORT_TYPE_CT_SEND_REQUEST (0x02) +#define FC_ABORT_TYPE_EXLINKSEND_REQUEST (0x03) + +/* FC Abort Reply */ +typedef struct _MSG_FC_ABORT_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_FC_ABORT_REPLY, MPI_POINTER PTR_MSG_FC_ABORT_REPLY, + FcAbortReply_t, MPI_POINTER pFcAbortReply_t; + + +/****************************************************************************/ +/* FC Common Transport Send messages */ +/****************************************************************************/ + +typedef struct _MSG_FC_COMMON_TRANSPORT_SEND_REQUEST +{ + U8 SendFlags; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U32 MsgFlags_Did; /* 04h */ + U32 MsgContext; /* 08h */ + U16 CTCommandCode; /* 0Ch */ + U8 FsType; /* 0Eh */ + U8 Reserved1; /* 0Fh */ + SGE_SIMPLE_UNION SGL; /* 10h */ +} MSG_FC_COMMON_TRANSPORT_SEND_REQUEST, + MPI_POINTER PTR_MSG_FC_COMMON_TRANSPORT_SEND_REQUEST, + FcCommonTransportSendRequest_t, MPI_POINTER pFcCommonTransportSendRequest_t; + +#define MPI_FC_CT_SEND_DID_MASK (0x00FFFFFF) +#define MPI_FC_CT_SEND_DID_SHIFT (0) +#define MPI_FC_CT_SEND_MSGFLAGS_MASK (0xFF000000) +#define MPI_FC_CT_SEND_MSGFLAGS_SHIFT (24) + + +/* FC Common Transport Send Reply */ +typedef struct _MSG_FC_COMMON_TRANSPORT_SEND_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 ResponseLength; /* 14h */ +} MSG_FC_COMMON_TRANSPORT_SEND_REPLY, MPI_POINTER PTR_MSG_FC_COMMON_TRANSPORT_SEND_REPLY, + FcCommonTransportSendReply_t, MPI_POINTER pFcCommonTransportSendReply_t; + + +/****************************************************************************/ +/* FC Primitive Send messages */ +/****************************************************************************/ + +typedef struct _MSG_FC_PRIMITIVE_SEND_REQUEST +{ + U8 SendFlags; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 FcPrimitive[4]; /* 0Ch */ +} MSG_FC_PRIMITIVE_SEND_REQUEST, MPI_POINTER PTR_MSG_FC_PRIMITIVE_SEND_REQUEST, + FcPrimitiveSendRequest_t, MPI_POINTER pFcPrimitiveSendRequest_t; + +#define MPI_FC_PRIM_SEND_FLAGS_PORT_MASK (0x01) +#define MPI_FC_PRIM_SEND_FLAGS_STOP_SEND (0x08) +#define MPI_FC_PRIM_SEND_FLAGS_SEND_ONCE (0x10) +#define MPI_FC_PRIM_SEND_FLAGS_SEND_AROUND (0x20) +#define MPI_FC_PRIM_SEND_FLAGS_UNTIL_FULL (0x40) +#define MPI_FC_PRIM_SEND_FLAGS_FOREVER (0x80) + +/* FC Primitive Send Reply */ +typedef struct _MSG_FC_PRIMITIVE_SEND_REPLY +{ + U8 SendFlags; /* 00h */ + U8 Reserved; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_FC_PRIMITIVE_SEND_REPLY, MPI_POINTER PTR_MSG_FC_PRIMITIVE_SEND_REPLY, + FcPrimitiveSendReply_t, MPI_POINTER pFcPrimitiveSendReply_t; + +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/lsi/mpi_history.txt linux.ac/drivers/message/fusion/lsi/mpi_history.txt --- linux.vanilla/drivers/message/fusion/lsi/mpi_history.txt Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/lsi/mpi_history.txt Tue Apr 3 17:54:47 2001 @@ -0,0 +1,237 @@ + + ============================== + MPI Header File Change History + ============================== + + Copyright (c) 2000-2001 LSI Logic Corporation. + + --------------------------------------- + Header Set Release Version: 01.01.08 + Header Set Release Date: 02-27-01 + --------------------------------------- + + Filename Current version Prior version + ---------- --------------- ------------- + mpi.h 01.01.06 01.01.05 + mpi_ioc.h 01.01.05 01.01.04 + mpi_cnfg.h 01.01.09 01.01.08 + mpi_init.h 01.01.03 01.01.03 + mpi_targ.h 01.01.03 01.01.03 + mpi_fc.h 01.01.05 01.01.05 + mpi_lan.h 01.01.02 01.01.02 + mpi_raid.h 01.01.01 none + mpi_type.h 01.01.02 01.01.02 + mpi_history.txt 01.01.08 01.01.07 + + + * Date Version Description + * -------- -------- ------------------------------------------------------ + +mpi.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH definition. + * 06-06-00 01.00.01 Update MPI_VERSION_MAJOR and MPI_VERSION_MINOR. + * 06-22-00 01.00.02 Added MPI_IOCSTATUS_LAN_ definitions. + * Removed LAN_SUSPEND function definition. + * Added MPI_MSGFLAGS_CONTINUATION_REPLY definition. + * 06-30-00 01.00.03 Added MPI_CONTEXT_REPLY_TYPE_LAN definition. + * Added MPI_GET/SET_CONTEXT_REPLY_TYPE macros. + * 07-27-00 01.00.04 Added MPI_FAULT_ definitions. + * Removed MPI_IOCSTATUS_MSG/DATA_XFER_ERROR definitions. + * Added MPI_IOCSTATUS_INTERNAL_ERROR definition. + * Added MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 12-04-00 01.01.02 Added new function codes. + * 01-09-01 01.01.03 Added more definitions to the system interface section + * Added MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT. + * 01-25-01 01.01.04 Changed MPI_VERSION_MINOR from 0x00 to 0x01. + * 02-20-01 01.01.05 Started using MPI_POINTER. + * Added defines for MPI_DIAG_PREVENT_IOC_BOOT and + * MPI_DIAG_CLEAR_FLASH_BAD_SIG. + * Obsoleted MPI_IOCSTATUS_TARGET_FC_ defines. + * 02-27-01 01.01.06 Removed MPI_HOST_INDEX_REGISTER define. + * Added function codes for RAID. + * -------------------------------------------------------------------------- + +mpi_ioc.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added _MSG_IOC_INIT_REPLY structure. + * 06-06-00 01.00.01 Added CurReplyFrameSize field to _MSG_IOC_FACTS_REPLY. + * 06-12-00 01.00.02 Added _MSG_PORT_ENABLE_REPLY structure. + * Added _MSG_EVENT_ACK_REPLY structure. + * Added _MSG_FW_DOWNLOAD_REPLY structure. + * Added _MSG_TOOLBOX_REPLY structure. + * 06-30-00 01.00.03 Added MaxLanBuckets to _PORT_FACT_REPLY structure. + * 07-27-00 01.00.04 Added _EVENT_DATA structure definitions for _SCSI, + * _LINK_STATUS, _LOOP_STATE and _LOGOUT. + * 08-11-00 01.00.05 Switched positions of MsgLength and Function fields in + * _MSG_EVENT_ACK_REPLY structure to match specification. + * 11-02-00 01.01.01 Original release for post 1.0 work + * Added a value for Manufacturer to WhoInit + * 12-04-00 01.01.02 Modified IOCFacts reply, added FWUpload messages, and + * removed toolbox message. + * 01-09-01 01.01.03 Added event enabled and disabled defines. + * Added structures for FwHeader and DataHeader. + * Added ImageType to FwUpload reply. + * 02-20-01 01.01.04 Started using MPI_POINTER. + * 02-27-01 01.01.05 Added event for RAID status change and its event data. + * Added IocNumber field to MSG_IOC_FACTS_REPLY. + * -------------------------------------------------------------------------- + +mpi_cnfg.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-08-00 01.00.02 Added _PAGEVERSION definitions for all pages. + * Added FcPhLowestVersion, FcPhHighestVersion, Reserved2 + * fields to FC_DEVICE_0 page, updated the page version. + * Changed _FREE_RUNNING_CLOCK to _PACING_TRANSFERS in + * SCSI_PORT_0, SCSI_DEVICE_0 and SCSI_DEVICE_1 pages + * and updated the page versions. + * Added _RESPONSE_ID_MASK definition to SCSI_PORT_1 + * page and updated the page version. + * Added Information field and _INFO_PARAMS_NEGOTIATED + * definitionto SCSI_DEVICE_0 page. + * 06-22-00 01.00.03 Removed batch controls from LAN_0 page and updated the + * page version. + * Added BucketsRemaining to LAN_1 page, redefined the + * state values, and updated the page version. + * Revised bus width definitions in SCSI_PORT_0, + * SCSI_DEVICE_0 and SCSI_DEVICE_1 pages. + * 06-30-00 01.00.04 Added MaxReplySize to LAN_1 page and updated the page + * version. + * Moved FC_DEVICE_0 PageAddress description to spec. + * 07-27-00 01.00.05 Corrected the SubsystemVendorID and SubsystemID field + * widths in IOC_0 page and updated the page version. + * 11-02-00 01.01.01 Original release for post 1.0 work + * Added Manufacturing pages, IO Unit Page 2, SCSI SPI + * Port Page 2, FC Port Page 4, FC Port Page 5 + * 12-04-00 01.01.03 Config page changes to match MPI rev 1.00.01. + * 12-05-00 01.01.04 Modified config page actions. + * 01-09-01 01.01.05 Added defines for page address formats. + * Data size for Manufacturing pages 2 and 3 no longer + * defined here. + * Io Unit Page 2 size is fixed at 4 adapters and some + * flags were changed. + * SCSI Port Page 2 Device Settings modified. + * New fields added to FC Port Page 0 and some flags + * cleaned up. + * Removed impedance flash from FC Port Page 1. + * Added FC Port pages 6 and 7. + * 01-25-01 01.01.06 Added MaxInitiators field to FcPortPage0. + * 01-29-01 01.01.07 Changed some defines to make them 32 character unique. + * Added some LinkType defines for FcPortPage0. + * 02-20-01 01.01.08 Started using MPI_POINTER. + * 02-27-01 01.01.09 Replaced MPI_CONFIG_PAGETYPE_SCSI_LUN with + * MPI_CONFIG_PAGETYPE_RAID_VOLUME. + * Added definitions and structures for IOC Page 2 and + * RAID Volume Page 2. + * -------------------------------------------------------------------------- + +mpi_init.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added SenseBufferLength to _MSG_SCSI_IO_REPLY. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-08-00 01.00.02 Added MPI_SCSI_RSP_INFO_ definitions. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 12-04-00 01.01.02 Added MPI_SCSIIO_CONTROL_NO_DISCONNECT. + * 02-20-01 01.01.03 Started using MPI_POINTER. + * -------------------------------------------------------------------------- + +mpi_targ.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-22-00 01.00.02 Added _MSG_TARGET_CMD_BUFFER_POST_REPLY structure. + * Corrected DECSRIPTOR typo to DESCRIPTOR. + * 11-02-00 01.01.01 Original release for post 1.0 work + * Modified target mode to use IoIndex instead of + * HostIndex and IocIndex. Added Alias. + * 01-09-01 01.01.02 Added defines for TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER + * and TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER. + * 02-20-01 01.01.03 Started using MPI_POINTER. + * Added structures for MPI_TARGET_SCSI_SPI_CMD_BUFFER and + * MPI_TARGET_FCP_CMD_BUFFER. + * -------------------------------------------------------------------------- + +mpi_fc.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-12-00 01.00.02 Added _MSG_FC_ABORT_REPLY structure. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 12-04-00 01.01.02 Added messages for Common Transport Send and + * Primitive Send. + * 01-09-01 01.01.03 Modifed some of the new flags to have an MPI prefix + * and modified the FcPrimitiveSend flags. + * 01-25-01 01.01.04 Move InitiatorIndex in LinkServiceRsp reply to a larger + * field. + * Added FC_ABORT_TYPE_CT_SEND_REQUEST and + * FC_ABORT_TYPE_EXLINKSEND_REQUEST for FcAbort request. + * Added MPI_FC_PRIM_SEND_FLAGS_STOP_SEND. + * 02-20-01 01.01.05 Started using MPI_POINTER. + * -------------------------------------------------------------------------- + +mpi_lan.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added LANStatus field to _MSG_LAN_SEND_REPLY. + * Added LANStatus field to _MSG_LAN_RECEIVE_POST_REPLY. + * Moved ListCount field in _MSG_LAN_RECEIVE_POST_REPLY. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-12-00 01.00.02 Added MPI_ to BUCKETSTATUS_ definitions. + * 06-22-00 01.00.03 Major changes to match new LAN definition in 1.0 spec. + * 06-30-00 01.00.04 Added Context Reply definitions per revised proposal. + * Changed transaction context usage to bucket/buffer. + * 07-05-00 01.00.05 Removed LAN_RECEIVE_POST_BUCKET_CONTEXT_MASK definition + * to lan private header file + * 11-02-00 01.01.01 Original release for post 1.0 work + * 02-20-01 01.01.02 Started using MPI_POINTER. + * -------------------------------------------------------------------------- + +mpi_raid.h + * 02-27-01 01.01.01 Original release for this file. + * -------------------------------------------------------------------------- + +mpi_type.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 02-20-01 01.01.02 Added define and ifdef for MPI_POINTER. + * -------------------------------------------------------------------------- + +mpi_history.txt Parts list history + +Filename 01.01.08 01.01.07 01.01.06 01.01.05 01.01.04 +---------- -------- -------- -------- -------- -------- +mpi.h 01.01.06 01.01.05 01.01.04 01.01.04 01.01.03 +mpi_ioc.h 01.01.05 01.01.04 01.01.03 01.01.03 01.01.03 +mpi_cnfg.h 01.01.09 01.01.08 01.01.07 01.01.06 01.01.05 +mpi_init.h 01.01.03 01.01.03 01.01.02 01.01.02 01.01.02 +mpi_targ.h 01.01.03 01.01.03 01.01.02 01.01.02 01.01.02 +mpi_fc.h 01.01.05 01.01.05 01.01.04 01.01.04 01.01.03 +mpi_lan.h 01.01.02 01.01.02 01.01.01 01.01.01 01.01.01 +mpi_raid.h 01.01.01 +mpi_type.h 01.01.02 01.01.02 01.01.01 01.01.01 01.01.01 + +Filename 01.01.03 01.01.02 01.01.01 01.00.07 01.00.06 01.00.05 +---------- -------- -------- -------- -------- -------- -------- +mpi.h 01.01.02 01.01.02 01.01.01 01.00.04 01.00.04 01.00.03 +mpi_ioc.h 01.01.02 01.01.02 01.01.01 01.00.05 01.00.04 01.00.03 +mpi_cnfg.h 01.01.04 01.01.03 01.01.01 01.00.05 01.00.05 01.00.04 +mpi_init.h 01.01.02 01.01.02 01.01.01 01.00.02 01.00.02 01.00.02 +mpi_targ.h 01.01.01 01.01.01 01.01.01 01.00.02 01.00.02 01.00.02 +mpi_fc.h 01.01.02 01.01.02 01.01.01 01.00.02 01.00.02 01.00.02 +mpi_lan.h 01.01.01 01.01.01 01.01.01 01.00.05 01.00.05 01.00.05 +mpi_type.h 01.01.01 01.01.01 01.01.01 01.00.01 01.00.01 01.00.01 + +Filename 01.00.04 01.00.03 01.00.02 01.00.01 00.10.02 00.10.01 +---------- -------- -------- -------- -------- -------- -------- +mpi.h 01.00.02 01.00.01 01.00.01 01.00.01 00.10.02 00.10.01 +mpi_ioc.h 01.00.02 01.00.02 01.00.01 01.00.01 00.10.02 00.10.01 +mpi_cnfg.h 01.00.03 01.00.02 01.00.02 01.00.01 00.10.01 00.10.01 +mpi_init.h 01.00.02 01.00.02 01.00.02 01.00.01 00.10.02 00.10.01 +mpi_targ.h 01.00.02 01.00.01 01.00.01 01.00.01 00.10.01 00.10.01 +mpi_fc.h 01.00.02 01.00.02 01.00.01 01.00.01 00.10.01 00.10.01 +mpi_lan.h 01.00.03 01.00.02 01.00.01 01.00.01 00.10.02 00.10.01 +mpi_type.h 01.00.01 01.00.01 01.00.01 01.00.01 00.10.01 00.10.01 + + + * -------------------------------------------------------------------------- + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/lsi/mpi_init.h linux.ac/drivers/message/fusion/lsi/mpi_init.h --- linux.vanilla/drivers/message/fusion/lsi/mpi_init.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/lsi/mpi_init.h Tue Apr 3 17:54:47 2001 @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2000-2001 LSI Logic Corporation. + * + * + * Name: MPI_INIT.H + * Title: MPI initiator mode messages and structures + * Creation Date: June 8, 2000 + * + * MPI Version: 01.01.03 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added SenseBufferLength to _MSG_SCSI_IO_REPLY. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-08-00 01.00.02 Added MPI_SCSI_RSP_INFO_ definitions. + * 11-02-00 01.01.01 Original release for post 1.0 work. + * 12-04-00 01.01.02 Added MPI_SCSIIO_CONTROL_NO_DISCONNECT. + * 02-20-01 01.01.03 Started using MPI_POINTER. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_INIT_H +#define MPI_INIT_H + + +/***************************************************************************** +* +* S C S I I n i t i a t o r M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* SCSI IO messages and assocaited structures */ +/****************************************************************************/ + +typedef struct _MSG_SCSI_IO_REQUEST +{ + U8 TargetID; + U8 Bus; + U8 ChainOffset; + U8 Function; + U8 CDBLength; + U8 SenseBufferLength; + U8 Reserved; + U8 MsgFlags; + U32 MsgContext; + U8 LUN[8]; + U32 Control; + U8 CDB[16]; + U32 DataLength; + U32 SenseBufferLowAddr; + SGE_IO_UNION SGL; +} MSG_SCSI_IO_REQUEST, MPI_POINTER PTR_MSG_SCSI_IO_REQUEST, + SCSIIORequest_t, MPI_POINTER pSCSIIORequest_t; + + +/* SCSIO MsgFlags bits */ + +#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH (0x01) +#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32 (0x00) +#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 (0x01) +#define MPI_SCSIIO_MSGFLGS_SENSE_LOCATION (0x02) +#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_HOST (0x00) +#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_IOC (0x02) + +/* SCSIIO LUN fields */ + +#define MPI_SCSIIO_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF) +#define MPI_SCSIIO_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000) +#define MPI_SCSIIO_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF) +#define MPI_SCSIIO_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000) +#define MPI_SCSIIO_LUN_LEVEL_1_WORD (0xFF00) +#define MPI_SCSIIO_LUN_LEVEL_1_DWORD (0x0000FF00) + +/* SCSIO Control bits */ + +#define MPI_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000) +#define MPI_SCSIIO_CONTROL_NODATATRANSFER (0x00000000) +#define MPI_SCSIIO_CONTROL_WRITE (0x01000000) +#define MPI_SCSIIO_CONTROL_READ (0x02000000) + +#define MPI_SCSIIO_CONTROL_ADDCDBLEN_MASK (0x3C000000) +#define MPI_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26) + +#define MPI_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700) +#define MPI_SCSIIO_CONTROL_SIMPLEQ (0x00000000) +#define MPI_SCSIIO_CONTROL_HEADOFQ (0x00000100) +#define MPI_SCSIIO_CONTROL_ORDEREDQ (0x00000200) +#define MPI_SCSIIO_CONTROL_ACAQ (0x00000400) +#define MPI_SCSIIO_CONTROL_UNTAGGED (0x00000500) +#define MPI_SCSIIO_CONTROL_NO_DISCONNECT (0x00000700) + +#define MPI_SCSIIO_CONTROL_TASKMANAGE_MASK (0x00FF0000) +#define MPI_SCSIIO_CONTROL_OBSOLETE (0x00800000) +#define MPI_SCSIIO_CONTROL_CLEAR_ACA_RSV (0x00400000) +#define MPI_SCSIIO_CONTROL_TARGET_RESET (0x00200000) +#define MPI_SCSIIO_CONTROL_LUN_RESET_RSV (0x00100000) +#define MPI_SCSIIO_CONTROL_RESERVED (0x00080000) +#define MPI_SCSIIO_CONTROL_CLR_TASK_SET_RSV (0x00040000) +#define MPI_SCSIIO_CONTROL_ABORT_TASK_SET (0x00020000) +#define MPI_SCSIIO_CONTROL_RESERVED2 (0x00010000) + + +/* SCSIIO reply structure */ +typedef struct _MSG_SCSI_IO_REPLY +{ + U8 TargetID; + U8 Bus; + U8 MsgLength; + U8 Function; + U8 CDBLength; + U8 SenseBufferLength; + U8 Reserved; + U8 MsgFlags; + U32 MsgContext; + U8 SCSIStatus; + U8 SCSIState; + U16 IOCStatus; + U32 IOCLogInfo; + U32 TransferCount; + U32 SenseCount; + U32 ResponseInfo; +} MSG_SCSI_IO_REPLY, MPI_POINTER PTR_MSG_SCSI_IO_REPLY, + SCSIIOReply_t, MPI_POINTER pSCSIIOReply_t; + + +/* SCSIIO Reply SCSIStatus values (SAM-2 status codes) */ + +#define MPI_SCSI_STATUS_SUCCESS (0x00) +#define MPI_SCSI_STATUS_CHECK_CONDITION (0x02) +#define MPI_SCSI_STATUS_CONDITION_MET (0x04) +#define MPI_SCSI_STATUS_BUSY (0x08) +#define MPI_SCSI_STATUS_INTERMEDIATE (0x10) +#define MPI_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14) +#define MPI_SCSI_STATUS_RESERVATION_CONFLICT (0x18) +#define MPI_SCSI_STATUS_COMMAND_TERMINATED (0x22) +#define MPI_SCSI_STATUS_TASK_SET_FULL (0x28) +#define MPI_SCSI_STATUS_ACA_ACTIVE (0x30) + + +/* SCSIIO Reply SCSIState values */ + +#define MPI_SCSI_STATE_AUTOSENSE_VALID (0x01) +#define MPI_SCSI_STATE_AUTOSENSE_FAILED (0x02) +#define MPI_SCSI_STATE_NO_SCSI_STATUS (0x04) +#define MPI_SCSI_STATE_TERMINATED (0x08) +#define MPI_SCSI_STATE_RESPONSE_INFO_VALID (0x10) + +/* SCSIIO Reply ResponseInfo values */ +/* (FCP-1 RSP_CODE values and SPI-3 Packetized Failure codes) */ + +#define MPI_SCSI_RSP_INFO_FUNCTION_COMPLETE (0x00000000) +#define MPI_SCSI_RSP_INFO_FCP_BURST_LEN_ERROR (0x01000000) +#define MPI_SCSI_RSP_INFO_CMND_FIELDS_INVALID (0x02000000) +#define MPI_SCSI_RSP_INFO_FCP_DATA_RO_ERROR (0x03000000) +#define MPI_SCSI_RSP_INFO_TASK_MGMT_UNSUPPORTED (0x04000000) +#define MPI_SCSI_RSP_INFO_TASK_MGMT_FAILED (0x05000000) +#define MPI_SCSI_RSP_INFO_SPI_LQ_INVALID_TYPE (0x06000000) + + +/****************************************************************************/ +/* SCSI Task Management messages */ +/****************************************************************************/ + +typedef struct _MSG_SCSI_TASK_MGMT +{ + U8 TargetID; + U8 Bus; + U8 ChainOffset; + U8 Function; + U8 Reserved; + U8 TaskType; + U8 Reserved1; + U8 MsgFlags; + U32 MsgContext; + U8 LUN[8]; + U32 Reserved2[7]; + U32 TaskMsgContext; +} MSG_SCSI_TASK_MGMT, MPI_POINTER PTR_SCSI_TASK_MGMT, + SCSITaskMgmt_t, MPI_POINTER pSCSITaskMgmt_t; + +/* TaskType values */ + +#define MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x00000001) +#define MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x00000002) +#define MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x00000003) +#define MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS (0x00000004) + +/* MsgFlags bits */ +#define MPI_SCSITASKMGMT_MSGFLAGS_LIP_RESET_OPTION (0x00000002) + +/* SCSI Task Management Reply */ +typedef struct _MSG_SCSI_TASK_MGMT_REPLY +{ + U8 TargetID; + U8 Bus; + U8 MsgLength; + U8 Function; + U8 Reserved; + U8 TaskType; + U8 Reserved1; + U8 MsgFlags; + U32 MsgContext; + U8 Reserved2[2]; + U16 IOCStatus; + U32 IOCLogInfo; + U32 TerminationCount; +} MSG_SCSI_TASK_MGMT_REPLY, MPI_POINTER PTR_MSG_SCSI_TASK_MGMT_REPLY, + SCSITaskMgmtReply_t, MPI_POINTER pSCSITaskMgmtReply_t; + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/lsi/mpi_ioc.h linux.ac/drivers/message/fusion/lsi/mpi_ioc.h --- linux.vanilla/drivers/message/fusion/lsi/mpi_ioc.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/lsi/mpi_ioc.h Tue Apr 3 17:54:47 2001 @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2000-2001 LSI Logic Corporation. + * + * + * Name: MPI_IOC.H + * Title: MPI IOC, Port, Event, FW Load, and ToolBox messages + * Creation Date: August 11, 2000 + * + * MPI Version: 01.01.05 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added _MSG_IOC_INIT_REPLY structure. + * 06-06-00 01.00.01 Added CurReplyFrameSize field to _MSG_IOC_FACTS_REPLY. + * 06-12-00 01.00.02 Added _MSG_PORT_ENABLE_REPLY structure. + * Added _MSG_EVENT_ACK_REPLY structure. + * Added _MSG_FW_DOWNLOAD_REPLY structure. + * Added _MSG_TOOLBOX_REPLY structure. + * 06-30-00 01.00.03 Added MaxLanBuckets to _PORT_FACT_REPLY structure. + * 07-27-00 01.00.04 Added _EVENT_DATA structure definitions for _SCSI, + * _LINK_STATUS, _LOOP_STATE and _LOGOUT. + * 08-11-00 01.00.05 Switched positions of MsgLength and Function fields in + * _MSG_EVENT_ACK_REPLY structure to match specification. + * 11-02-00 01.01.01 Original release for post 1.0 work. + * Added a value for Manufacturer to WhoInit. + * 12-04-00 01.01.02 Modified IOCFacts reply, added FWUpload messages, and + * removed toolbox message. + * 01-09-01 01.01.03 Added event enabled and disabled defines. + * Added structures for FwHeader and DataHeader. + * Added ImageType to FwUpload reply. + * 02-20-01 01.01.04 Started using MPI_POINTER. + * 02-27-01 01.01.05 Added event for RAID status change and its event data. + * Added IocNumber field to MSG_IOC_FACTS_REPLY. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_IOC_H +#define MPI_IOC_H + + +/***************************************************************************** +* +* I O C M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* IOCInit message */ +/****************************************************************************/ + +typedef struct _MSG_IOC_INIT +{ + U8 WhoInit; + U8 Reserved; + U8 ChainOffset; + U8 Function; + U8 Flags; + U8 MaxDevices; + U8 MaxBuses; + U8 MsgFlags; + U32 MsgContext; + U16 ReplyFrameSize; + U8 Reserved1[2]; + U32 HostMfaHighAddr; + U32 SenseBufferHighAddr; +} MSG_IOC_INIT, MPI_POINTER PTR_MSG_IOC_INIT, + IOCInit_t, MPI_POINTER pIOCInit_t; + +typedef struct _MSG_IOC_INIT_REPLY +{ + U8 WhoInit; + U8 Reserved; + U8 MsgLength; + U8 Function; + U8 Flags; + U8 MaxDevices; + U8 MaxBuses; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved2; + U16 IOCStatus; + U32 IOCLogInfo; +} MSG_IOC_INIT_REPLY, MPI_POINTER PTR_MSG_IOC_INIT_REPLY, + IOCInitReply_t, MPI_POINTER pIOCInitReply_t; + +/* WhoInit values */ + +#define MPI_WHOINIT_NO_ONE (0x00) +#define MPI_WHOINIT_SYSTEM_BIOS (0x01) +#define MPI_WHOINIT_ROM_BIOS (0x02) +#define MPI_WHOINIT_PCI_PEER (0x03) +#define MPI_WHOINIT_HOST_DRIVER (0x04) +#define MPI_WHOINIT_MANUFACTURER (0x05) + + +/****************************************************************************/ +/* IOC Facts message */ +/****************************************************************************/ + +typedef struct _MSG_IOC_FACTS +{ + U8 Reserved[2]; + U8 ChainOffset; + U8 Function; + U8 Reserved1[3]; + U8 MsgFlags; + U32 MsgContext; +} MSG_IOC_FACTS, MPI_POINTER PTR_IOC_FACTS, + IOCFacts_t, MPI_POINTER pIOCFacts_t; + +/* IOC Facts Reply */ + +typedef struct _MSG_IOC_FACTS_REPLY +{ + U16 MsgVersion; + U8 MsgLength; + U8 Function; + U16 Reserved; + U8 IOCNumber; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved2; + U16 IOCStatus; + U32 IOCLogInfo; + U8 MaxChainDepth; + U8 WhoInit; + U8 BlockSize; + U8 Flags; + U16 ReplyQueueDepth; + U16 RequestFrameSize; + U16 FWVersion; + U16 ProductID; + U32 CurrentHostMfaHighAddr; + U16 GlobalCredits; + U8 NumberOfPorts; + U8 EventState; + U32 CurrentSenseBufferHighAddr; + U16 CurReplyFrameSize; + U8 MaxDevices; + U8 MaxBuses; + U32 FWImageSize; + U32 DataImageSize; +} MSG_IOC_FACTS_REPLY, MPI_POINTER PTR_MSG_IOC_FACTS_REPLY, + IOCFactsReply_t, MPI_POINTER pIOCFactsReply_t; + +#define MPI_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00) +#define MPI_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF) + +#define MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT (0x01) +#define MPI_IOCFACTS_FLAGS_DATA_IMAGE_UPLOAD (0x02) + +#define MPI_IOCFACTS_EVENTSTATE_DISABLED (0x00) +#define MPI_IOCFACTS_EVENTSTATE_ENABLED (0x01) + + + +/***************************************************************************** +* +* P o r t M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Port Facts message and Reply */ +/****************************************************************************/ + +typedef struct _MSG_PORT_FACTS +{ + U8 Reserved[2]; + U8 ChainOffset; + U8 Function; + U8 Reserved1[2]; + U8 PortNumber; + U8 MsgFlags; + U32 MsgContext; +} MSG_PORT_FACTS, MPI_POINTER PTR_MSG_PORT_FACTS, + PortFacts_t, MPI_POINTER pPortFacts_t; + +typedef struct _MSG_PORT_FACTS_REPLY +{ + U16 Reserved; + U8 MsgLength; + U8 Function; + U16 Reserved1; + U8 PortNumber; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved2; + U16 IOCStatus; + U32 IOCLogInfo; + U8 Reserved3; + U8 PortType; + U16 MaxDevices; + U16 PortSCSIID; + U16 ProtocolFlags; + U16 MaxPostedCmdBuffers; + U16 MaxPersistentIDs; + U16 MaxLanBuckets; + U16 Reserved4; + U32 Reserved5; +} MSG_PORT_FACTS_REPLY, MPI_POINTER PTR_MSG_PORT_FACTS_REPLY, + PortFactsReply_t, MPI_POINTER pPortFactsReply_t; + + +/* PortTypes values */ + +#define MPI_PORTFACTS_PORTTYPE_INACTIVE (0x00) +#define MPI_PORTFACTS_PORTTYPE_SCSI (0x01) +#define MPI_PORTFACTS_PORTTYPE_FC (0x10) + +/* ProtocolFlags values */ + +#define MPI_PORTFACTS_PROTOCOL_LOGBUSADDR (0x01) +#define MPI_PORTFACTS_PROTOCOL_LAN (0x02) +#define MPI_PORTFACTS_PROTOCOL_TARGET (0x04) +#define MPI_PORTFACTS_PROTOCOL_INITIATOR (0x08) + + +/****************************************************************************/ +/* Port Enable Message */ +/****************************************************************************/ + +typedef struct _MSG_PORT_ENABLE +{ + U8 Reserved[2]; + U8 ChainOffset; + U8 Function; + U8 Reserved1[2]; + U8 PortNumber; + U8 MsgFlags; + U32 MsgContext; +} MSG_PORT_ENABLE, MPI_POINTER PTR_MSG_PORT_ENABLE, + PortEnable_t, MPI_POINTER pPortEnable_t; + +typedef struct _MSG_PORT_ENABLE_REPLY +{ + U8 Reserved[2]; + U8 MsgLength; + U8 Function; + U8 Reserved1[2]; + U8 PortNumber; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved2; + U16 IOCStatus; + U32 IOCLogInfo; +} MSG_PORT_ENABLE_REPLY, MPI_POINTER PTR_MSG_PORT_ENABLE_REPLY, + PortEnableReply_t, MPI_POINTER pPortEnableReply_t; + + +/***************************************************************************** +* +* E v e n t M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Event Notification messages */ +/****************************************************************************/ + +typedef struct _MSG_EVENT_NOTIFY +{ + U8 Switch; + U8 Reserved; + U8 ChainOffset; + U8 Function; + U8 Reserved1[3]; + U8 MsgFlags; + U32 MsgContext; +} MSG_EVENT_NOTIFY, MPI_POINTER PTR_MSG_EVENT_NOTIFY, + EventNotification_t, MPI_POINTER pEventNotification_t; + +/* Event Notification Reply */ + +typedef struct _MSG_EVENT_NOTIFY_REPLY +{ + U16 EventDataLength; + U8 MsgLength; + U8 Function; + U8 Reserved1[2]; + U8 AckRequired; + U8 MsgFlags; + U32 MsgContext; + U8 Reserved2[2]; + U16 IOCStatus; + U32 IOCLogInfo; + U32 Event; + U32 EventContext; + U32 Data[1]; +} MSG_EVENT_NOTIFY_REPLY, MPI_POINTER PTR_MSG_EVENT_NOTIFY_REPLY, + EventNotificationReply_t, MPI_POINTER pEventNotificationReply_t; + +/* Event Acknowledge */ + +typedef struct _MSG_EVENT_ACK +{ + U8 Reserved[2]; + U8 ChainOffset; + U8 Function; + U8 Reserved1[3]; + U8 MsgFlags; + U32 MsgContext; + U32 Event; + U32 EventContext; +} MSG_EVENT_ACK, MPI_POINTER PTR_MSG_EVENT_ACK, + EventAck_t, MPI_POINTER pEventAck_t; + +typedef struct _MSG_EVENT_ACK_REPLY +{ + U8 Reserved[2]; + U8 MsgLength; + U8 Function; + U8 Reserved1[3]; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved2; + U16 IOCStatus; + U32 IOCLogInfo; +} MSG_EVENT_ACK_REPLY, MPI_POINTER PTR_MSG_EVENT_ACK_REPLY, + EventAckReply_t, MPI_POINTER pEventAckReply_t; + + +/* Switch */ + +#define MPI_EVENT_NOTIFICATION_SWITCH_OFF (0x00) +#define MPI_EVENT_NOTIFICATION_SWITCH_ON (0x01) + +/* Event */ + +#define MPI_EVENT_NONE (0x00000000) +#define MPI_EVENT_LOG_DATA (0x00000001) +#define MPI_EVENT_STATE_CHANGE (0x00000002) +#define MPI_EVENT_UNIT_ATTENTION (0x00000003) +#define MPI_EVENT_IOC_BUS_RESET (0x00000004) +#define MPI_EVENT_EXT_BUS_RESET (0x00000005) +#define MPI_EVENT_RESCAN (0x00000006) +#define MPI_EVENT_LINK_STATUS_CHANGE (0x00000007) +#define MPI_EVENT_LOOP_STATE_CHANGE (0x00000008) +#define MPI_EVENT_LOGOUT (0x00000009) +#define MPI_EVENT_EVENT_CHANGE (0x0000000A) +#define MPI_EVENT_RAID_STATUS_CHANGE (0x0000000B) + +/* AckRequired field values */ + +#define MPI_EVENT_NOTIFICATION_ACK_NOT_REQUIRED (0x00) +#define MPI_EVENT_NOTIFICATION_ACK_REQUIRED (0x01) + +/* SCSI Event data for Port, Bus and Device forms) */ + +typedef struct _EVENT_DATA_SCSI +{ + U8 TargetID; + U8 BusPort; + U16 Reserved; +} EVENT_DATA_SCSI, MPI_POINTER PTR_EVENT_DATA_SCSI, + EventDataScsi_t, MPI_POINTER pEventDataScsi_t; + +/* MPI Link Status Change Event data */ + +typedef struct _EVENT_DATA_LINK_STATUS +{ + U8 State; + U8 Reserved; + U16 Reserved1; + U8 Reserved2; + U8 Port; + U16 Reserved3; +} EVENT_DATA_LINK_STATUS, MPI_POINTER PTR_EVENT_DATA_LINK_STATUS, + EventDataLinkStatus_t, MPI_POINTER pEventDataLinkStatus_t; + +#define MPI_EVENT_LINK_STATUS_FAILURE (0x00000000) +#define MPI_EVENT_LINK_STATUS_ACTIVE (0x00000001) + +/* MPI Loop State Change Event data */ + +typedef struct _EVENT_DATA_LOOP_STATE +{ + U8 Character4; + U8 Character3; + U8 Type; + U8 Reserved; + U8 Reserved1; + U8 Port; + U16 Reserved2; +} EVENT_DATA_LOOP_STATE, MPI_POINTER PTR_EVENT_DATA_LOOP_STATE, + EventDataLoopState_t, MPI_POINTER pEventDataLoopState_t; + +#define MPI_EVENT_LOOP_STATE_CHANGE_LIP (0x0001) +#define MPI_EVENT_LOOP_STATE_CHANGE_LPE (0x0002) +#define MPI_EVENT_LOOP_STATE_CHANGE_LPB (0x0003) + +/* MPI LOGOUT Event data */ + +typedef struct _EVENT_DATA_LOGOUT +{ + U32 NPortID; + U8 Reserved; + U8 Port; + U16 Reserved1; +} EVENT_DATA_LOGOUT, MPI_POINTER PTR_EVENT_DATA_LOGOUT, + EventDataLogout_t, MPI_POINTER pEventDataLogout_t; + +/* MPI RAID Status Change Event data */ + +typedef struct _EVENT_DATA_RAID_STATUS_CHANGE +{ + U8 VolumeTargetID; + U8 VolumeBus; + U8 ReasonCode; + U8 PhysDiskNum; + U8 ASC; + U8 ASCQ; + U16 Reserved; +} EVENT_DATA_RAID_STATUS_CHANGE, MPI_POINTER PTR_EVENT_DATA_RAID_STATUS_CHANGE, + MpiEventDataRaidStatusChange_t, MPI_POINTER pMpiEventDataRaidStatusChange_t; + + +/* MPI RAID Status Change Event data ReasonCode values */ + +#define MPI_EVENT_RAID_DATA_RC_VOLUME_OPTIMAL (0x00) +#define MPI_EVENT_RAID_DATA_RC_VOLUME_DEGRADED (0x01) +#define MPI_EVENT_RAID_DATA_RC_STARTED_RESYNC (0x02) +#define MPI_EVENT_RAID_DATA_RC_DISK_ADDED (0x03) +#define MPI_EVENT_RAID_DATA_RC_DISK_NOT_RESPONDING (0x04) +#define MPI_EVENT_RAID_DATA_RC_SMART_DATA (0x05) + + +/***************************************************************************** +* +* F i r m w a r e L o a d M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Firmware Download message and associated structures */ +/****************************************************************************/ + +typedef struct _MSG_FW_DOWNLOAD +{ + U8 ImageType; + U8 Reserved; + U8 ChainOffset; + U8 Function; + U8 Reserved1[3]; + U8 MsgFlags; + U32 MsgContext; + SGE_MPI_UNION SGL; +} MSG_FW_DOWNLOAD, MPI_POINTER PTR_MSG_FW_DOWNLOAD, + FWDownload_t, MPI_POINTER pFWDownload_t; + +#define MPI_FW_DOWNLOAD_ITYPE_RESERVED (0x00) +#define MPI_FW_DOWNLOAD_ITYPE_FW (0x01) +#define MPI_FW_DOWNLOAD_ITYPE_BIOS (0x02) + + +typedef struct _FWDownloadTCSGE +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 Reserved1; + U32 ImageOffset; + U32 ImageSize; +} FW_DOWNLOAD_TCSGE, MPI_POINTER PTR_FW_DOWNLOAD_TCSGE, + FWDownloadTCSGE_t, MPI_POINTER pFWDownloadTCSGE_t; + +/* Firmware Download reply */ +typedef struct _MSG_FW_DOWNLOAD_REPLY +{ + U8 ImageType; + U8 Reserved; + U8 MsgLength; + U8 Function; + U8 Reserved1[3]; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved2; + U16 IOCStatus; + U32 IOCLogInfo; +} MSG_FW_DOWNLOAD_REPLY, MPI_POINTER PTR_MSG_FW_DOWNLOAD_REPLY, + FWDownloadReply_t, MPI_POINTER pFWDownloadReply_t; + + +/****************************************************************************/ +/* Firmware Upload message and associated structures */ +/****************************************************************************/ + +typedef struct _MSG_FW_UPLOAD +{ + U8 ImageType; + U8 Reserved; + U8 ChainOffset; + U8 Function; + U8 Reserved1[3]; + U8 MsgFlags; + U32 MsgContext; + SGE_MPI_UNION SGL; +} MSG_FW_UPLOAD, MPI_POINTER PTR_MSG_FW_UPLOAD, + FWUpload_t, MPI_POINTER pFWUpload_t; + +#define MPI_FW_UPLOAD_ITYPE_FW_IOC_MEM (0x00) +#define MPI_FW_UPLOAD_ITYPE_FW_FLASH (0x01) +#define MPI_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02) +#define MPI_FW_UPLOAD_ITYPE_DATA_IOC_MEM (0x03) + +typedef struct _FWUploadTCSGE +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 Reserved1; + U32 ImageOffset; + U32 ImageSize; +} FW_UPLOAD_TCSGE, MPI_POINTER PTR_FW_UPLOAD_TCSGE, + FWUploadTCSGE_t, MPI_POINTER pFWUploadTCSGE_t; + +/* Firmware Upload reply */ +typedef struct _MSG_FW_UPLOAD_REPLY +{ + U8 ImageType; + U8 Reserved; + U8 MsgLength; + U8 Function; + U8 Reserved1[3]; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved2; + U16 IOCStatus; + U32 IOCLogInfo; + U32 ActualImageSize; +} MSG_FW_UPLOAD_REPLY, MPI_POINTER PTR_MSG_FW_UPLOAD_REPLY, + FWUploadReply_t, MPI_POINTER pFWUploadReply_t; + + +typedef struct _MPI_FW_HEADER +{ + U32 ArmBranchInstruction0; + U32 Signature0; + U32 Signature1; + U32 Signature2; + U32 ArmBranchInstruction1; + U32 ArmBranchInstruction2; + U32 Reserved; + U32 Checksum; + U16 VendorId; + U16 ProductId; + U16 FwVersion; + U16 Reserved1; + U32 SeqCodeVersion; + U32 ImageSize; + U32 Reserved2; + U32 LoadStartAddress; + U32 IopResetVectorValue; + U32 IopResetRegAddr; + U32 VersionNameWhat; + U8 VersionName[32]; + U32 VendorNameWhat; + U8 VendorName[32]; +} MPI_FW_HEADER, MPI_POINTER PTR_MPI_FW_HEADER, + MpiFwHeader_t, MPI_POINTER pMpiFwHeader_t; + +#define MPI_FW_HEADER_WHAT_SIGNATURE (0x29232840) + + +typedef struct _MPI_DATA_HEADER +{ + U32 Signature; + U16 FunctionNumber; + U16 Length; + U32 Checksum; + U32 LoadStartAddress; +} MPI_DATA_HEADER, MPI_POINTER PTR_MPI_DATA_HEADER, + MpiDataHeader_t, MPI_POINTER pMpiDataHeader_t; + +#define MPI_DATA_HEADER_SIGNATURE (0x43504147) + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/lsi/mpi_lan.h linux.ac/drivers/message/fusion/lsi/mpi_lan.h --- linux.vanilla/drivers/message/fusion/lsi/mpi_lan.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/lsi/mpi_lan.h Tue Apr 3 23:12:52 2001 @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2000-2001 LSI Logic Corporation. + * + * + * Name: MPI_LAN.H + * Title: MPI LAN messages and structures + * Creation Date: June 30, 2000 + * + * MPI Version: 01.01.02 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added LANStatus field to _MSG_LAN_SEND_REPLY. + * Added LANStatus field to _MSG_LAN_RECEIVE_POST_REPLY. + * Moved ListCount field in _MSG_LAN_RECEIVE_POST_REPLY. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-12-00 01.00.02 Added MPI_ to BUCKETSTATUS_ definitions. + * 06-22-00 01.00.03 Major changes to match new LAN definition in 1.0 spec. + * 06-30-00 01.00.04 Added Context Reply definitions per revised proposal. + * Changed transaction context usage to bucket/buffer. + * 07-05-00 01.00.05 Removed LAN_RECEIVE_POST_BUCKET_CONTEXT_MASK definition + * to lan private header file + * 11-02-00 01.01.01 Original release for post 1.0 work + * 02-20-01 01.01.02 Started using MPI_POINTER. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_LAN_H +#define MPI_LAN_H + + +/****************************************************************************** +* +* L A N M e s s a g e s +* +*******************************************************************************/ + +/* LANSend messages */ + +typedef struct _MSG_LAN_SEND_REQUEST +{ + U16 Reserved; + U8 ChainOffset; + U8 Function; + U16 Reserved2; + U8 PortNumber; + U8 MsgFlags; + U32 MsgContext; + SGE_MPI_UNION SG_List[1]; +} MSG_LAN_SEND_REQUEST, MPI_POINTER PTR_MSG_LAN_SEND_REQUEST, + LANSendRequest_t, MPI_POINTER pLANSendRequest_t; + + +typedef struct _MSG_LAN_SEND_REPLY +{ + U16 Reserved; + U8 MsgLength; + U8 Function; + U8 Reserved2; + U8 NumberOfContexts; + U8 PortNumber; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved3; + U16 IOCStatus; + U32 IOCLogInfo; + U32 BufferContext; +} MSG_LAN_SEND_REPLY, MPI_POINTER PTR_MSG_LAN_SEND_REPLY, + LANSendReply_t, MPI_POINTER pLANSendReply_t; + + +/* LANReceivePost */ + +typedef struct _MSG_LAN_RECEIVE_POST_REQUEST +{ + U16 Reserved; + U8 ChainOffset; + U8 Function; + U16 Reserved2; + U8 PortNumber; + U8 MsgFlags; + U32 MsgContext; + U32 BucketCount; + SGE_MPI_UNION SG_List[1]; +} MSG_LAN_RECEIVE_POST_REQUEST, MPI_POINTER PTR_MSG_LAN_RECEIVE_POST_REQUEST, + LANReceivePostRequest_t, MPI_POINTER pLANReceivePostRequest_t; + + +typedef struct _MSG_LAN_RECEIVE_POST_REPLY +{ + U16 Reserved; + U8 MsgLength; + U8 Function; + U8 Reserved2; + U8 NumberOfContexts; + U8 PortNumber; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved3; + U16 IOCStatus; + U32 IOCLogInfo; + U32 BucketsRemaining; + U32 PacketOffset; + U32 PacketLength; + U32 BucketContext[1]; +} MSG_LAN_RECEIVE_POST_REPLY, MPI_POINTER PTR_MSG_LAN_RECEIVE_POST_REPLY, + LANReceivePostReply_t, MPI_POINTER pLANReceivePostReply_t; + + +/* LANReset */ + +typedef struct _MSG_LAN_RESET_REQUEST +{ + U16 Reserved; + U8 ChainOffset; + U8 Function; + U16 Reserved2; + U8 PortNumber; + U8 MsgFlags; + U32 MsgContext; +} MSG_LAN_RESET_REQUEST, MPI_POINTER PTR_MSG_LAN_RESET_REQUEST, + LANResetRequest_t, MPI_POINTER pLANResetRequest_t; + + +typedef struct _MSG_LAN_RESET_REPLY +{ + U16 Reserved; + U8 MsgLength; + U8 Function; + U16 Reserved2; + U8 PortNumber; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved3; + U16 IOCStatus; + U32 IOCLogInfo; +} MSG_LAN_RESET_REPLY, MPI_POINTER PTR_MSG_LAN_RESET_REPLY, + LANResetReply_t, MPI_POINTER pLANResetReply_t; + + +/****************************************************************************/ +/* LAN Context Reply defines and macros */ +/****************************************************************************/ + +#define LAN_REPLY_PACKET_LENGTH_MASK (0x0000FFFF) +#define LAN_REPLY_PACKET_LENGTH_SHIFT (0) +#define LAN_REPLY_BUCKET_CONTEXT_MASK (0x07FF0000) +#define LAN_REPLY_BUCKET_CONTEXT_SHIFT (16) +#define LAN_REPLY_BUFFER_CONTEXT_MASK (0x07FFFFFF) +#define LAN_REPLY_BUFFER_CONTEXT_SHIFT (0) +#define LAN_REPLY_FORM_MASK (0x18000000) +#define LAN_REPLY_FORM_RECEIVE_SINGLE (0x00) +#define LAN_REPLY_FORM_RECEIVE_MULTIPLE (0x01) +#define LAN_REPLY_FORM_SEND_SINGLE (0x02) +#define LAN_REPLY_FORM_MESSAGE_CONTEXT (0x03) +#define LAN_REPLY_FORM_SHIFT (27) + +#define GET_LAN_PACKET_LENGTH(x) (((x) & LAN_REPLY_PACKET_LENGTH_MASK) \ + >> LAN_REPLY_PACKET_LENGTH_SHIFT) + +#define SET_LAN_PACKET_LENGTH(x, lth) \ + ((x) = ((x) & ~LAN_REPLY_PACKET_LENGTH_MASK) | \ + (((lth) << LAN_REPLY_PACKET_LENGTH_SHIFT) & \ + LAN_REPLY_PACKET_LENGTH_MASK)) + +#define GET_LAN_BUCKET_CONTEXT(x) (((x) & LAN_REPLY_BUCKET_CONTEXT_MASK) \ + >> LAN_REPLY_BUCKET_CONTEXT_SHIFT) + +#define SET_LAN_BUCKET_CONTEXT(x, ctx) \ + ((x) = ((x) & ~LAN_REPLY_BUCKET_CONTEXT_MASK) | \ + (((ctx) << LAN_REPLY_BUCKET_CONTEXT_SHIFT) & \ + LAN_REPLY_BUCKET_CONTEXT_MASK)) + +#define GET_LAN_BUFFER_CONTEXT(x) (((x) & LAN_REPLY_BUFFER_CONTEXT_MASK) \ + >> LAN_REPLY_BUFFER_CONTEXT_SHIFT) + +#define SET_LAN_BUFFER_CONTEXT(x, ctx) \ + ((x) = ((x) & ~LAN_REPLY_BUFFER_CONTEXT_MASK) | \ + (((ctx) << LAN_REPLY_BUFFER_CONTEXT_SHIFT) & \ + LAN_REPLY_BUFFER_CONTEXT_MASK)) + +#define GET_LAN_FORM(x) (((x) & LAN_REPLY_FORM_MASK) \ + >> LAN_REPLY_FORM_SHIFT) + +#define SET_LAN_FORM(x, frm) \ + ((x) = ((x) & ~LAN_REPLY_FORM_MASK) | \ + (((frm) << LAN_REPLY_FORM_SHIFT) & \ + LAN_REPLY_FORM_MASK)) + + +/****************************************************************************/ +/* LAN Current Device State defines */ +/****************************************************************************/ + +#define MPI_LAN_DEVICE_STATE_RESET (0x00) +#define MPI_LAN_DEVICE_STATE_OPERATIONAL (0x01) + + +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/lsi/mpi_targ.h linux.ac/drivers/message/fusion/lsi/mpi_targ.h --- linux.vanilla/drivers/message/fusion/lsi/mpi_targ.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/lsi/mpi_targ.h Tue Apr 3 17:54:47 2001 @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2000-2001 LSI Logic Corporation. + * + * + * Name: MPI_TARG.H + * Title: MPI Target mode messages and structures + * Creation Date: June 22, 2000 + * + * MPI Version: 01.01.03 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-22-00 01.00.02 Added _MSG_TARGET_CMD_BUFFER_POST_REPLY structure. + * Corrected DECSRIPTOR typo to DESCRIPTOR. + * 11-02-00 01.01.01 Original release for post 1.0 work + * Modified target mode to use IoIndex instead of + * HostIndex and IocIndex. Added Alias. + * 01-09-01 01.01.02 Added defines for TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER + * and TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER. + * 02-20-01 01.01.03 Started using MPI_POINTER. + * Added structures for MPI_TARGET_SCSI_SPI_CMD_BUFFER and + * MPI_TARGET_FCP_CMD_BUFFER. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_TARG_H +#define MPI_TARG_H + + +/****************************************************************************** +* +* S C S I T a r g e t M e s s a g e s +* +*******************************************************************************/ + +typedef struct _CMD_BUFFER_DESCRIPTOR +{ + U16 IoIndex; + U16 Reserved; + union + { + U32 PhysicalAddress32; + U64 PhysicalAddress64; + } u; +} CMD_BUFFER_DESCRIPTOR, MPI_POINTER PTR_CMD_BUFFER_DESCRIPTOR, + CmdBufferDescriptor_t, MPI_POINTER pCmdBufferDescriptor_t; + + +/****************************************************************************/ +/* Target Command Buffer Post Request */ +/****************************************************************************/ + +typedef struct _MSG_TARGET_CMD_BUFFER_POST_REQUEST +{ + U8 BufferPostFlags; + U8 BufferCount; + U8 ChainOffset; + U8 Function; + U8 BufferLength; + U8 Reserved; + U8 Reserved1; + U8 MsgFlags; + U32 MsgContext; + CMD_BUFFER_DESCRIPTOR Buffer[1]; +} MSG_TARGET_CMD_BUFFER_POST_REQUEST, MPI_POINTER PTR_MSG_TARGET_CMD_BUFFER_POST_REQUEST, + TargetCmdBufferPostRequest_t, MPI_POINTER pTargetCmdBufferPostRequest_t; + +#define CMD_BUFFER_POST_FLAGS_PORT_MASK (0x01) +#define CMD_BUFFER_POST_FLAGS_ADDR_MODE_MASK (0x80) +#define CMD_BUFFER_POST_FLAGS_ADDR_MODE_32 (0) +#define CMD_BUFFER_POST_FLAGS_ADDR_MODE_64 (1) +#define CMD_BUFFER_POST_FLAGS_64_BIT_ADDR (0x80) + +#define CMD_BUFFER_POST_IO_INDEX_MASK (0x00003FFF) + + +typedef struct _MSG_TARGET_CMD_BUFFER_POST_REPLY +{ + U8 BufferPostFlags; + U8 BufferCount; + U8 MsgLength; + U8 Function; + U8 BufferLength; + U8 Reserved; + U8 Reserved1; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved2; + U16 IOCStatus; + U32 IOCLogInfo; +} MSG_TARGET_CMD_BUFFER_POST_REPLY, MPI_POINTER PTR_MSG_TARGET_CMD_BUFFER_POST_REPLY, + TargetCmdBufferPostReply_t, MPI_POINTER pTargetCmdBufferPostReply_t; + + +typedef struct _MSG_PRIORITY_CMD_RECEIVED_REPLY +{ + U16 Reserved; + U8 MsgLength; + U8 Function; + U16 Reserved1; + U8 Reserved2; + U8 MsgFlags; + U32 MsgContext; + U8 PriorityReason; + U8 Reserved3; + U16 IOCStatus; + U32 IOCLogInfo; + U32 ReplyWord; +} MSG_PRIORITY_CMD_RECEIVED_REPLY, MPI_POINTER PTR_MSG_PRIORITY_CMD_RECEIVED_REPLY, + PriorityCommandReceivedReply_t, MPI_POINTER pPriorityCommandReceivedReply_t; + +#define PRIORITY_REASON_NO_DISCONNECT (0x00) +#define PRIORITY_REASON_SCSI_TASK_MANAGEMENT (0x01) +#define PRIORITY_REASON_UNKNOWN (0xFF) + + +typedef struct _MSG_TARGET_CMD_BUFFER_POST_ERROR_REPLY +{ + U16 Reserved; + U8 MsgLength; + U8 Function; + U16 Reserved1; + U8 Reserved2; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved3; + U16 IOCStatus; + U32 IOCLogInfo; + U32 ReplyWord; +} MSG_TARGET_CMD_BUFFER_POST_ERROR_REPLY, + MPI_POINTER PTR_MSG_TARGET_CMD_BUFFER_POST_ERROR_REPLY, + TargetCmdBufferPostErrorReply_t, MPI_POINTER pTargetCmdBufferPostErrorReply_t; + + +typedef struct _MPI_TARGET_FCP_CMD_BUFFER +{ + U8 FcpLun[8]; + U8 FcpCntl[4]; + U8 FcpCdb[16]; + U32 FcpDl; +} MPI_TARGET_FCP_CMD_BUFFER, MPI_POINTER PTR_MPI_TARGET_FCP_CMD_BUFFER, + MpiTargetFcpCmdBuffer, MPI_POINTER pMpiTargetFcpCmdBuffer; + + +typedef struct _MPI_TARGET_SCSI_SPI_CMD_BUFFER +{ + /* SPI L_Q information unit */ + U8 L_QType; + U8 Reserved; + U16 Tag; + U8 LogicalUnitNumber[8]; + U32 DataLength; + /* SPI command information unit */ + U8 ReservedFirstByteOfCommandIU; + U8 TaskAttribute; + U8 TaskManagementFlags; + U8 AdditionalCDBLength; + U8 CDB[16]; +} MPI_TARGET_SCSI_SPI_CMD_BUFFER, + MPI_POINTER PTR_MPI_TARGET_SCSI_SPI_CMD_BUFFER, + MpiTargetScsiSpiCmdBuffer, MPI_POINTER pMpiTargetScsiSpiCmdBuffer; + + +/****************************************************************************/ +/* Target Assist Request */ +/****************************************************************************/ + +typedef struct _MSG_TARGET_ASSIST_REQUEST +{ + U8 StatusCode; + U8 TargetAssistFlags; + U8 ChainOffset; + U8 Function; + U16 QueueTag; + U8 Reserved; + U8 MsgFlags; + U32 MsgContext; + U32 ReplyWord; + U8 LUN[8]; + U32 RelativeOffset; + U32 DataLength; + SGE_IO_UNION SGL[1]; +} MSG_TARGET_ASSIST_REQUEST, MPI_POINTER PTR_MSG_TARGET_ASSIST_REQUEST, + TargetAssistRequest_t, MPI_POINTER pTargetAssistRequest_t; + +#define TARGET_ASSIST_FLAGS_DATA_DIRECTION (0x01) +#define TARGET_ASSIST_FLAGS_AUTO_STATUS (0x02) +#define TARGET_ASSIST_FLAGS_HIGH_PRIORITY (0x04) +#define TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER (0x80) + + +typedef struct _MSG_TARGET_ERROR_REPLY +{ + U16 Reserved; + U8 MsgLength; + U8 Function; + U16 Reserved1; + U8 Reserved2; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved3; + U16 IOCStatus; + U32 IOCLogInfo; + U32 ReplyWord; + U32 TransferCount; +} MSG_TARGET_ERROR_REPLY, MPI_POINTER PTR_MSG_TARGET_ERROR_REPLY, + TargetErrorReply_t, MPI_POINTER pTargetErrorReply_t; + + +/****************************************************************************/ +/* Target Status Send Request */ +/****************************************************************************/ + +typedef struct _MSG_TARGET_STATUS_SEND_REQUEST +{ + U8 StatusCode; + U8 StatusFlags; + U8 ChainOffset; + U8 Function; + U16 QueueTag; + U8 Reserved; + U8 MsgFlags; + U32 MsgContext; + U32 ReplyWord; + U8 LUN[8]; + SGE_SIMPLE_UNION StatusDataSGE; +} MSG_TARGET_STATUS_SEND_REQUEST, MPI_POINTER PTR_MSG_TARGET_STATUS_SEND_REQUEST, + TargetStatusSendRequest_t, MPI_POINTER pTargetStatusSendRequest_t; + +#define TARGET_STATUS_SEND_FLAGS_AUTO_GOOD_STATUS (0x01) +#define TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER (0x80) + + +/****************************************************************************/ +/* Target Mode Abort Request */ +/****************************************************************************/ + +typedef struct _MSG_TARGET_MODE_ABORT_REQUEST +{ + U8 AbortType; + U8 Reserved; + U8 ChainOffset; + U8 Function; + U16 Reserved1; + U8 Reserved2; + U8 MsgFlags; + U32 MsgContext; + U32 ReplyWord; + U32 MsgContextToAbort; +} MSG_TARGET_MODE_ABORT, MPI_POINTER PTR_MSG_TARGET_MODE_ABORT, + TargetModeAbort_t, MPI_POINTER pTargetModeAbort_t; + +#define TARGET_MODE_ABORT_TYPE_ALL_CMD_BUFFERS (0x00) +#define TARGET_MODE_ABORT_TYPE_ALL_IO (0x01) +#define TARGET_MODE_ABORT_TYPE_EXACT_IO (0x02) +#define TARGET_MODE_ABORT_TYPE_EXACT_IO_REQUEST (0x03) + +/* Target Mode Abort Reply */ + +typedef struct _MSG_TARGET_MODE_ABORT_REPLY +{ + U16 Reserved; + U8 MsgLength; + U8 Function; + U16 Reserved1; + U8 Reserved2; + U8 MsgFlags; + U32 MsgContext; + U16 Reserved3; + U16 IOCStatus; + U32 IOCLogInfo; + U32 AbortCount; +} MSG_TARGET_MODE_ABORT_REPLY, MPI_POINTER PTR_MSG_TARGET_MODE_ABORT_REPLY, + TargetModeAbortReply_t, MPI_POINTER pTargetModeAbortReply_t; + + +/****************************************************************************/ +/* Target Mode Context Reply */ +/****************************************************************************/ + +#define TARGET_MODE_REPLY_IO_INDEX_MASK (0x00003FFF) +#define TARGET_MODE_REPLY_IO_INDEX_SHIFT (0) +#define TARGET_MODE_REPLY_INITIATOR_INDEX_MASK (0x03FFC000) +#define TARGET_MODE_REPLY_INITIATOR_INDEX_SHIFT (14) +#define TARGET_MODE_REPLY_ALIAS_MASK (0x0C000000) +#define TARGET_MODE_REPLY_ALIAS_SHIFT (26) +#define TARGET_MODE_REPLY_PORT_MASK (0x10000000) +#define TARGET_MODE_REPLY_PORT_SHIFT (28) + + +#define GET_IO_INDEX(x) (((x) & TARGET_MODE_REPLY_IO_INDEX_MASK) \ + >> TARGET_MODE_REPLY_IO_INDEX_SHIFT) + +#define SET_IO_INDEX(t, i) \ + ((t) = ((t) & ~TARGET_MODE_REPLY_IO_INDEX_MASK) | \ + (((i) << TARGET_MODE_REPLY_IO_INDEX_SHIFT) & \ + TARGET_MODE_REPLY_IO_INDEX_MASK)) + +#define GET_INITIATOR_INDEX(x) (((x) & TARGET_MODE_REPLY_INITIATOR_INDEX_MASK) \ + >> TARGET_MODE_REPLY_INITIATOR_INDEX_SHIFT) + +#define SET_INITIATOR_INDEX(t, ii) \ + ((t) = ((t) & ~TARGET_MODE_REPLY_INITIATOR_INDEX_MASK) | \ + (((ii) << TARGET_MODE_REPLY_INITIATOR_INDEX_SHIFT) & \ + TARGET_MODE_REPLY_INITIATOR_INDEX_MASK)) + +#define GET_ALIAS(x) (((x) & TARGET_MODE_REPLY_ALIAS_MASK) \ + >> TARGET_MODE_REPLY_ALIAS_SHIFT) + +#define SET_ALIAS(t, a) ((t) = ((t) & ~TARGET_MODE_REPLY_ALIAS_MASK) | \ + (((a) << TARGET_MODE_REPLY_ALIAS_SHIFT) & \ + TARGET_MODE_REPLY_ALIAS_MASK)) + +#define GET_PORT(x) (((x) & TARGET_MODE_REPLY_PORT_MASK) \ + >> TARGET_MODE_REPLY_PORT_SHIFT) + +#define SET_PORT(t, p) ((t) = ((t) & ~TARGET_MODE_REPLY_PORT_MASK) | \ + (((p) << TARGET_MODE_REPLY_PORT_SHIFT) & \ + TARGET_MODE_REPLY_PORT_MASK)) + + +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/lsi/mpi_type.h linux.ac/drivers/message/fusion/lsi/mpi_type.h --- linux.vanilla/drivers/message/fusion/lsi/mpi_type.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/lsi/mpi_type.h Tue Apr 3 17:54:47 2001 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2000-2001 LSI Logic Corporation. + * + * + * Name: MPI_TYPE.H + * Title: MPI Basic type definitions + * Creation Date: June 6, 2000 + * + * MPI Version: 01.01.02 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 02-20-01 01.01.02 Added define and ifdef for MPI_POINTER. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_TYPE_H +#define MPI_TYPE_H + + +/******************************************************************************* + * Define MPI_POINTER if it hasn't already been defined. By default MPI_POINTER + * is defined to be a near pointer. MPI_POINTER can be defined as a far pointer + * by defining MPI_POINTER as "far *" before this header file is included. + */ +#ifndef MPI_POINTER +#define MPI_POINTER * +#endif + + +/***************************************************************************** +* +* B a s i c T y p e s +* +*****************************************************************************/ + +typedef signed char S8; +typedef unsigned char U8; +typedef signed short S16; +typedef unsigned short U16; + + +#if defined(unix) || defined(__arm) || defined(ALPHA) + + typedef signed int S32; + typedef unsigned int U32; + +#else + + typedef signed long S32; + typedef unsigned long U32; + +#endif + + +typedef struct _S64 +{ + U32 Low; + S32 High; +} S64; + +typedef struct _U64 +{ + U32 Low; + U32 High; +} U64; + + +/****************************************************************************/ +/* Pointers */ +/****************************************************************************/ + +typedef S8 *PS8; +typedef U8 *PU8; +typedef S16 *PS16; +typedef U16 *PU16; +typedef S32 *PS32; +typedef U32 *PU32; +typedef S64 *PS64; +typedef U64 *PU64; + + +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/mptbase.c linux.ac/drivers/message/fusion/mptbase.c --- linux.vanilla/drivers/message/fusion/mptbase.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/mptbase.c Tue Apr 3 23:12:52 2001 @@ -0,0 +1,3360 @@ +/* + * linux/drivers/message/fusion/mptbase.c + * High performance SCSI + LAN / Fibre Channel device drivers. + * This is the Fusion MPT base driver which supports multiple + * (SCSI + LAN) specialized protocol drivers. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Credits: + * There are lots of people not mentioned below that deserve credit + * and thanks but won't get it here - sorry in advance that you + * got overlooked. + * + * This driver would not exist if not for Alan Cox's development + * of the linux i2o driver. + * + * A special thanks to Noah Romer (LSI Logic) for tons of work + * and tough debugging on the LAN driver, especially early on;-) + * And to Roger Hickerson (LSI Logic) for tirelessly supporting + * this driver project. + * + * All manner of help from Stephen Shirron (LSI Logic): + * low-level FC analysis, debug + various fixes in FCxx firmware, + * initial port to alpha platform, various driver code optimizations, + * being a faithful sounding board on all sorts of issues & ideas, + * etc. + * + * A huge debt of gratitude is owed to David S. Miller (DaveM) + * for fixing much of the stupid and broken stuff in the early + * driver while porting to sparc64 platform. THANK YOU! + * + * Special thanks goes to the I2O LAN driver people at the + * University of Helsinki, who, unbeknownst to them, provided + * the inspiration and initial structure for this driver. + * + * A really huge debt of gratitude is owed to Eddie C. Dost + * for gobs of hard work fixing and optimizing LAN code. + * THANK YOU! + * + * Copyright (c) 1999-2001 LSI Logic Corporation + * Originally By: Steven J. Ralston + * (mailto:Steve.Ralston@lsil.com) + * + * $Id: mptbase.c,v 1.47 2001/03/22 10:32:23 sralston Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + 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; version 2 of the License. + + 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. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_MTRR +#include +#endif + +#include "mptbase.h" + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#define my_NAME "Fusion MPT base driver" +#define my_VERSION MPT_LINUX_VERSION_COMMON +#define MYNAM "mptbase" + +MODULE_AUTHOR(MODULEAUTHOR); +MODULE_DESCRIPTION(my_NAME); + +/* + * cmd line parameters + */ +MODULE_PARM(PortIo, "0-1i"); +MODULE_PARM_DESC(PortIo, "[0]=Use mmap, 1=Use port io"); +MODULE_PARM(HardReset, "0-1i"); +MODULE_PARM_DESC(HardReset, "0=Disable HardReset, [1]=Enable HardReset"); +static int PortIo = 0; +static int HardReset = 1; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Public data... + */ +int mpt_lan_index = 0; +int mpt_stm_index = 0; + +void *mpt_v_ASCQ_TablePtr = NULL; +const char **mpt_ScsiOpcodesPtr = NULL; +int mpt_ASCQ_TableSz = 0; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Private data... + */ + /* Adapter lookup table */ +static MPT_ADAPTER *mpt_adapters[MPT_MAX_ADAPTERS] = {0}; +static MPT_ADAPTER_TRACKER MptAdapters; + /* Callback lookup table */ +static MPT_CALLBACK MptCallbacks[MPT_MAX_PROTOCOL_DRIVERS]; + /* Protocol driver class lookup table */ +static int MptDriverClass[MPT_MAX_PROTOCOL_DRIVERS]; + /* Event handler lookup table */ +static MPT_EVHANDLER MptEvHandlers[MPT_MAX_PROTOCOL_DRIVERS]; + +static int FusionInitCalled = 0; +static int mpt_base_index = -1; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Forward protos... + */ +static void mpt_interrupt(int irq, void *bus_id, struct pt_regs *r); +static int mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply); + +static int mpt_adapter_install(struct pci_dev *pdev); +static void mpt_detect_929_bound_ports(MPT_ADAPTER *this, struct pci_dev *pdev); +static void mpt_adapter_disable(MPT_ADAPTER *ioc); +static void mpt_adapter_dispose(MPT_ADAPTER *ioc); + +static void MptDisplayIocCapabilities(MPT_ADAPTER *ioc); +static u32 GetIocState(MPT_ADAPTER *ioc, int cooked); +static int GetIocFacts(MPT_ADAPTER *ioc); +static int GetPortFacts(MPT_ADAPTER *ioc); +static int SendIocInit(MPT_ADAPTER *ioc); +static int SendPortEnable(MPT_ADAPTER *ioc, int portnum); +static int mpt_fc9x9_reset(MPT_ADAPTER *ioc); +static int KickStart(MPT_ADAPTER *ioc); +static int SendIocReset(MPT_ADAPTER *ioc, u8 reset_type); +static int PrimeIocFifos(MPT_ADAPTER *ioc); +static int HandShakeReqAndReply(MPT_ADAPTER *ioc, int reqBytes, u32 *req, int replyBytes, u16 *u16reply); +static int WaitForDoorbellAck(MPT_ADAPTER *ioc); +static int WaitForDoorbellInt(MPT_ADAPTER *ioc); +static int WaitForDoorbellReply(MPT_ADAPTER *ioc); +static int GetLanConfigPages(MPT_ADAPTER *ioc); +static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch); +static int SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp); + +static int procmpt_create(void); +#ifdef CONFIG_PROC_FS +static int procmpt_destroy(void); +#endif +static int procmpt_read_summary(char *page, char **start, off_t off, int count, int *eof, void *data); +static int procmpt_read_dbg(char *page, char **start, off_t off, int count, int *eof, void *data); +/*static int procmpt_info(char *buf, char **start, off_t offset, int len);*/ + +static int ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *evReply, int *evHandlers); +static void mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info); +static void mpt_sp_log_info(MPT_ADAPTER *ioc, u32 log_info); + +static struct proc_dir_entry *procmpt_root_dir = NULL; + +int fusion_init(void); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* 20000207 -sralston + * GRRRRR... IOSpace (port i/o) register access (for the 909) is back! + * 20000517 -sralston + * Let's trying going back to default mmap register access... + */ + +static inline u32 CHIPREG_READ32(volatile u32 *a) +{ + if (PortIo) + return inl((unsigned long)a); + else + return readl(a); +} + +static inline void CHIPREG_WRITE32(volatile u32 *a, u32 v) +{ + if (PortIo) + outl(v, (unsigned long)a); + else + writel(v, a); +} + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_interrupt - MPT adapter (IOC) specific interrupt handler. + * @irq: irq number (not used) + * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure + * @r: pt_regs pointer (not used) + * + * This routine is registered via the request_irq() kernel API call, + * and handles all interrupts generated from a specific MPT adapter + * (also referred to as a IO Controller or IOC). + * This routine must clear the interrupt from the adapter and does + * so by reading the reply FIFO. Multiple replies may be processed + * per single call to this routine; up to MPT_MAX_REPLIES_PER_ISR + * which is currently set to 32 in mptbase.h. + * + * This routine handles register-level access of the adapter but + * dispatches (calls) a protocol-specific callback routine to handle + * the protocol-specific details of the MPT request completion. + */ +static void +mpt_interrupt(int irq, void *bus_id, struct pt_regs *r) +{ + MPT_ADAPTER *ioc; + MPT_FRAME_HDR *mf; + MPT_FRAME_HDR *mr; + u32 pa; + u32 *m; + int req_idx; + int cb_idx; + int type; + int freeme; + int count = 0; + + ioc = bus_id; + + /* + * Drain the reply FIFO! + * + * NOTES: I've seen up to 10 replies processed in this loop, so far... + * Update: I've seen up to 9182 replies processed in this loop! ?? + * Update: Limit ourselves to processing max of N replies + * (bottom of loop). + */ + while (1) { + + if ((pa = CHIPREG_READ32(&ioc->chip->ReplyFifo)) == 0xFFFFFFFF) + return; + + cb_idx = 0; + freeme = 0; + + /* + * Check for non-TURBO reply! + */ + if (pa & MPI_ADDRESS_REPLY_A_BIT) { + dma_addr_t reply_dma_addr; + u16 ioc_stat; + + /* non-TURBO reply! Hmmm, something may be up... + * Newest turbo reply mechanism; get address + * via left shift 1 (get rid of MPI_ADDRESS_REPLY_A_BIT)! + */ + reply_dma_addr = (pa = (pa << 1)); + + /* Map DMA address of reply header to cpu address. */ + m = (u32 *) ((u8 *)ioc->reply_frames + + (reply_dma_addr - ioc->reply_frames_dma)); + + mr = (MPT_FRAME_HDR *) m; + req_idx = le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx); + cb_idx = mr->u.frame.hwhdr.msgctxu.fld.cb_idx; + mf = MPT_INDEX_2_MFPTR(ioc, req_idx); + + dprintk((KERN_INFO MYNAM ": %s: Got non-TURBO reply=%p\n", + ioc->name, mr)); + DBG_DUMP_REPLY_FRAME(mr) + + /* NEW! 20010301 -sralston + * Check/log IOC log info + */ + ioc_stat = le16_to_cpu(mr->u.reply.IOCStatus); + if (ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) { + u32 log_info = le32_to_cpu(mr->u.reply.IOCLogInfo); + if ((int)ioc->chip_type <= (int)FC929) + mpt_fc_log_info(ioc, log_info); + else + mpt_sp_log_info(ioc, log_info); + } + } else { + /* + * Process turbo (context) reply... + */ + dirqprintk((KERN_INFO MYNAM ": %s: Got TURBO reply(=%08x)\n", ioc->name, pa)); + type = (pa >> MPI_CONTEXT_REPLY_TYPE_SHIFT); + if (type == MPI_CONTEXT_REPLY_TYPE_SCSI_TARGET) { + cb_idx = mpt_stm_index; + mf = NULL; + mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa); + } else if (type == MPI_CONTEXT_REPLY_TYPE_LAN) { + cb_idx = mpt_lan_index; + /* + * BUG FIX! 20001218 -sralston + * Blind set of mf to NULL here was fatal + * after lan_reply says "freeme" + * Fix sort of combined with an optimization here; + * added explicit check for case where lan_reply + * was just returning 1 and doing nothing else. + * For this case skip the callback, but set up + * proper mf value first here:-) + */ + if ((pa & 0x58000000) == 0x58000000) { + req_idx = pa & 0x0000FFFF; + mf = MPT_INDEX_2_MFPTR(ioc, req_idx); + freeme = 1; + /* + * IMPORTANT! Invalidate the callback! + */ + cb_idx = 0; + } else { + mf = NULL; + } + mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa); + } else { + req_idx = pa & 0x0000FFFF; + cb_idx = (pa & 0x00FF0000) >> 16; + mf = MPT_INDEX_2_MFPTR(ioc, req_idx); + mr = NULL; + } + pa = 0; /* No reply flush! */ + } + + /* Check for (valid) IO callback! */ + if (cb_idx) { + /* Do the callback! */ + freeme = (*(MptCallbacks[cb_idx]))(ioc, mf, mr); + } + + if (pa) { + /* Flush (non-TURBO) reply with a WRITE! */ + CHIPREG_WRITE32(&ioc->chip->ReplyFifo, pa); + } + + if (freeme) { + unsigned long flags; + + /* Put Request back on FreeQ! */ + spin_lock_irqsave(&ioc->FreeQlock, flags); + Q_ADD_TAIL(&ioc->FreeQ, &mf->u.frame.linkage, MPT_FRAME_HDR); + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + } + + count++; + dirqprintk((KERN_INFO MYNAM ": %s: ISR processed frame #%d\n", ioc->name, count)); + mb(); + + if (count >= MPT_MAX_REPLIES_PER_ISR) { + dirqprintk((KERN_INFO MYNAM ": %s: ISR processed %d replies.", + ioc->name, count)); + dirqprintk((" Giving this ISR a break!\n")); + return; + } + + } /* drain reply FIFO */ +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_base_reply - MPT base driver's callback routine; all base driver + * "internal" request/reply processing is routed here. + * Currently used for EventNotification and EventAck handling. + * @ioc: Pointer to MPT_ADAPTER structure + * @mf: Pointer to original MPT request frame + * @reply: Pointer to MPT reply frame (NULL if TurboReply) + * + * Returns 1 indicating original alloc'd request frame ptr + * should be freed, or 0 if it shouldn't. + */ +static int +mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply) +{ + int freereq = 1; + u8 func; + + dprintk((KERN_INFO MYNAM ": %s: mpt_base_reply() called\n", ioc->name)); + + if ((mf == NULL) || + (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) { + printk(KERN_ERR MYNAM ": %s: ERROR - NULL or BAD request frame ptr! (=%p)\n", + ioc->name, mf); + return 1; + } + + if (reply == NULL) { + dprintk((KERN_ERR MYNAM ": %s: ERROR - Unexpected NULL Event (turbo?) reply!\n", + ioc->name)); + return 1; + } + + if (!(reply->u.hdr.MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)) { + dmfprintk((KERN_INFO MYNAM ": Original request frame (@%p) header\n", mf)); + DBG_DUMP_REQUEST_FRAME_HDR(mf) + } + + func = reply->u.hdr.Function; + dprintk((KERN_INFO MYNAM ": %s: mpt_base_reply, Function=%02Xh\n", + ioc->name, func)); + + if (func == MPI_FUNCTION_EVENT_NOTIFICATION) { + EventNotificationReply_t *pEvReply = (EventNotificationReply_t *) reply; + int evHandlers = 0; + int results; + + results = ProcessEventNotification(ioc, pEvReply, &evHandlers); + if (results != evHandlers) { + /* CHECKME! Any special handling needed here? */ + dprintk((KERN_WARNING MYNAM ": %s: Hmmm... Called %d event handlers, sum results = %d\n", + ioc->name, evHandlers, results)); + } + + /* + * Hmmm... It seems that EventNotificationReply is an exception + * to the rule of one reply per request. + */ + if (pEvReply->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY) + freereq = 0; +#ifdef CONFIG_PROC_FS +// LogEvent(ioc, pEvReply); +#endif + } else if (func == MPI_FUNCTION_EVENT_ACK) { + dprintk((KERN_INFO MYNAM ": %s: mpt_base_reply, EventAck reply received\n", + ioc->name)); + } else { + printk(KERN_ERR MYNAM ": %s: ERROR - Unexpected msg function (=%02Xh) reply received!\n", + ioc->name, func); + } + + /* + * Conditionally tell caller to free the original + * EventNotification/EventAck/unexpected request frame! + */ + return freereq; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_register - Register protocol-specific main callback handler. + * @cbfunc: callback function pointer + * @dclass: Protocol driver's class (%MPT_DRIVER_CLASS enum value) + * + * This routine is called by a protocol-specific driver (SCSI host, + * LAN, SCSI target) to register it's reply callback routine. Each + * protocol-specific driver must do this before it will be able to + * use any IOC resources, such as obtaining request frames. + * + * NOTES: The SCSI protocol driver currently calls this routine twice + * in order to register separate callbacks; one for "normal" SCSI IO + * and another for MptScsiTaskMgmt requests. + * + * Returns a positive integer valued "handle" in the + * range (and S.O.D. order) {7,6,...,1} if successful. + * Any non-positive return value (including zero!) should be considered + * an error by the caller. + */ +int +mpt_register(MPT_CALLBACK cbfunc, MPT_DRIVER_CLASS dclass) +{ + int r = -1; + int i; + +#ifndef MODULE + /* + * Handle possibility of the mptscsih_detect() routine getting + * called *before* fusion_init! + */ + if (!FusionInitCalled) { + dprintk((KERN_INFO MYNAM ": Hmmm, calling fusion_init from mpt_register!\n")); + /* + * NOTE! We'll get recursion here, as fusion_init() + * calls mpt_register()! + */ + fusion_init(); + FusionInitCalled++; + } +#endif + + /* + * Search for empty callback slot in this order: {7,6,...,1} + * (slot/handle 0 is reserved!) + */ + for (i = MPT_MAX_PROTOCOL_DRIVERS-1; i; i--) { + if (MptCallbacks[i] == NULL) { + MptCallbacks[i] = cbfunc; + MptDriverClass[i] = dclass; + MptEvHandlers[i] = NULL; + r = i; + if (cbfunc != mpt_base_reply) { + MOD_INC_USE_COUNT; + } + break; + } + } + + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_deregister - Deregister a protocol drivers resources. + * @cb_idx: previously registered callback handle + * + * Each protocol-specific driver should call this routine when it's + * module is unloaded. + */ +void +mpt_deregister(int cb_idx) +{ + if (cb_idx && (cb_idx < MPT_MAX_PROTOCOL_DRIVERS)) { + MptCallbacks[cb_idx] = NULL; + MptDriverClass[cb_idx] = MPTUNKNOWN_DRIVER; + MptEvHandlers[cb_idx] = NULL; + if (cb_idx != mpt_base_index) { + MOD_DEC_USE_COUNT; + } + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_event_register - Register protocol-specific event callback + * handler. + * @cb_idx: previously registered (via mpt_register) callback handle + * @ev_cbfunc: callback function + * + * This routine can be called by one or more protocol-specific drivers + * if/when they choose to be notified of MPT events. + * + * Returns 0 for success. + */ +int +mpt_event_register(int cb_idx, MPT_EVHANDLER ev_cbfunc) +{ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) + return -1; + + MptEvHandlers[cb_idx] = ev_cbfunc; + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_event_deregister - Deregister protocol-specific event callback + * handler. + * @cb_idx: previously registered callback handle + * + * Each protocol-specific driver should call this routine + * when it does not (or can no longer) handle events, + * or when it's module is unloaded. + */ +void +mpt_event_deregister(int cb_idx) +{ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) + return; + + MptEvHandlers[cb_idx] = NULL; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_get_msg_frame - Obtain a MPT request frame from the pool (of 1024) + * allocated per MPT adapter. + * @handle: Handle of registered MPT protocol driver + * @iocid: IOC unique identifier (integer) + * + * Returns pointer to a MPT request frame or %NULL if none are available. + */ +MPT_FRAME_HDR* +mpt_get_msg_frame(int handle, int iocid) +{ + MPT_FRAME_HDR *mf = NULL; + MPT_ADAPTER *iocp; + unsigned long flags; + + /* validate handle and ioc identifier */ + iocp = mpt_adapters[iocid]; + spin_lock_irqsave(&iocp->FreeQlock, flags); + if (! Q_IS_EMPTY(&iocp->FreeQ)) { + int req_offset; + + mf = iocp->FreeQ.head; + Q_DEL_ITEM(&mf->u.frame.linkage); + mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; /* byte */ + req_offset = (u8 *)mf - (u8 *)iocp->req_frames; + /* u16! */ + mf->u.frame.hwhdr.msgctxu.fld.req_idx = + cpu_to_le16(req_offset / iocp->req_sz); + mf->u.frame.hwhdr.msgctxu.fld.rsvd = 0; + } + spin_unlock_irqrestore(&iocp->FreeQlock, flags); + dmfprintk((KERN_INFO MYNAM ": %s: mpt_get_msg_frame(%d,%d), got mf=%p\n", + iocp->name, handle, iocid, mf)); + return mf; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_put_msg_frame - Send a protocol specific MPT request frame + * to a IOC. + * @handle: Handle of registered MPT protocol driver + * @iocid: IOC unique identifier (integer) + * @mf: Pointer to MPT request frame + * + * This routine posts a MPT request frame to the request post FIFO of a + * specific MPT adapter. + */ +void +mpt_put_msg_frame(int handle, int iocid, MPT_FRAME_HDR *mf) +{ + MPT_ADAPTER *iocp; + + iocp = mpt_adapters[iocid]; + if (iocp != NULL) { + dma_addr_t mf_dma_addr; + int req_offset; + + /* ensure values are reset properly! */ + mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; /* byte */ + req_offset = (u8 *)mf - (u8 *)iocp->req_frames; + /* u16! */ + mf->u.frame.hwhdr.msgctxu.fld.req_idx = cpu_to_le16(req_offset / iocp->req_sz); + mf->u.frame.hwhdr.msgctxu.fld.rsvd = 0; + +#ifdef MPT_DEBUG_MSG_FRAME + { + u32 *m = mf->u.frame.hwhdr.__hdr; + int i, n; + + printk(KERN_INFO MYNAM ": %s: About to Put msg frame @ %p:\n" KERN_INFO " ", + iocp->name, m); + n = iocp->req_sz/4 - 1; + while (m[n] == 0) + n--; + for (i=0; i<=n; i++) { + if (i && ((i%8)==0)) + printk("\n" KERN_INFO " "); + printk(" %08x", le32_to_cpu(m[i])); + } + printk("\n"); + } +#endif + + mf_dma_addr = iocp->req_frames_dma + req_offset; + CHIPREG_WRITE32(&iocp->chip->RequestFifo, mf_dma_addr); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_free_msg_frame - Place MPT request frame back on FreeQ. + * @handle: Handle of registered MPT protocol driver + * @iocid: IOC unique identifier (integer) + * @mf: Pointer to MPT request frame + * + * This routine places a MPT request frame back on the MPT adapter's + * FreeQ. + */ +void +mpt_free_msg_frame(int handle, int iocid, MPT_FRAME_HDR *mf) +{ + MPT_ADAPTER *iocp; + unsigned long flags; + + iocp = mpt_adapters[iocid]; + if (iocp != NULL) { + /* Put Request back on FreeQ! */ + spin_lock_irqsave(&iocp->FreeQlock, flags); + Q_ADD_TAIL(&iocp->FreeQ, &mf->u.frame.linkage, MPT_FRAME_HDR); + spin_unlock_irqrestore(&iocp->FreeQlock, flags); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_send_handshake_request - Send MPT request via doorbell + * handshake method. + * @handle: Handle of registered MPT protocol driver + * @iocid: IOC unique identifier (integer) + * @reqBytes: Size of the request in bytes + * @req: Pointer to MPT request frame + * + * This routine is used exclusively by mptscsih to send MptScsiTaskMgmt + * requests since they are required to be sent via doorbell handshake. + * + * NOTE: It is the callers responsibility to byte-swap fields in the + * request which are greater than 1 byte in size. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt_send_handshake_request(int handle, int iocid, int reqBytes, u32 *req) +{ + MPT_ADAPTER *iocp; + int r = 0; + + iocp = mpt_adapters[iocid]; + if (iocp != NULL) { + u8 *req_as_bytes; + int i; + + /* + * Emulate what mpt_put_msg_frame() does /wrt to sanity + * setting cb_idx/req_idx. But ONLY if this request + * is in proper (pre-alloc'd) request buffer range... + */ + i = MFPTR_2_MPT_INDEX(iocp,(MPT_FRAME_HDR*)req); + if (reqBytes >= 12 && i >= 0 && i < iocp->req_depth) { + MPT_FRAME_HDR *mf = (MPT_FRAME_HDR*)req; + mf->u.frame.hwhdr.msgctxu.fld.req_idx = cpu_to_le16(i); + mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; + } + + /* Make sure there are no doorbells */ + CHIPREG_WRITE32(&iocp->chip->IntStatus, 0); + + CHIPREG_WRITE32(&iocp->chip->Doorbell, + ((MPI_FUNCTION_HANDSHAKE<name, i)); + + CHIPREG_WRITE32(&iocp->chip->IntStatus, 0); + + if ((r = WaitForDoorbellAck(iocp)) < 0) + return -2; + + /* Send request via doorbell handshake */ + req_as_bytes = (u8 *) req; + for (i = 0; i < reqBytes/4; i++) { + u32 word; + + word = ((req_as_bytes[(i*4) + 0] << 0) | + (req_as_bytes[(i*4) + 1] << 8) | + (req_as_bytes[(i*4) + 2] << 16) | + (req_as_bytes[(i*4) + 3] << 24)); + CHIPREG_WRITE32(&iocp->chip->Doorbell, word); + if ((r = WaitForDoorbellAck(iocp)) < 0) { + r = -3; + break; + } + } + + /* Make sure there are no doorbells */ + CHIPREG_WRITE32(&iocp->chip->IntStatus, 0); + + } + + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_adapter_find_first - Find first MPT adapter pointer. + * + * Returns first MPT adapter pointer or %NULL if no MPT adapters + * are present. + */ +MPT_ADAPTER * +mpt_adapter_find_first(void) +{ + MPT_ADAPTER *this = NULL; + + if (! Q_IS_EMPTY(&MptAdapters)) + this = MptAdapters.head; + + return this; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_adapter_find_next - Find next MPT adapter pointer. + * @prev: Pointer to previous MPT adapter + * + * Returns next MPT adapter pointer or %NULL if there are no more. + */ +MPT_ADAPTER * +mpt_adapter_find_next(MPT_ADAPTER *prev) +{ + MPT_ADAPTER *next = NULL; + + if (prev && (prev->forw != (MPT_ADAPTER*)&MptAdapters.head)) + next = prev->forw; + + return next; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_pci_scan - Scan PCI devices for MPT adapters. + * + * Returns count of MPT adapters found, keying off of PCI vendor and + * device_id's. + */ +int __init +mpt_pci_scan(void) +{ + struct pci_dev *pdev; + struct pci_dev *pdev2; + int found = 0; + int count = 0; + int r; + + dprintk((KERN_INFO MYNAM ": Checking for MPT adapters...\n")); + + /* + * NOTE: The 929 (I believe) will appear as 2 separate PCI devices, + * one for each channel. + */ + pci_for_each_dev(pdev) { + pdev2 = NULL; + if (pdev->vendor != 0x1000) + continue; + + if ((pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC909) && + (pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC929) && +#if 0 + /* FIXME! FC919 */ + (pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC919) && + /* FIXME! C103x family */ + (pdev->device != MPI_MANUFACTPAGE_DEVID_53C1030) && + (pdev->device != MPI_MANUFACTPAGE_DEVID_53C1030_ZC) && + (pdev->device != MPI_MANUFACTPAGE_DEVID_53C1035) && +#endif + 1) { + dprintk((KERN_INFO MYNAM ": Skipping LSI device=%04xh\n", pdev->device)); + continue; + } + + /* GRRRRR + * 929 dual function devices may be presented in Func 1,0 order, + * but we'd really really rather have them in Func 0,1 order. + * Do some kind of look ahead here... + */ + if (pdev->devfn & 1) { + pdev2 = pci_peek_next_dev(pdev); + if (pdev2 && (pdev2->vendor == 0x1000) && + (PCI_SLOT(pdev2->devfn) == PCI_SLOT(pdev->devfn)) && + (pdev2->device == MPI_MANUFACTPAGE_DEVICEID_FC929) && + (pdev2->bus->number == pdev->bus->number) && + !(pdev2->devfn & 1)) { + dprintk((KERN_INFO MYNAM ": MPT adapter found: PCI bus/dfn=%02x/%02xh, class=%08x, id=%xh\n", + pdev2->bus->number, pdev2->devfn, pdev2->class, pdev2->device)); + found++; + if ((r = mpt_adapter_install(pdev2)) == 0) + count++; + } else { + pdev2 = NULL; + } + } + + dprintk((KERN_INFO MYNAM ": MPT adapter found: PCI bus/dfn=%02x/%02xh, class=%08x, id=%xh\n", + pdev->bus->number, pdev->devfn, pdev->class, pdev->device)); + found++; + if ((r = mpt_adapter_install(pdev)) == 0) + count++; + + if (pdev2) + pdev = pdev2; + } + + printk(KERN_INFO MYNAM ": %d MPT adapter%s found, %d installed.\n", + found, (found==1) ? "" : "s", count); + + if (count == 0) + return -ENODEV; + +#ifdef CONFIG_PROC_FS + if (procmpt_create() != 0) + printk(KERN_WARNING MYNAM ": WARNING! - %s creation failed!\n", + MPT_PROCFS_MPTBASEDIR); +#endif + + return count; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_verify_adapter - Given a unique IOC identifier, set pointer to + * the associated MPT adapter structure. + * @iocid: IOC unique identifier (integer) + * @iocpp: Pointer to pointer to IOC adapter + * + * Returns iocid and sets iocpp. + */ +int +mpt_verify_adapter(int iocid, MPT_ADAPTER **iocpp) +{ + MPT_ADAPTER *p; + + *iocpp = NULL; + if (iocid >= MPT_MAX_ADAPTERS) + return -1; + + p = mpt_adapters[iocid]; + if (p == NULL) + return -1; + + *iocpp = p; + return iocid; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_adapter_install - Install a PCI intelligent MPT adapter. + * @pdev: Pointer to pci_dev structure + * + * This routine performs all the steps necessary to bring the IOC of + * a MPT adapter to a OPERATIONAL state. This includes registering + * memory regions, registering the interrupt, and allocating request + * and reply memory pools. + * + * This routine also pre-fetches the LAN MAC address of a Fibre Channel + * MPT adapter. + * + * Returns 0 for success, non-zero for failure. + * + * TODO: Add support for polled controllers + */ +static int __init +mpt_adapter_install(struct pci_dev *pdev) +{ + MPT_ADAPTER *ioc; + char *myname; + u8 *mem; + unsigned long mem_phys; + unsigned long port; + u32 msize; + u32 psize; + u32 ioc_state; + int i; + int r = -ENODEV; + int cntdn; + int len; + int statefault = 0; + + ioc = kmalloc(sizeof(MPT_ADAPTER), GFP_KERNEL); + if (ioc == NULL) { + printk(KERN_ERR MYNAM ": ERROR - Insufficient memory to add adapter!\n"); + return -ENOMEM; + } + memset(ioc, 0, sizeof(*ioc)); + ioc->req_sz = MPT_REQ_SIZE; /* avoid div by zero! */ + ioc->alloc_total = sizeof(MPT_ADAPTER); + + ioc->pcidev = pdev; + + /* Find lookup slot. GRRRR... */ + for (i=0; i < MPT_MAX_ADAPTERS; i++) { + if (mpt_adapters[i] == NULL) { + ioc->id = i; /* Assign adapter unique id (lookup) */ + break; + } + } + if (i == MPT_MAX_ADAPTERS) { + printk(KERN_ERR MYNAM ": ERROR - mpt_adapters[%d] table overflow!\n", i); + kfree(ioc); + return -ENFILE; + } + + mem_phys = msize = 0; + port = psize = 0; + for (i=0; i < DEVICE_COUNT_RESOURCE; i++) { + if (pdev->PCI_BASEADDR_FLAGS(i) & PCI_BASE_ADDRESS_SPACE_IO) { + /* Get I/O space! */ + port = pdev->PCI_BASEADDR_START(i); + psize = PCI_BASEADDR_SIZE(pdev,i); + } else { + /* Get memmap */ + mem_phys = pdev->PCI_BASEADDR_START(i); + msize = PCI_BASEADDR_SIZE(pdev,i); + break; + } + } + ioc->mem_size = msize; + + if (i == DEVICE_COUNT_RESOURCE) { + printk(KERN_ERR MYNAM ": ERROR - MPT adapter has no memory regions defined!\n"); + kfree(ioc); + return -EINVAL; + } + + dprintk((KERN_INFO MYNAM ": MPT adapter @ %lx, msize=%dd bytes\n", mem_phys, msize)); + dprintk((KERN_INFO MYNAM ": (port i/o @ %lx, psize=%dd bytes)\n", port, psize)); + dprintk((KERN_INFO MYNAM ": Using %s register access method\n", PortIo ? "PortIo" : "MemMap")); + + mem = NULL; + if (! PortIo) { + /* Get logical ptr for PciMem0 space */ + /*mem = ioremap(mem_phys, msize);*/ + mem = ioremap(mem_phys, 0x100); + if (mem == NULL) { + printk(KERN_ERR MYNAM ": ERROR - Unable to map adapter memory!\n"); + kfree(ioc); + return -EINVAL; + } + ioc->memmap = mem; + } + dprintk((KERN_INFO MYNAM ": mem = %p, mem_phys = %lx\n", mem, mem_phys)); + + if (PortIo) { + u8 *pmem = (u8*)port; + ioc->mem_phys = port; + ioc->chip = (SYSIF_REGS*)pmem; + } else { + ioc->mem_phys = mem_phys; + ioc->chip = (SYSIF_REGS*)mem; + } + + ioc->chip_type = FCUNK; + if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC909) { + ioc->chip_type = FC909; + ioc->prod_name = "LSIFC909"; + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC929) { + ioc->chip_type = FC929; + ioc->prod_name = "LSIFC929"; + } +#if 0 + else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC919) { + ioc->chip_type = C1030; + ioc->prod_name = "LSIFC919"; + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_53C1030) { + ioc->chip_type = C1030; + ioc->prod_name = "LSI53C1030"; + } +#endif + + myname = "iocN"; + len = strlen(myname); + memcpy(ioc->name, myname, len+1); + ioc->name[len-1] = '0' + ioc->id; + + Q_INIT(&ioc->FreeQ, MPT_FRAME_HDR); + spin_lock_init(&ioc->FreeQlock); + + /* Disable all! */ + CHIPREG_WRITE32(&ioc->chip->IntMask, 0xFFFFFFFF); + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + ioc->active = 0; + + ioc->pci_irq = -1; + if (pdev->irq) { + r = request_irq(pdev->irq, mpt_interrupt, SA_SHIRQ, ioc->name, ioc); + + if (r < 0) { + printk(KERN_ERR MYNAM ": %s: ERROR - Unable to allocate interrupt %d!\n", + ioc->name, pdev->irq); + iounmap(mem); + kfree(ioc); + return -EBUSY; + } + + ioc->pci_irq = pdev->irq; + + pci_set_master(pdev); /* ?? */ + + dprintk((KERN_INFO MYNAM ": %s installed at interrupt %d\n", ioc->name, pdev->irq)); + } + + /* tack onto tail of our MPT adapter list */ + Q_ADD_TAIL(&MptAdapters, ioc, MPT_ADAPTER); + + /* Set lookup ptr. */ + mpt_adapters[ioc->id] = ioc; + + /* NEW! 20010220 -sralston + * Check for "929 bound ports" to reduce redundant resets. + */ + if (ioc->chip_type == FC929) + mpt_detect_929_bound_ports(ioc, pdev); + + /* Get current [raw] IOC state */ + ioc_state = GetIocState(ioc, 0); + dhsprintk((KERN_INFO MYNAM ": %s initial [raw] state=%08x\n", ioc->name, ioc_state)); + + /* + * Check to see if IOC got left/stuck in doorbell handshake + * grip of death. If so, hard reset the IOC. + */ + if (ioc_state & MPI_DOORBELL_ACTIVE) { + statefault = 1; + printk(KERN_WARNING MYNAM ": %s: Uh-oh, unexpected doorbell active!\n", + ioc->name); + } + + /* + * Check to see if IOC is in FAULT state. + * If so, hard reset the IOC. + */ + if ((ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) { + statefault = 2; + printk(KERN_WARNING MYNAM ": %s: Uh-oh, IOC is in FAULT state!!!\n", + ioc->name); + printk(KERN_WARNING " FAULT code = %04xh\n", + ioc_state & MPI_DOORBELL_DATA_MASK); + } + + if (HardReset || statefault) { + if ((r = KickStart(ioc)) != 0) { + r = -ENODEV; + goto ioc_up_fail; + } + } + + /* + * Loop here waiting for IOC to come READY. + */ + i = 0; + cntdn = HZ * 10; + while ((ioc_state = GetIocState(ioc, 1)) != MPI_IOC_STATE_READY) { + if (ioc_state == MPI_IOC_STATE_OPERATIONAL) { + /* + * BIOS or previous driver load left IOC in OP state. + * Reset messaging FIFOs. + */ + dprintk((KERN_WARNING MYNAM ": %s: Sending IOC msg unit reset!\n", ioc->name)); + if ((r = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET)) != 0) { + printk(KERN_ERR MYNAM ": %s: ERROR - IOC msg unit reset failed!\n", ioc->name); + r = -ENODEV; + goto ioc_up_fail; + } + } else if (ioc_state == MPI_IOC_STATE_RESET) { + /* + * Something is wrong. Try to get IOC back + * to a known state. + */ + dprintk((KERN_WARNING MYNAM ": %s: Sending IO unit reset!\n", ioc->name)); + if ((r = SendIocReset(ioc, MPI_FUNCTION_IO_UNIT_RESET)) != 0) { + printk(KERN_ERR MYNAM ": %s: ERROR - IO unit reset failed!\n", ioc->name); + r = -ENODEV; + goto ioc_up_fail; + } + } + + i++; cntdn--; + if (!cntdn) { + printk(KERN_ERR MYNAM ": %s: ERROR - Wait IOC_READY state timeout(%d)!\n", + ioc->name, (i+5)/HZ); + r = -ETIME; + goto ioc_up_fail; + } + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + if (statefault) { + printk(KERN_WARNING MYNAM ": %s: Whew! Recovered from %s\n", + ioc->name, statefault==1 ? "stuck handshake" : "IOC FAULT"); + } + + /* Enable! (reply interrupt) */ + CHIPREG_WRITE32(&ioc->chip->IntMask, ~(MPI_HIM_RIM)); + ioc->active = 1; + + /* Get IOC facts! (first time, ioc->facts0 and ioc->pfacts0) */ + if ((r = GetIocFacts(ioc)) != 0) + goto ioc_up_fail; + + /* Does IocFacts.EventState need any looking at / attention here? */ + + if ((r = SendIocInit(ioc)) != 0) + goto ioc_up_fail; + + /* + * Prime reply & request queues! + * (mucho alloc's) + */ + if ((r = PrimeIocFifos(ioc)) != 0) + goto ioc_up_fail; + + /* Get IOC facts again! (2nd time, ioc->factsN and ioc->pfactsN) */ + if ((r = GetIocFacts(ioc)) != 0) + goto ioc_up_fail; + + /* Does IocFacts.EventState need any looking at / attention here? */ + + MptDisplayIocCapabilities(ioc); + + if (ioc->pfacts0.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) { + /* + * Pre-fetch the ports LAN MAC address! + * (LANPage1_t stuff) + */ + (void) GetLanConfigPages(ioc); +#ifdef MPT_DEBUG + { + u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow; + dprintk((KERN_INFO MYNAM ": %s: LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + ioc->name, a[5], a[4], a[3], a[2], a[1], a[0] )); + } +#endif + } + + /* NEW! 20010120 -sralston + * Enable MPT base driver management of EventNotification + * and EventAck handling. + */ + (void) SendEventNotification(ioc, 1); /* 1=Enable EventNotification */ + + return 0; + +ioc_up_fail: + //Q_DEL_ITEM(ioc); + //mpt_adapter_dispose(ioc); + mpt_adapter_disable(ioc); + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_detect_929_bound_ports - Search for PCI bus/dev_function + * which matches PCI bus/dev_function (+/-1) for newly discovered 929. + * @ioc: Pointer to MPT adapter structure + * @pdev: Pointer to (struct pci_dev) structure + * + * If match on PCI dev_function +/-1 is found, bind the two MPT adapters + * using alt_ioc pointer fields in their %MPT_ADAPTER structures. + */ +static void +mpt_detect_929_bound_ports(MPT_ADAPTER *ioc, struct pci_dev *pdev) +{ + MPT_ADAPTER *ioc_srch = mpt_adapter_find_first(); + unsigned int match_lo, match_hi; + + match_lo = pdev->devfn-1; + match_hi = pdev->devfn+1; + dprintk((KERN_INFO MYNAM ": %s: PCI bus/devfn=%x/%x, searching for devfn match on %x or %x\n", + ioc->name, pdev->bus->number, pdev->devfn, match_lo, match_hi)); + + while (ioc_srch != NULL) { + struct pci_dev *_pcidev = ioc_srch->pcidev; + + if ( (_pcidev->device == MPI_MANUFACTPAGE_DEVICEID_FC929) && + (_pcidev->bus->number == pdev->bus->number) && + (_pcidev->devfn == match_lo || _pcidev->devfn == match_hi) ) { + /* Paranoia checks */ + if (ioc->alt_ioc != NULL) { + printk(KERN_WARNING MYNAM ": Oops, already bound (%s <==> %s)!\n", + ioc->name, ioc->alt_ioc->name); + break; + } else if (ioc_srch->alt_ioc != NULL) { + printk(KERN_WARNING MYNAM ": Oops, already bound (%s <==> %s)!\n", + ioc_srch->name, ioc_srch->alt_ioc->name); + break; + } + dprintk((KERN_INFO MYNAM ": FOUND! binding %s <==> %s\n", + ioc->name, ioc_srch->name)); + ioc_srch->alt_ioc = ioc; + ioc->alt_ioc = ioc_srch; + ioc->sod_reset = ioc->alt_ioc->sod_reset; + ioc->last_kickstart = ioc->alt_ioc->last_kickstart; + break; + } + ioc_srch = mpt_adapter_find_next(ioc_srch); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_adapter_disable - Disable misbehaving MPT adapter. + * @this: Pointer to MPT adapter structure + */ +static void +mpt_adapter_disable(MPT_ADAPTER *this) +{ + if (this != NULL) { + int sz; + + /* Disable adapter interrupts! */ + CHIPREG_WRITE32(&this->chip->IntMask, 0xFFFFFFFF); + /* Clear any lingering interrupt */ + CHIPREG_WRITE32(&this->chip->IntStatus, 0); + this->active = 0; + + if (this->reply_alloc != NULL) { + sz = (this->reply_sz * this->reply_depth) + 128; + pci_free_consistent(this->pcidev, sz, + this->reply_alloc, this->reply_alloc_dma); + this->reply_frames = NULL; + this->reply_alloc = NULL; + this->alloc_total -= sz; + } + + if (this->req_alloc != NULL) { + sz = (this->req_sz * this->req_depth) + 128; + /* + * Rounding UP to nearest 4-kB boundary here... + */ + sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000; + pci_free_consistent(this->pcidev, sz, + this->req_alloc, this->req_alloc_dma); + this->req_frames = NULL; + this->req_alloc = NULL; + this->alloc_total -= sz; + } + + if (this->sense_buf_pool != NULL) { + sz = (this->req_depth * 256); + pci_free_consistent(this->pcidev, sz, + this->sense_buf_pool, this->sense_buf_pool_dma); + this->sense_buf_pool = NULL; + this->alloc_total -= sz; + } + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_adapter_dispose - Free all resources associated with a MPT + * adapter. + * @this: Pointer to MPT adapter structure + * + * This routine unregisters h/w resources and frees all alloc'd memory + * associated with a MPT adapter structure. + */ +static void +mpt_adapter_dispose(MPT_ADAPTER *this) +{ + if (this != NULL) { + int sz_first, sz_last; + + sz_first = this->alloc_total; + + mpt_adapter_disable(this); + + if (this->pci_irq != -1) { + free_irq(this->pci_irq, this); + this->pci_irq = -1; + } + + if (this->memmap != NULL) + iounmap((u8 *) this->memmap); + +#if defined(CONFIG_MTRR) && 0 + if (this->mtrr_reg > 0) { + mtrr_del(this->mtrr_reg, 0, 0); + dprintk((KERN_INFO MYNAM ": %s: MTRR region de-registered\n", this->name)); + } +#endif + + /* Zap the adapter lookup ptr! */ + mpt_adapters[this->id] = NULL; + + sz_last = this->alloc_total; + dprintk((KERN_INFO MYNAM ": %s: free'd %d of %d bytes\n", + this->name, sz_first-sz_last+(int)sizeof(*this), sz_first)); + kfree(this); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * MptDisplayIocCapabilities - Disply IOC's capacilities. + * @ioc: Pointer to MPT adapter structure + */ +static void +MptDisplayIocCapabilities(MPT_ADAPTER *ioc) +{ + int i = 0; + + printk(KERN_INFO "%s: ", ioc->name); + if (ioc->prod_name && strlen(ioc->prod_name) > 3) + printk("%s: ", ioc->prod_name+3); + printk("Capabilities={"); + + if (ioc->pfacts0.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR) { + printk("Initiator"); + i++; + } + + if (ioc->pfacts0.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_TARGET) { + printk("%sTarget", i ? "," : ""); + i++; + } + + if (ioc->pfacts0.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) { + printk("%sLAN", i ? "," : ""); + i++; + } + +#if 0 + /* + * This would probably evoke more questions than it's worth + */ + if (ioc->pfacts0.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_TARGET) { + printk("%sLogBusAddr", i ? "," : ""); + i++; + } +#endif + + printk("}\n"); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * GetIocState - Get the current state of a MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @cooked: Request raw or cooked IOC state + * + * Returns all IOC Doorbell register bits if cooked==0, else just the + * Doorbell bits in MPI_IOC_STATE_MASK. + */ +static u32 +GetIocState(MPT_ADAPTER *ioc, int cooked) +{ + u32 s, sc; + + /* Get! */ + s = CHIPREG_READ32(&ioc->chip->Doorbell); + dprintk((KERN_INFO MYNAM ": %s: raw state = %08x\n", ioc->name, s)); + sc = s & MPI_IOC_STATE_MASK; + + /* Save! */ + ioc->last_state = sc; + + return cooked ? sc : s; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * GetIocFacts - Send IOCFacts request to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * + * Returns 0 for success, non-zero for failure. + */ +static int +GetIocFacts(MPT_ADAPTER *ioc) +{ + IOCFacts_t get_facts; + IOCFactsReply_t *facts; + int r; + int req_sz; + int reply_sz; + u32 status; + + /* IOC *must* NOT be in RESET state! */ + if (ioc->last_state == MPI_IOC_STATE_RESET) { + printk(KERN_ERR MYNAM ": ERROR - Can't get IOCFacts, %s NOT READY! (%08x)\n", + ioc->name, + ioc->last_state ); + return -44; + } + + facts = &ioc->facts0; + /* Nth (2,3,...) time thru? (been here, done that?) */ + if (ioc->facts0.Function == MPI_FUNCTION_IOC_FACTS) { + facts = &ioc->factsN; + } + + /* Destination (reply area)... */ + reply_sz = sizeof(*facts); + memset(facts, 0, reply_sz); + + /* Request area (get_facts on the stack right now!) */ + req_sz = sizeof(get_facts); + memset(&get_facts, 0, req_sz); + + get_facts.Function = MPI_FUNCTION_IOC_FACTS; + /* Assert: All other get_facts fields are zero! */ + + dprintk((KERN_INFO MYNAM ": %s: Sending IocFacts%s request\n", + ioc->name, facts == &ioc->facts0 ? "0" : "N" )); + + /* No non-zero fields in the get_facts request are greater than + * 1 byte in size, so we can just fire it off as is. + */ + r = HandShakeReqAndReply(ioc, + req_sz, (u32*)&get_facts, + reply_sz, (u16*)facts); + if (r != 0) + return r; + + /* + * Now byte swap the necessary fields before any further + * inspection of reply contents. + * + * But need to do some sanity checks on MsgLength (byte) field + * to make sure we don't zero IOC's req_sz! + */ + /* Did we get a valid reply? */ + if (facts->MsgLength > offsetof(IOCFactsReply_t, RequestFrameSize)/sizeof(u32)) { + facts->MsgVersion = le16_to_cpu(facts->MsgVersion); + facts->MsgContext = le32_to_cpu(facts->MsgContext); + facts->IOCStatus = le16_to_cpu(facts->IOCStatus); + facts->IOCLogInfo = le32_to_cpu(facts->IOCLogInfo); + status = facts->IOCStatus & MPI_IOCSTATUS_MASK; + /* CHECKME! IOCStatus, IOCLogInfo */ + + facts->ReplyQueueDepth = le16_to_cpu(facts->ReplyQueueDepth); + facts->RequestFrameSize = le16_to_cpu(facts->RequestFrameSize); + facts->FWVersion = le16_to_cpu(facts->FWVersion); + facts->ProductID = le16_to_cpu(facts->ProductID); + facts->CurrentHostMfaHighAddr = + le32_to_cpu(facts->CurrentHostMfaHighAddr); + facts->GlobalCredits = le16_to_cpu(facts->GlobalCredits); + facts->CurrentSenseBufferHighAddr = + le32_to_cpu(facts->CurrentSenseBufferHighAddr); + facts->CurReplyFrameSize = + le16_to_cpu(facts->CurReplyFrameSize); + + /* + * Handle NEW (!) IOCFactsReply fields in MPI-1.01.xx + * Older MPI-1.00.xx struct had 13 dwords, and enlarged + * to 14 in MPI-1.01.0x. + */ + if (facts->MsgLength >= sizeof(IOCFactsReply_t)/sizeof(u32) && facts->MsgVersion > 0x0100) { + facts->FWImageSize = le32_to_cpu(facts->FWImageSize); + facts->DataImageSize = le32_to_cpu(facts->DataImageSize); + } + + if (facts->RequestFrameSize) { + /* + * Set values for this IOC's REQUEST queue size & depth... + */ + ioc->req_sz = MIN(MPT_REQ_SIZE, facts->RequestFrameSize * 4); + + /* + * Set values for this IOC's REPLY queue size & depth... + * + * BUG? FIX? 20000516 -nromer & sralston + * GRRR... The following did not translate well from MPI v0.09: + * ioc->reply_sz = MIN(MPT_REPLY_SIZE, facts->ReplySize * 4); + * to 0.10: + * ioc->reply_sz = MIN(MPT_REPLY_SIZE, facts->BlockSize * 4); + * Was trying to minimally optimize to smallest possible reply size + * (and greatly reduce kmalloc size). But LAN may need larger reply? + * + * So for now, just set reply size to request size. FIXME? + */ + ioc->reply_sz = ioc->req_sz; + } else { + /* Something is wrong! */ + printk(KERN_ERR MYNAM ": %s: ERROR - IOC reported invalid 0 request size!\n", + ioc->name); + ioc->req_sz = MPT_REQ_SIZE; + ioc->reply_sz = MPT_REPLY_SIZE; + return -55; + } + ioc->req_depth = MIN(MPT_REQ_DEPTH, facts->GlobalCredits); + ioc->reply_depth = MIN(MPT_REPLY_DEPTH, facts->ReplyQueueDepth); + + dprintk((KERN_INFO MYNAM ": %s: reply_sz=%3d, reply_depth=%4d\n", + ioc->name, ioc->reply_sz, ioc->reply_depth)); + dprintk((KERN_INFO MYNAM ": %s: req_sz =%3d, req_depth =%4d\n", + ioc->name, ioc->req_sz, ioc->req_depth)); + + /* Get port facts! */ + if ( (r = GetPortFacts(ioc)) != 0 ) + return r; + } else { + printk(KERN_ERR MYNAM ": %s: ERROR - Invalid IOC facts reply!\n", + ioc->name); + return -66; + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * GetPortFacts - Send PortFacts request to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * + * Returns 0 for success, non-zero for failure. + */ +static int +GetPortFacts(MPT_ADAPTER *ioc) +{ + PortFacts_t get_pfacts; + PortFactsReply_t *pfacts; + int i; + int req_sz; + int reply_sz; + + /* IOC *must* NOT be in RESET state! */ + if (ioc->last_state == MPI_IOC_STATE_RESET) { + printk(KERN_ERR MYNAM ": ERROR - Can't get PortFacts, %s NOT READY! (%08x)\n", + ioc->name, + ioc->last_state ); + return -4; + } + + pfacts = &ioc->pfacts0; + /* Nth (2,3,...) time thru? (been here, done that?) */ + if (ioc->pfacts0.Function == MPI_FUNCTION_PORT_FACTS) { + pfacts = &ioc->pfactsN; + } + + /* Destination (reply area)... */ + reply_sz = sizeof(*pfacts); + memset(pfacts, 0, reply_sz); + + /* Request area (get_pfacts on the stack right now!) */ + req_sz = sizeof(get_pfacts); + memset(&get_pfacts, 0, req_sz); + + get_pfacts.Function = MPI_FUNCTION_PORT_FACTS; + /* Assert: All other get_pfacts fields are zero! */ + + dprintk((KERN_INFO MYNAM ": %s: Sending PortFacts%s request\n", + ioc->name, pfacts == &ioc->pfacts0 ? "0" : "N" )); + + /* No non-zero fields in the get_pfacts request are greater than + * 1 byte in size, so we can just fire it off as is. + */ + i = HandShakeReqAndReply(ioc, req_sz, (u32*)&get_pfacts, + reply_sz, (u16*)pfacts); + if (i != 0) + return i; + + /* Did we get a valid reply? */ + + /* Now byte swap the necessary fields in the response. */ + pfacts->MsgContext = le32_to_cpu(pfacts->MsgContext); + pfacts->IOCStatus = le16_to_cpu(pfacts->IOCStatus); + pfacts->IOCLogInfo = le32_to_cpu(pfacts->IOCLogInfo); + pfacts->MaxDevices = le16_to_cpu(pfacts->MaxDevices); + pfacts->PortSCSIID = le16_to_cpu(pfacts->PortSCSIID); + pfacts->ProtocolFlags = le16_to_cpu(pfacts->ProtocolFlags); + pfacts->MaxPostedCmdBuffers = le16_to_cpu(pfacts->MaxPostedCmdBuffers); + pfacts->MaxPersistentIDs = le16_to_cpu(pfacts->MaxPersistentIDs); + pfacts->MaxLanBuckets = le16_to_cpu(pfacts->MaxLanBuckets); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SendIocInit - Send IOCInit request to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * + * Send IOCInit followed by PortEnable to bring IOC to OPERATIONAL state. + * + * Returns 0 for success, non-zero for failure. + */ +static int +SendIocInit(MPT_ADAPTER *ioc) +{ + IOCInit_t ioc_init; + MPIDefaultReply_t init_reply; + u32 state; + int r; + int count; + int cntdn; + + memset(&ioc_init, 0, sizeof(ioc_init)); + memset(&init_reply, 0, sizeof(init_reply)); + + ioc_init.WhoInit = MPI_WHOINIT_HOST_DRIVER; +/* ioc_init.ChainOffset = 0; */ + ioc_init.Function = MPI_FUNCTION_IOC_INIT; +/* ioc_init.Flags = 0; */ + + /*ioc_init.MaxDevices = 16;*/ + ioc_init.MaxDevices = 255; +/* ioc_init.MaxBuses = 16; */ + ioc_init.MaxBuses = 1; + +/* ioc_init.MsgFlags = 0; */ +/* ioc_init.MsgContext = cpu_to_le32(0x00000000); */ + ioc_init.ReplyFrameSize = cpu_to_le16(ioc->reply_sz); /* in BYTES */ + ioc_init.HostMfaHighAddr = cpu_to_le32(0); /* Say we 32-bit! for now */ + + dprintk((KERN_INFO MYNAM ": %s: Sending IOCInit (req @ %p)\n", ioc->name, &ioc_init)); + + r = HandShakeReqAndReply(ioc, sizeof(IOCInit_t), (u32*)&ioc_init, + sizeof(MPIDefaultReply_t), (u16*)&init_reply); + if (r != 0) + return r; + + /* No need to byte swap the multibyte fields in the reply + * since we don't even look at it's contents. + */ + + if ((r = SendPortEnable(ioc, 0)) != 0) + return r; + + /* YIKES! SUPER IMPORTANT!!! + * Poll IocState until _OPERATIONAL while IOC is doing + * LoopInit and TargetDiscovery! + */ + count = 0; + cntdn = HZ * 60; /* chg'd from 30 to 60 seconds */ + state = GetIocState(ioc, 1); + while (state != MPI_IOC_STATE_OPERATIONAL && --cntdn) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + + if (!cntdn) { + printk(KERN_ERR MYNAM ": %s: ERROR - Wait IOC_OP state timeout(%d)!\n", + ioc->name, (count+5)/HZ); + return -9; + } + + state = GetIocState(ioc, 1); + count++; + } + dhsprintk((KERN_INFO MYNAM ": %s: INFO - Wait IOC_OPERATIONAL state (cnt=%d)\n", + ioc->name, count)); + + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SendPortEnable - Send PortEnable request to MPT adapter port. + * @ioc: Pointer to MPT_ADAPTER structure + * @portnum: Port number to enable + * + * Send PortEnable to bring IOC to OPERATIONAL state. + * + * Returns 0 for success, non-zero for failure. + */ +static int +SendPortEnable(MPT_ADAPTER *ioc, int portnum) +{ + PortEnable_t port_enable; + MPIDefaultReply_t reply_buf; + int i; + int req_sz; + int reply_sz; + + /* Destination... */ + reply_sz = sizeof(MPIDefaultReply_t); + memset(&reply_buf, 0, reply_sz); + + req_sz = sizeof(PortEnable_t); + memset(&port_enable, 0, req_sz); + + port_enable.Function = MPI_FUNCTION_PORT_ENABLE; + port_enable.PortNumber = portnum; +/* port_enable.ChainOffset = 0; */ +/* port_enable.MsgFlags = 0; */ +/* port_enable.MsgContext = 0; */ + + dprintk((KERN_INFO MYNAM ": %s: Sending Port(%d)Enable (req @ %p)\n", + ioc->name, portnum, &port_enable)); + + i = HandShakeReqAndReply(ioc, req_sz, (u32*)&port_enable, + reply_sz, (u16*)&reply_buf); + if (i != 0) + return i; + + /* We do not even look at the reply, so we need not + * swap the multi-byte fields. + */ + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * KickStart - Perform hard reset of MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * + * This routine places MPT adapter in diagnostic mode via the + * WriteSequence register, and then performs a hard reset of adapter + * via the Diagnostic register. + * + * Returns 0 for success, non-zero for failure. + */ +static int +KickStart(MPT_ADAPTER *ioc) +{ + int r; + u32 ioc_state; + int cnt = 0; + + dprintk((KERN_WARNING MYNAM ": KickStarting %s!\n", ioc->name)); + + if (ioc->chip_type == FC909) { + r = mpt_fc9x9_reset(ioc); + } else if (ioc->chip_type == FC929) { + unsigned long delta; + + delta = jiffies - ioc->last_kickstart; + dprintk((KERN_INFO MYNAM ": %s: 929 KickStart, last=%ld, delta = %ld\n", + ioc->name, ioc->last_kickstart, delta)); + if ((ioc->sod_reset == 0) || (delta >= 10*HZ)) + r = mpt_fc9x9_reset(ioc); + else { + dprintk((KERN_INFO MYNAM ": %s: Skipping KickStart (delta=%ld)!\n", + ioc->name, delta)); + return 0; + } + /* TODO! Add FC919! + } else if (ioc->chip_type == FC919) { + */ + /* TODO! Add C1030! + } else if (ioc->chip_type == C1030) { + */ + } else { + printk(KERN_ERR MYNAM ": %s: ERROR - Bad chip_type (0x%x)\n", + ioc->name, ioc->chip_type); + return -5; + } + + if (r != 0) + return -r; + + dprintk((KERN_INFO MYNAM ": %s: Diagnostic reset successful\n", + ioc->name)); + + for (cnt=0; cntname, cnt)); + return 0; + } + /* udelay(10000) ? */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + printk(KERN_ERR MYNAM ": %s: ERROR - Failed to come READY after reset!\n", + ioc->name); + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_fc9x9_reset - Perform hard reset of FC9x9 adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * + * This routine places FC9x9 adapter in diagnostic mode via the + * WriteSequence register, and then performs a hard reset of adapter + * via the Diagnostic register. + * + * Returns 0 for success, non-zero for failure. + */ +static int +mpt_fc9x9_reset(MPT_ADAPTER *ioc) +{ + u32 diagval; + + /* Use "Diagnostic reset" method! (only thing available!) */ + + /* + * Extra read to handle 909 B.0 chip problem with reset + * logic not finishing the RAM access before hard reset hits. + * (? comment taken from NT SYMMPI source) + */ + (void) CHIPREG_READ32(&ioc->chip->Fubar); + + /* + * Write magic sequence to WriteSequence register. + * But, send 0x0F first to insure a reset to the beginning of the sequence. + */ + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_KEY_VALUE_MASK); + + /* Now write magic sequence */ + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_3RD_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_4TH_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE); + dprintk((KERN_INFO MYNAM ": %s: Wrote magic DiagWriteEn sequence\n", + ioc->name)); + + /* Now hit the reset bit in the Diagnostic register */ + CHIPREG_WRITE32(&ioc->chip->Diagnostic, MPI_DIAG_RESET_ADAPTER); + + udelay(100); + + if ((diagval = CHIPREG_READ32(&ioc->chip->Diagnostic)) & + (MPI_DIAG_FLASH_BAD_SIG | MPI_DIAG_DISABLE_ARM)) { + printk(KERN_ERR MYNAM ": %s: ERROR - Diagnostic reset FAILED!\n", + ioc->name); + return -9; + } + + /* TODO! + * Cleanup all event stuff for this IOC; + * re-issue EventNotification request if needed. + */ + if (ioc->factsN.Function) + ioc->factsN.EventState = 0; + + /* NEW! 20010220 -sralston + * Try to avoid redundant resets of the 929. + */ + ioc->sod_reset++; + ioc->last_kickstart = jiffies; + if (ioc->alt_ioc) { + ioc->alt_ioc->sod_reset = ioc->sod_reset; + ioc->alt_ioc->last_kickstart = ioc->last_kickstart; + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SendIocReset - Send IOCReset request to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @reset_type: reset type, expected values are + * %MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET or %MPI_FUNCTION_IO_UNIT_RESET + * + * Send IOCReset request to the MPT adapter. + * + * Returns 0 for success, non-zero for failure. + */ +static int +SendIocReset(MPT_ADAPTER *ioc, u8 reset_type) +{ + int r; + + printk(KERN_WARNING MYNAM ": %s: Sending IOC reset(0x%02x)!\n", + ioc->name, reset_type); + CHIPREG_WRITE32(&ioc->chip->Doorbell, reset_type<factsN.Function) + ioc->factsN.EventState = 0; + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * PrimeIocFifos - Initialize IOC request and reply FIFOs. + * @ioc: Pointer to MPT_ADAPTER structure + * + * This routine allocates memory for the MPT reply and request frame + * pools, and primes the IOC reply FIFO with reply frames. + * + * Returns 0 for success, non-zero for failure. + */ +static int +PrimeIocFifos(MPT_ADAPTER *ioc) +{ + MPT_FRAME_HDR *mf; + unsigned long b; + dma_addr_t aligned_mem_dma; + u8 *mem, *aligned_mem; + int i, sz; + + /* Prime reply FIFO... */ + + if (ioc->reply_frames == NULL) { + sz = (ioc->reply_sz * ioc->reply_depth) + 128; + mem = pci_alloc_consistent(ioc->pcidev, sz, &ioc->reply_alloc_dma); + if (mem == NULL) + goto out_fail; + + memset(mem, 0, sz); + ioc->alloc_total += sz; + ioc->reply_alloc = mem; + dprintk((KERN_INFO MYNAM ": %s.reply_alloc @ %p[%08x], sz=%d bytes\n", + ioc->name, mem, ioc->reply_alloc_dma, sz)); + + b = (unsigned long) mem; + b = (b + (0x80UL - 1UL)) & ~(0x80UL - 1UL); /* round up to 128-byte boundary */ + aligned_mem = (u8 *) b; + ioc->reply_frames = (MPT_FRAME_HDR *) aligned_mem; + ioc->reply_frames_dma = + (ioc->reply_alloc_dma + (aligned_mem - mem)); + aligned_mem_dma = ioc->reply_frames_dma; + dprintk((KERN_INFO MYNAM ": %s.reply_frames @ %p[%08x]\n", + ioc->name, aligned_mem, aligned_mem_dma)); + + for (i = 0; i < ioc->reply_depth; i++) { + /* Write each address to the IOC! */ + CHIPREG_WRITE32(&ioc->chip->ReplyFifo, aligned_mem_dma); + aligned_mem_dma += ioc->reply_sz; + } + } + + + /* Request FIFO - WE manage this! */ + + if (ioc->req_frames == NULL) { + sz = (ioc->req_sz * ioc->req_depth) + 128; + /* + * Rounding UP to nearest 4-kB boundary here... + */ + sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000; + + mem = pci_alloc_consistent(ioc->pcidev, sz, &ioc->req_alloc_dma); + if (mem == NULL) + goto out_fail; + + memset(mem, 0, sz); + ioc->alloc_total += sz; + ioc->req_alloc = mem; + dprintk((KERN_INFO MYNAM ": %s.req_alloc @ %p[%08x], sz=%d bytes\n", + ioc->name, mem, ioc->req_alloc_dma, sz)); + + b = (unsigned long) mem; + b = (b + (0x80UL - 1UL)) & ~(0x80UL - 1UL); /* round up to 128-byte boundary */ + aligned_mem = (u8 *) b; + ioc->req_frames = (MPT_FRAME_HDR *) aligned_mem; + ioc->req_frames_dma = + (ioc->req_alloc_dma + (aligned_mem - mem)); + aligned_mem_dma = ioc->req_frames_dma; + + dprintk((KERN_INFO MYNAM ": %s.req_frames @ %p[%08x]\n", + ioc->name, aligned_mem, aligned_mem_dma)); + + for (i = 0; i < ioc->req_depth; i++) { + mf = (MPT_FRAME_HDR *) aligned_mem; + + /* Queue REQUESTs *internally*! */ + Q_ADD_TAIL(&ioc->FreeQ.head, &mf->u.frame.linkage, MPT_FRAME_HDR); + aligned_mem += ioc->req_sz; + } + +#if defined(CONFIG_MTRR) && 0 + /* + * Enable Write Combining MTRR for IOC's memory region. + * (at least as much as we can; "size and base must be + * multiples of 4 kiB" + */ + ioc->mtrr_reg = mtrr_add(ioc->req_alloc_dma, + sz, + MTRR_TYPE_WRCOMB, 1); + dprintk((KERN_INFO MYNAM ": %s: MTRR region registered (base:size=%08x:%x)\n", + ioc->name, ioc->req_alloc_dma, + sz )); +#endif + + } + + if (ioc->sense_buf_pool == NULL) { + sz = (ioc->req_depth * 256); + ioc->sense_buf_pool = + pci_alloc_consistent(ioc->pcidev, sz, &ioc->sense_buf_pool_dma); + if (ioc->sense_buf_pool == NULL) + goto out_fail; + + ioc->alloc_total += sz; + } + + return 0; + +out_fail: + if (ioc->reply_alloc != NULL) { + sz = (ioc->reply_sz * ioc->reply_depth) + 128; + pci_free_consistent(ioc->pcidev, + sz, + ioc->reply_alloc, ioc->reply_alloc_dma); + ioc->reply_frames = NULL; + ioc->reply_alloc = NULL; + ioc->alloc_total -= sz; + } + if (ioc->req_alloc != NULL) { + sz = (ioc->req_sz * ioc->req_depth) + 128; + /* + * Rounding UP to nearest 4-kB boundary here... + */ + sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000; + pci_free_consistent(ioc->pcidev, + sz, + ioc->req_alloc, ioc->req_alloc_dma); +#if defined(CONFIG_MTRR) && 0 + if (ioc->mtrr_reg > 0) { + mtrr_del(ioc->mtrr_reg, 0, 0); + dprintk((KERN_INFO MYNAM ": %s: MTRR region de-registered\n", + ioc->name)); + } +#endif + ioc->req_frames = NULL; + ioc->req_alloc = NULL; + ioc->alloc_total -= sz; + } + if (ioc->sense_buf_pool != NULL) { + sz = (ioc->req_depth * 256); + pci_free_consistent(ioc->pcidev, + sz, + ioc->sense_buf_pool, ioc->sense_buf_pool_dma); + ioc->sense_buf_pool = NULL; + } + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * HandShakeReqAndReply - Send MPT request to and receive reply from + * IOC via doorbell handshake method. + * @ioc: Pointer to MPT_ADAPTER structure + * @reqBytes: Size of the request in bytes + * @req: Pointer to MPT request frame + * @replyBytes: Expected size of the reply in bytes + * @u16reply: Pointer to area where reply should be written + * + * NOTES: It is the callers responsibility to byte-swap fields in the + * request which are greater than 1 byte in size. It is also the + * callers responsibility to byte-swap response fields which are + * greater than 1 byte in size. + * + * Returns 0 for success, non-zero for failure. + */ +static int +HandShakeReqAndReply(MPT_ADAPTER *ioc, int reqBytes, u32 *req, int replyBytes, u16 *u16reply) +{ + MPIDefaultReply_t *mptReply; + int failcnt = 0; + int t; + + /* + * Get ready to cache a handshake reply + */ + ioc->hs_reply_idx = 0; + mptReply = (MPIDefaultReply_t *) ioc->hs_reply; + mptReply->MsgLength = 0; + + /* + * Make sure there are no doorbells (WRITE 0 to IntStatus reg), + * then tell IOC that we want to handshake a request of N words. + * (WRITE u32val to Doorbell reg). + */ + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + CHIPREG_WRITE32(&ioc->chip->Doorbell, + ((MPI_FUNCTION_HANDSHAKE<name, t, failcnt ? " - MISSING DOORBELL HANDSHAKE!" : "")); + + /* + * Clear doorbell int (WRITE 0 to IntStatus reg), + * then wait for IOC to ACKnowledge that it's ready for + * our handshake request. + */ + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + if (!failcnt && (t = WaitForDoorbellAck(ioc)) < 0) + failcnt++; + + if (!failcnt) { + int i; + u8 *req_as_bytes = (u8 *) req; + + /* + * Stuff request words via doorbell handshake, + * with ACK from IOC for each. + */ + for (i = 0; !failcnt && i < reqBytes/4; i++) { + u32 word = ((req_as_bytes[(i*4) + 0] << 0) | + (req_as_bytes[(i*4) + 1] << 8) | + (req_as_bytes[(i*4) + 2] << 16) | + (req_as_bytes[(i*4) + 3] << 24)); + + CHIPREG_WRITE32(&ioc->chip->Doorbell, word); + if ((t = WaitForDoorbellAck(ioc)) < 0) + failcnt++; + } + + dmfprintk((KERN_INFO MYNAM ": Handshake request frame (@%p) header\n", req)); + DBG_DUMP_REQUEST_FRAME_HDR(req) + + dhsprintk((KERN_INFO MYNAM ": %s: HandShake request post done, WaitCnt=%d%s\n", + ioc->name, t, failcnt ? " - MISSING DOORBELL ACK!" : "")); + + /* + * Wait for completion of doorbell handshake reply from the IOC + */ + if (!failcnt && (t = WaitForDoorbellReply(ioc)) < 0) + failcnt++; + + /* + * Copy out the cached reply... + */ + for(i=0; i < MIN(replyBytes/2,mptReply->MsgLength*2); i++) + u16reply[i] = ioc->hs_reply[i]; + } else { + return -99; + } + + return -failcnt; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * WaitForDoorbellAck - Wait for IOC to clear the IOP_DOORBELL_STATUS bit + * in it's IntStatus register. + * @ioc: Pointer to MPT_ADAPTER structure + * + * This routine waits (up to ~30 seconds max) for IOC doorbell + * handshake ACKnowledge. + * + * Returns a negative value on failure, else wait loop count. + */ +static int +WaitForDoorbellAck(MPT_ADAPTER *ioc) +{ + int cntdn = HZ * 30; /* ~30 seconds */ + int count = 0; + u32 intstat; + + while (--cntdn) { + intstat = CHIPREG_READ32(&ioc->chip->IntStatus); + if (! (intstat & MPI_HIS_IOP_DOORBELL_STATUS)) + break; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + count++; + } + + if (cntdn) { + dhsprintk((KERN_INFO MYNAM ": %s: WaitForDoorbell ACK (cnt=%d)\n", + ioc->name, count)); + return count; + } + + printk(KERN_ERR MYNAM ": %s: ERROR - Doorbell ACK timeout(%d)!\n", + ioc->name, (count+5)/HZ); + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * WaitForDoorbellInt - Wait for IOC to set the HIS_DOORBELL_INTERRUPT bit + * in it's IntStatus register. + * @ioc: Pointer to MPT_ADAPTER structure + * + * This routine waits (up to ~30 seconds max) for IOC doorbell interrupt. + * + * Returns a negative value on failure, else wait loop count. + */ +static int +WaitForDoorbellInt(MPT_ADAPTER *ioc) +{ + int cntdn = HZ * 30; /* ~30 seconds */ + int count = 0; + u32 intstat; + + while (--cntdn) { + intstat = CHIPREG_READ32(&ioc->chip->IntStatus); + if (intstat & MPI_HIS_DOORBELL_INTERRUPT) + break; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + count++; + } + + if (cntdn) { + dhsprintk((KERN_INFO MYNAM ": %s: WaitForDoorbell INT (cnt=%d)\n", + ioc->name, count)); + return count; + } + + printk(KERN_ERR MYNAM ": %s: ERROR - Doorbell INT timeout(%d)!\n", + ioc->name, (count+5)/HZ); + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * WaitForDoorbellReply - Wait for and capture a IOC handshake reply. + * @ioc: Pointer to MPT_ADAPTER structure + * + * This routine polls the IOC for a handshake reply, 16 bits at a time. + * Reply is cached to IOC private area large enough to hold a maximum + * of 128 bytes of reply data. + * + * Returns a negative value on failure, else size of reply in WORDS. + */ +static int +WaitForDoorbellReply(MPT_ADAPTER *ioc) +{ + int u16cnt = 0; + int failcnt = 0; + int t; + u16 *hs_reply = ioc->hs_reply; + volatile MPIDefaultReply_t *mptReply = (MPIDefaultReply_t *) ioc->hs_reply; + u16 hword; + + hs_reply[0] = hs_reply[1] = hs_reply[7] = 0; + + /* + * Get first two u16's so we can look at IOC's intended reply MsgLength + */ + for (u16cnt=0; !failcnt && u16cnt < 2; u16cnt++) { + if ((t = WaitForDoorbellInt(ioc)) < 0) + failcnt++; + hs_reply[u16cnt] = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF); + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + } + + dhsprintk((KERN_INFO MYNAM ": %s: First handshake reply word=%08x%s\n", + ioc->name, le32_to_cpu(*(u32 *)hs_reply), + failcnt ? " - MISSING DOORBELL HANDSHAKE!" : "")); + + /* + * If no error (and IOC said MsgLength is > 0), piece together + * reply 16 bits at a time. + */ + for (u16cnt=2; !failcnt && u16cnt < (2 * mptReply->MsgLength); u16cnt++) { + if ((t = WaitForDoorbellInt(ioc)) < 0) + failcnt++; + hword = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF); + /* don't overflow our IOC hs_reply[] buffer! */ + if (u16cnt < sizeof(ioc->hs_reply) / sizeof(ioc->hs_reply[0])) + hs_reply[u16cnt] = hword; + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + } + + if (!failcnt && (t = WaitForDoorbellInt(ioc)) < 0) + failcnt++; + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + + if (failcnt) { + printk(KERN_ERR MYNAM ": %s: ERROR - Handshake reply failure!\n", + ioc->name); + return -failcnt; + } +#if 0 + else if (u16cnt != (2 * mptReply->MsgLength)) { + return -101; + } + else if ((mptReply->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) { + return -102; + } +#endif + + dmfprintk((KERN_INFO MYNAM ": %s: Got Handshake reply:\n", ioc->name)); + DBG_DUMP_REPLY_FRAME(mptReply) + + dhsprintk((KERN_INFO MYNAM ": %s: WaitForDoorbell REPLY (sz=%d)\n", + ioc->name, u16cnt/2)); + return u16cnt/2; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * GetLanConfigPages - Fetch LANConfig pages. + * @ioc: Pointer to MPT_ADAPTER structure + * + * Returns 0 for success, non-zero for failure. + */ +static int +GetLanConfigPages(MPT_ADAPTER *ioc) +{ + Config_t config_req; + ConfigReply_t config_reply; + LANPage0_t *page0; + dma_addr_t page0_dma; + LANPage1_t *page1; + dma_addr_t page1_dma; + int i; + int req_sz; + int reply_sz; + int data_sz; + +/* LANPage0 */ + /* Immediate destination (reply area)... */ + reply_sz = sizeof(config_reply); + memset(&config_reply, 0, reply_sz); + + /* Ultimate destination... */ + page0 = &ioc->lan_cnfg_page0; + data_sz = sizeof(*page0); + memset(page0, 0, data_sz); + + /* Request area (config_req on the stack right now!) */ + req_sz = sizeof(config_req); + memset(&config_req, 0, req_sz); + config_req.Function = MPI_FUNCTION_CONFIG; + config_req.Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + /* config_req.Header.PageVersion = 0; */ + /* config_req.Header.PageLength = 0; */ + config_req.Header.PageNumber = 0; + config_req.Header.PageType = MPI_CONFIG_PAGETYPE_LAN; + /* config_req.PageAddress = 0; */ + config_req.PageBufferSGE.u.Simple.FlagsLength = cpu_to_le32( + ((MPI_SGE_FLAGS_LAST_ELEMENT | + MPI_SGE_FLAGS_END_OF_BUFFER | + MPI_SGE_FLAGS_END_OF_LIST | + MPI_SGE_FLAGS_SIMPLE_ELEMENT | + MPI_SGE_FLAGS_SYSTEM_ADDRESS | + MPI_SGE_FLAGS_32_BIT_ADDRESSING | + MPI_SGE_FLAGS_32_BIT_CONTEXT) << MPI_SGE_FLAGS_SHIFT) | + (u32)data_sz + ); + page0_dma = pci_map_single(ioc->pcidev, page0, data_sz, PCI_DMA_FROMDEVICE); + config_req.PageBufferSGE.u.Simple.u.Address32 = cpu_to_le32(page0_dma); + + dprintk((KERN_INFO MYNAM ": %s: Sending Config request LAN_PAGE_0\n", + ioc->name)); + + i = HandShakeReqAndReply(ioc, req_sz, (u32*)&config_req, + reply_sz, (u16*)&config_reply); + pci_unmap_single(ioc->pcidev, page0_dma, data_sz, PCI_DMA_FROMDEVICE); + if (i != 0) + return i; + + /* Now byte swap the necessary LANPage0 fields */ + +/* LANPage1 */ + /* Immediate destination (reply area)... */ + reply_sz = sizeof(config_reply); + memset(&config_reply, 0, reply_sz); + + /* Ultimate destination... */ + page1 = &ioc->lan_cnfg_page1; + data_sz = sizeof(*page1); + memset(page1, 0, data_sz); + + /* Request area (config_req on the stack right now!) */ + req_sz = sizeof(config_req); + memset(&config_req, 0, req_sz); + config_req.Function = MPI_FUNCTION_CONFIG; + config_req.Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + /* config_req.Header.PageVersion = 0; */ + /* config_req.Header.PageLength = 0; */ + config_req.Header.PageNumber = 1; + config_req.Header.PageType = MPI_CONFIG_PAGETYPE_LAN; + /* config_req.PageAddress = 0; */ + config_req.PageBufferSGE.u.Simple.FlagsLength = cpu_to_le32( + ((MPI_SGE_FLAGS_LAST_ELEMENT | + MPI_SGE_FLAGS_END_OF_BUFFER | + MPI_SGE_FLAGS_END_OF_LIST | + MPI_SGE_FLAGS_SIMPLE_ELEMENT | + MPI_SGE_FLAGS_SYSTEM_ADDRESS | + MPI_SGE_FLAGS_32_BIT_ADDRESSING | + MPI_SGE_FLAGS_32_BIT_CONTEXT) << MPI_SGE_FLAGS_SHIFT) | + (u32)data_sz + ); + page1_dma = pci_map_single(ioc->pcidev, page1, data_sz, PCI_DMA_FROMDEVICE); + config_req.PageBufferSGE.u.Simple.u.Address32 = cpu_to_le32(page1_dma); + + dprintk((KERN_INFO MYNAM ": %s: Sending Config request LAN_PAGE_1\n", + ioc->name)); + + i = HandShakeReqAndReply(ioc, req_sz, (u32*)&config_req, + reply_sz, (u16*)&config_reply); + pci_unmap_single(ioc->pcidev, page1_dma, data_sz, PCI_DMA_FROMDEVICE); + if (i != 0) + return i; + + /* Now byte swap the necessary LANPage1 fields */ + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * SendEventNotification - Send EventNotification (on or off) request + * to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @EvSwitch: Event switch flags + */ +static int +SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch) +{ + EventNotification_t *evnp; + + evnp = (EventNotification_t *) mpt_get_msg_frame(mpt_base_index, ioc->id); + if (evnp == NULL) { + dprintk((KERN_WARNING MYNAM ": %s: WARNING - Unable to allocate a event request frame!\n", + ioc->name)); + return 0; + } + memset(evnp, 0, sizeof(*evnp)); + + dprintk((KERN_INFO MYNAM ": %s: Sending EventNotification(%d)\n", ioc->name, EvSwitch)); + + evnp->Function = MPI_FUNCTION_EVENT_NOTIFICATION; + evnp->ChainOffset = 0; + evnp->MsgFlags = 0; + evnp->Switch = EvSwitch; + + mpt_put_msg_frame(mpt_base_index, ioc->id, (MPT_FRAME_HDR *)evnp); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * SendEventAck - Send EventAck request to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @evnp: Pointer to original EventNotification request + */ +static int +SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp) +{ + EventAck_t *pAck; + + if ((pAck = (EventAck_t *) mpt_get_msg_frame(mpt_base_index, ioc->id)) == NULL) { + printk(KERN_WARNING MYNAM ": %s: WARNING - Unable to allocate event ACK request frame!\n", + ioc->name); + return -1; + } + memset(pAck, 0, sizeof(*pAck)); + + dprintk((KERN_INFO MYNAM ": %s: Sending EventAck\n", ioc->name)); + + pAck->Function = MPI_FUNCTION_EVENT_ACK; + pAck->ChainOffset = 0; + pAck->MsgFlags = 0; + pAck->Event = evnp->Event; + pAck->EventContext = evnp->EventContext; + + mpt_put_msg_frame(mpt_base_index, ioc->id, (MPT_FRAME_HDR *)pAck); + + return 0; +} + +#ifdef CONFIG_PROC_FS /* { */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * procfs (%MPT_PROCFS_MPTBASEDIR/...) support stuff... + */ + +#define PROC_MPT_READ_RETURN(page,start,off,count,eof,len) \ +{ \ + len -= off; \ + if (len < count) { \ + *eof = 1; \ + if (len <= 0) \ + return 0; \ + } else \ + len = count; \ + *start = page + off; \ + return len; \ +} + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * procmpt_create - Create %MPT_PROCFS_MPTBASEDIR entries. + * + * Returns 0 for success, non-zero for failure. + */ +static int +procmpt_create(void) +{ + MPT_ADAPTER *ioc; + struct proc_dir_entry *ent; + int errcnt = 0; + + /* + * BEWARE: If/when MPT_PROCFS_MPTBASEDIR changes from "mpt" + * (single level) to multi level (e.g. "driver/message/fusion") + * something here needs to change. -sralston + */ + procmpt_root_dir = CREATE_PROCDIR_ENTRY(MPT_PROCFS_MPTBASEDIR, NULL); + if (procmpt_root_dir == NULL) + return -ENOTDIR; + + if ((ioc = mpt_adapter_find_first()) != NULL) { + ent = create_proc_read_entry(MPT_PROCFS_SUMMARY_NODE, 0, NULL, procmpt_read_summary, NULL); + if (ent == NULL) { + printk(KERN_WARNING MYNAM ": WARNING - Could not create %s entry!\n", + MPT_PROCFS_SUMMARY_PATHNAME); + errcnt++; + } + } + + while (ioc != NULL) { + char pname[32]; + int namelen; + /* + * Create "/proc/mpt/iocN" subdirectory entry for each MPT adapter. + */ + namelen = sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s", ioc->name); + if ((ent = CREATE_PROCDIR_ENTRY(pname, NULL)) != NULL) { + /* + * And populate it with: "summary" and "dbg" file entries. + */ + (void) sprintf(pname+namelen, "/summary"); + ent = create_proc_read_entry(pname, 0, NULL, procmpt_read_summary, ioc); + if (ent == NULL) { + errcnt++; + printk(KERN_WARNING MYNAM ": %s: WARNING - Could not create /proc/%s entry!\n", + ioc->name, pname); + } +//#ifdef MPT_DEBUG + /* DEBUG aid! */ + (void) sprintf(pname+namelen, "/dbg"); + ent = create_proc_read_entry(pname, 0, NULL, procmpt_read_dbg, ioc); + if (ent == NULL) { + errcnt++; + printk(KERN_WARNING MYNAM ": %s: WARNING - Could not create /proc/%s entry!\n", + ioc->name, pname); + } +//#endif + } else { + errcnt++; + printk(KERN_WARNING MYNAM ": %s: WARNING - Could not create /proc/%s entry!\n", + ioc->name, pname); + + } + + ioc = mpt_adapter_find_next(ioc); + } + + if (errcnt) { +// remove_proc_entry("mpt", 0); + return -ENOTDIR; + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * procmpt_destroy - Tear down %MPT_PROCFS_MPTBASEDIR entries. + * + * Returns 0 for success, non-zero for failure. + */ +static int +procmpt_destroy(void) +{ + MPT_ADAPTER *ioc; + + /* + * BEWARE: If/when MPT_PROCFS_MPTBASEDIR changes from "mpt" + * (single level) to multi level (e.g. "driver/message/fusion") + * something here needs to change. -sralston + */ + + ioc = mpt_adapter_find_first(); + if (ioc != NULL) { + remove_proc_entry(MPT_PROCFS_SUMMARY_NODE, 0); + } + + while (ioc != NULL) { + char pname[32]; + int namelen; + /* + * Tear down each "/proc/mpt/iocN" subdirectory. + */ + namelen = sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s", ioc->name); + (void) sprintf(pname+namelen, "/summary"); + remove_proc_entry(pname, 0); +//#ifdef MPT_DEBUG + (void) sprintf(pname+namelen, "/dbg"); + remove_proc_entry(pname, 0); +//#endif + (void) sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s", ioc->name); + remove_proc_entry(pname, 0); + + ioc = mpt_adapter_find_next(ioc); + } + + if (atomic_read((atomic_t *)&procmpt_root_dir->count) == 0) { + remove_proc_entry(MPT_PROCFS_MPTBASEDIR, 0); + return 0; + } + + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * procmpt_read_summary - Handle read request from /proc/mpt/summary + * or from /proc/mpt/iocN/summary. + * @page: Pointer to area to write information + * @start: Pointer to start pointer + * @off: Offset to start writing + * @count: + * @eof: Pointer to EOF integer + * @data: Pointer + * + * Returns numbers of characters written to process performing the read. + */ +static int +procmpt_read_summary(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + MPT_ADAPTER *ioc; + char *out = page; + int len; + + if (data == NULL) + ioc = mpt_adapter_find_first(); + else + ioc = data; + +// Too verbose! +// out += sprintf(out, "Attached Fusion MPT I/O Controllers:%s\n", ioc ? "" : " none"); + + while (ioc) { + int more = 0; + +// Too verbose! +// mpt_print_ioc_facts(ioc, out, &more, 0); + mpt_print_ioc_summary(ioc, out, &more, 0); + + out += more; + if ((out-page) >= count) { + break; + } + + if (data == NULL) + ioc = mpt_adapter_find_next(ioc); + else + ioc = NULL; /* force exit for iocN */ + } + len = out - page; + + PROC_MPT_READ_RETURN(page,start,off,count,eof,len); +} + +// debug aid! +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * procmpt_read_dbg - Handle read request from /proc/mpt/iocN/dbg. + * @page: Pointer to area to write information + * @start: Pointer to start pointer + * @off: Offset to start writing + * @count: + * @eof: Pointer to EOF integer + * @data: Pointer + * + * Returns numbers of characters written to process performing the read. + */ +static int +procmpt_read_dbg(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + MPT_ADAPTER *ioc; + char *out = page; + int len; + + ioc = data; + + while (ioc) { + int more = 0; + + mpt_print_ioc_facts(ioc, out, &more, 0); + + out += more; + if ((out-page) >= count) { + break; + } + ioc = NULL; + } + len = out - page; + + PROC_MPT_READ_RETURN(page,start,off,count,eof,len); +} +#endif /* CONFIG_PROC_FS } */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static void +mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc) +{ + if ((ioc->facts0.FWVersion & 0xF000) == 0xE000) + sprintf(buf, " (Exp %02d%02d)", + (ioc->facts0.FWVersion & 0x0F00) >> 8, /* Month */ + ioc->facts0.FWVersion & 0x001F); /* Day */ + else + buf[0] ='\0'; + + /* insider hack! */ + if (ioc->facts0.FWVersion & 0x0080) { + strcat(buf, " [MDBG]"); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_print_ioc_summary - Write ASCII summary of IOC to a buffer. + * @ioc: Pointer to MPT_ADAPTER structure + * @buffer: Pointer to buffer where IOC summary info should be written + * @size: Pointer to number of bytes we wrote (set by this routine) + * @len: Offset at which to start writing in buffer + * + * This routine writes (english readable) ASCII text, which represents + * a summary of IOC information, to a buffer. + */ +void +mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buffer, int *size, int len) +{ + char expVer[32]; + int y; + + + mpt_get_fw_exp_ver(expVer, ioc); + + /* + * Shorter summary of attached ioc's... + */ + y = sprintf(buffer+len, "%s: %s, %s%04xh%s, Ports=%d, MaxQ=%d", + ioc->name, + ioc->prod_name, + MPT_FW_REV_MAGIC_ID_STRING, /* "FwRev=" or somesuch */ + ioc->facts0.FWVersion, + expVer, + ioc->facts0.NumberOfPorts, + ioc->req_depth); + + if (ioc->pfacts0.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) { + u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow; + y += sprintf(buffer+len+y, ", LanAddr=%02X:%02X:%02X:%02X:%02X:%02X", + a[5], a[4], a[3], a[2], a[1], a[0]); + } + + if (!ioc->active) + y += sprintf(buffer+len+y, " (disabled)"); + + y += sprintf(buffer+len+y, "\n"); + + *size = y; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_print_ioc_facts - Write ASCII summary of IOC facts to a buffer. + * @ioc: Pointer to MPT_ADAPTER structure + * @buffer: Pointer to buffer where IOC facts should be written + * @size: Pointer to number of bytes we wrote (set by this routine) + * @len: Offset at which to start writing in buffer + * + * This routine writes (english readable) ASCII text, which represents + * a summary of the IOC facts, to a buffer. + */ +void +mpt_print_ioc_facts(MPT_ADAPTER *ioc, char *buffer, int *size, int len) +{ + char expVer[32]; + char iocName[16]; + int sz; + int y; + + + mpt_get_fw_exp_ver(expVer, ioc); + + strcpy(iocName, ioc->name); + y = sprintf(buffer+len, "%s:\n", iocName); + + y += sprintf(buffer+len+y, " ProductID = 0x%04x\n", ioc->facts0.ProductID); + y += sprintf(buffer+len+y, " PortNumber = %d (of %d)\n", + ioc->pfacts0.PortNumber+1, + ioc->facts0.NumberOfPorts); + if (ioc->pfacts0.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) { + u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow; + y += sprintf(buffer+len+y, " LanAddr = 0x%02x:%02x:%02x:%02x:%02x:%02x\n", + a[5], a[4], a[3], a[2], a[1], a[0]); + } + y += sprintf(buffer+len+y, " FWVersion = 0x%04x%s\n", ioc->facts0.FWVersion, expVer); + y += sprintf(buffer+len+y, " MsgVersion = 0x%04x\n", ioc->facts0.MsgVersion); + y += sprintf(buffer+len+y, " WhoInit = 0x%02x\n", ioc->facts0.WhoInit); + y += sprintf(buffer+len+y, " EventState = 0x%02x\n", ioc->facts0.EventState); + y += sprintf(buffer+len+y, " CurrentHostMfaHighAddr = 0x%08x\n", + ioc->facts0.CurrentHostMfaHighAddr); + y += sprintf(buffer+len+y, " CurrentSenseBufferHighAddr = 0x%08x\n", + ioc->facts0.CurrentSenseBufferHighAddr); + y += sprintf(buffer+len+y, " MaxChainDepth = 0x%02x frames\n", ioc->facts0.MaxChainDepth); + y += sprintf(buffer+len+y, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts0.BlockSize); + + y += sprintf(buffer+len+y, " RequestFrames @ 0x%p (Dma @ 0x%08x)\n", + ioc->req_alloc, ioc->req_alloc_dma); + /* + * Rounding UP to nearest 4-kB boundary here... + */ + sz = (ioc->req_sz * ioc->req_depth) + 128; + sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000; + y += sprintf(buffer+len+y, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n", + ioc->req_sz, ioc->req_depth, ioc->req_sz*ioc->req_depth, sz); + y += sprintf(buffer+len+y, " {MaxReqSz=%d} {MaxReqDepth=%d}\n", + 4*ioc->facts0.RequestFrameSize, + ioc->facts0.GlobalCredits); + + y += sprintf(buffer+len+y, " ReplyFrames @ 0x%p (Dma @ 0x%08x)\n", + ioc->reply_alloc, ioc->reply_alloc_dma); + sz = (ioc->reply_sz * ioc->reply_depth) + 128; + y += sprintf(buffer+len+y, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n", + ioc->reply_sz, ioc->reply_depth, ioc->reply_sz*ioc->reply_depth, sz); + y += sprintf(buffer+len+y, " {MaxRepSz=%d} {MaxRepDepth=%d}\n", + ioc->factsN.CurReplyFrameSize, + ioc->facts0.ReplyQueueDepth); + + *size = y; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static char * +EventDescriptionStr(u8 event, u32 evData0) +{ + char *ds = NULL; + + switch(event) { + case MPI_EVENT_NONE: + ds = "None"; + break; + case MPI_EVENT_LOG_DATA: + ds = "Log Data"; + break; + case MPI_EVENT_STATE_CHANGE: + ds = "State Change"; + break; + case MPI_EVENT_UNIT_ATTENTION: + ds = "Unit Attention"; + break; + case MPI_EVENT_IOC_BUS_RESET: + ds = "IOC Bus Reset"; + break; + case MPI_EVENT_EXT_BUS_RESET: + ds = "External Bus Reset"; + break; + case MPI_EVENT_RESCAN: + ds = "Bus Rescan Event"; + /* Ok, do we need to do anything here? As far as + I can tell, this is when a new device gets added + to the loop. */ + break; + case MPI_EVENT_LINK_STATUS_CHANGE: + if (evData0 == MPI_EVENT_LINK_STATUS_FAILURE) + ds = "Link Status(FAILURE) Change"; + else + ds = "Link Status(ACTIVE) Change"; + break; + case MPI_EVENT_LOOP_STATE_CHANGE: + if (evData0 == MPI_EVENT_LOOP_STATE_CHANGE_LIP) + ds = "Loop State(LIP) Change"; + else if (evData0 == MPI_EVENT_LOOP_STATE_CHANGE_LPE) + ds = "Loop State(LPE) Change"; /* ??? */ + else + ds = "Loop State(LPB) Change"; /* ??? */ + break; + case MPI_EVENT_LOGOUT: + ds = "Logout"; + break; + case MPI_EVENT_EVENT_CHANGE: + if (evData0) + ds = "Events(ON) Change"; + else + ds = "Events(OFF) Change"; + break; + /* + * MPT base "custom" events may be added here... + */ + default: + ds = "Unknown"; + break; + } + return ds; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * ProcessEventNotification - Route a received EventNotificationReply to + * all currently regeistered event handlers. + * @ioc: Pointer to MPT_ADAPTER structure + * @pEventReply: Pointer to EventNotification reply frame + * @evHandlers: Pointer to integer, number of event handlers + * + * Returns sum of event handlers return values. + */ +static int +ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply, int *evHandlers) +{ + u16 evDataLen; + u32 evData0 = 0; +// u32 evCtx; + int i; + int r = 0; + int handlers = 0; + char *evStr; + u8 event; + + /* + * Do platform normalization of values + */ + event = le32_to_cpu(pEventReply->Event) & 0xFF; +// evCtx = le32_to_cpu(pEventReply->EventContext); + evDataLen = le16_to_cpu(pEventReply->EventDataLength); + if (evDataLen) { + evData0 = le32_to_cpu(pEventReply->Data[0]); + } + + evStr = EventDescriptionStr(event, evData0); + dprintk((KERN_INFO MYNAM ": %s: MPT event (%s=%02Xh) detected!\n", + ioc->name, + evStr, + event)); + +#if defined(MPT_DEBUG) || defined(MPT_DEBUG_EVENTS) + printk(KERN_INFO MYNAM ": Event data:\n" KERN_INFO); + for (i = 0; i < evDataLen; i++) + printk(" %08x", le32_to_cpu(pEventReply->Data[i])); + printk("\n"); +#endif + + /* + * Do general / base driver event processing + */ + switch(event) { + case MPI_EVENT_NONE: /* 00 */ + case MPI_EVENT_LOG_DATA: /* 01 */ + case MPI_EVENT_STATE_CHANGE: /* 02 */ + case MPI_EVENT_UNIT_ATTENTION: /* 03 */ + case MPI_EVENT_IOC_BUS_RESET: /* 04 */ + case MPI_EVENT_EXT_BUS_RESET: /* 05 */ + case MPI_EVENT_RESCAN: /* 06 */ + case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */ + case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */ + case MPI_EVENT_LOGOUT: /* 09 */ + default: + break; + case MPI_EVENT_EVENT_CHANGE: /* 0A */ + if (evDataLen) { + u8 evState = evData0 & 0xFF; + + /* CHECKME! What if evState unexpectedly says OFF (0)? */ + + /* Update EventState field in cached IocFacts */ + if (ioc->factsN.Function) { + ioc->factsN.EventState = evState; + } + } + break; + } + + /* + * Call each currently registered protocol event handler. + */ + for (i=MPT_MAX_PROTOCOL_DRIVERS-1; i; i--) { + if (MptEvHandlers[i]) { + dprintk((KERN_INFO MYNAM ": %s: Routing Event to event handler #%d\n", + ioc->name, i)); + r += (*(MptEvHandlers[i]))(ioc, pEventReply); + handlers++; + } + } + /* FIXME? Examine results here? */ + + /* + * If needed, send (a single) EventAck. + */ + if (pEventReply->AckRequired == MPI_EVENT_NOTIFICATION_ACK_REQUIRED) { + if ((i = SendEventAck(ioc, pEventReply)) != 0) { + } + } + + *evHandlers = handlers; + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_fc_log_info - Log information returned from Fibre Channel IOC. + * @ioc: Pointer to MPT_ADAPTER structure + * @log_info: U32 LogInfo reply word from the IOC + * + * Refer to lsi/fc_log.h. + */ +static void +mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info) +{ + static char *subcl_str[8] = { + "FCP Initiator", "FCP Target", "LAN", "MPI Message Layer", + "FC Link", "Context Manager", "Invalid Field Offset", "State Change Info" + }; + char *desc = "unknown"; + u8 subcl = (log_info >> 24) & 0x7; + u32 SubCl = log_info & 0x27000000; + + switch(log_info) { +/* FCP Initiator */ + case MPI_IOCLOGINFO_FC_INIT_ERROR_OUT_OF_ORDER_FRAME: + desc = "Received an out of order frame - unsupported"; + break; + case MPI_IOCLOGINFO_FC_INIT_ERROR_BAD_START_OF_FRAME: + desc = "Bad start of frame primative"; + break; + case MPI_IOCLOGINFO_FC_INIT_ERROR_BAD_END_OF_FRAME: + desc = "Bad end of frame primative"; + break; + case MPI_IOCLOGINFO_FC_INIT_ERROR_OVER_RUN: + desc = "Receiver hardware detected overrun"; + break; + case MPI_IOCLOGINFO_FC_INIT_ERROR_RX_OTHER: + desc = "Other errors caught by IOC which require retries"; + break; + case MPI_IOCLOGINFO_FC_INIT_ERROR_SUBPROC_DEAD: + desc = "Main processor could not initialize sub-processor"; + break; +/* FC Target */ + case MPI_IOCLOGINFO_FC_TARGET_NO_PDISC: + desc = "Not sent because we are waiting for a PDISC from the initiator"; + break; + case MPI_IOCLOGINFO_FC_TARGET_NO_LOGIN: + desc = "Not sent because we are not logged in to the remote node"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DOAR_KILLED_BY_LIP: + desc = "Data Out, Auto Response, not sent due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DIAR_KILLED_BY_LIP: + desc = "Data In, Auto Response, not sent due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DIAR_MISSING_DATA: + desc = "Data In, Auto Response, missing data frames"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DONR_KILLED_BY_LIP: + desc = "Data Out, No Response, not sent due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_WRSP_KILLED_BY_LIP: + desc = "Auto-response after a write not sent due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DINR_KILLED_BY_LIP: + desc = "Data In, No Response, not completed due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DINR_MISSING_DATA: + desc = "Data In, No Response, missing data frames"; + break; + case MPI_IOCLOGINFO_FC_TARGET_MRSP_KILLED_BY_LIP: + desc = "Manual Response not sent due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_NO_CLASS_3: + desc = "Not sent because remote node does not support Class 3"; + break; + case MPI_IOCLOGINFO_FC_TARGET_LOGIN_NOT_VALID: + desc = "Not sent because login to remote node not validated"; + break; + case MPI_IOCLOGINFO_FC_TARGET_FROM_OUTBOUND: + desc = "Cleared from the outbound after a logout"; + break; + case MPI_IOCLOGINFO_FC_TARGET_WAITING_FOR_DATA_IN: + desc = "Cleared waiting for data after a logout"; + break; +/* LAN */ + case MPI_IOCLOGINFO_FC_LAN_TRANS_SGL_MISSING: + desc = "Transaction Context Sgl Missing"; + break; + case MPI_IOCLOGINFO_FC_LAN_TRANS_WRONG_PLACE: + desc = "Transaction Context found before an EOB"; + break; + case MPI_IOCLOGINFO_FC_LAN_TRANS_RES_BITS_SET: + desc = "Transaction Context value has reserved bits set"; + break; + case MPI_IOCLOGINFO_FC_LAN_WRONG_SGL_FLAG: + desc = "Invalid SGL Flags"; + break; +/* FC Link */ + case MPI_IOCLOGINFO_FC_LINK_LOOP_INIT_TIMEOUT: + desc = "Loop initialization timed out"; + break; + case MPI_IOCLOGINFO_FC_LINK_ALREADY_INITIALIZED: + desc = "Another system controller already initialized the loop"; + break; + case MPI_IOCLOGINFO_FC_LINK_LINK_NOT_ESTABLISHED: + desc = "Not synchronized to signal or still negotiating (possible cable problem)"; + break; + } + + printk(KERN_INFO MYNAM ": %s: LogInfo(0x%08x): SubCl={%s}", + ioc->name, log_info, subcl_str[subcl]); + if (SubCl == MPI_IOCLOGINFO_FC_INVALID_FIELD_BYTE_OFFSET) + printk(", byte_offset=%d\n", log_info & MPI_IOCLOGINFO_FC_INVALID_FIELD_MAX_OFFSET); + else if (SubCl == MPI_IOCLOGINFO_FC_STATE_CHANGE) + printk("\n"); /* StateChg in LogInfo & 0x00FFFFFF, above */ + else + printk("\n" KERN_INFO " %s\n", desc); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_sp_log_info - Log information returned from SCSI Parallel IOC. + * @ioc: Pointer to MPT_ADAPTER structure + * @mr: Pointer to MPT reply frame + * @log_info: U32 LogInfo word from the IOC + * + * Refer to lsi/sp_log.h. + */ +static void +mpt_sp_log_info(MPT_ADAPTER *ioc, u32 log_info) +{ + /* FIXME! */ + printk(KERN_INFO MYNAM ": %s: LogInfo(0x%08x)\n", ioc->name, log_info); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_register_ascqops_strings - Register SCSI ASC/ASCQ and SCSI + * OpCode strings from the (optional) isense module. + * @ascqTable: Pointer to ASCQ_Table_t structure + * @ascqtbl_sz: Number of entries in ASCQ_Table + * @opsTable: Pointer to array of SCSI OpCode strings (char pointers) + * + * Specialized driver registration routine for the isense driver. + */ +int +mpt_register_ascqops_strings(/*ASCQ_Table_t*/void *ascqTable, int ascqtbl_sz, const char **opsTable) +{ + int r = 0; + + if (ascqTable && ascqtbl_sz && opsTable) { + mpt_v_ASCQ_TablePtr = ascqTable; + mpt_ASCQ_TableSz = ascqtbl_sz; + mpt_ScsiOpcodesPtr = opsTable; + printk(KERN_INFO MYNAM ": English readable SCSI-3 strings enabled:-)\n"); + r = 1; + } + MOD_INC_USE_COUNT; + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_deregister_ascqops_strings - Deregister SCSI ASC/ASCQ and SCSI + * OpCode strings from the isense driver. + * + * Specialized driver deregistration routine for the isense driver. + */ +void +mpt_deregister_ascqops_strings(void) +{ + mpt_v_ASCQ_TablePtr = NULL; + mpt_ASCQ_TableSz = 0; + mpt_ScsiOpcodesPtr = NULL; + printk(KERN_INFO MYNAM ": English readable SCSI-3 strings disabled)-:\n"); + MOD_DEC_USE_COUNT; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +EXPORT_SYMBOL(mpt_register); +EXPORT_SYMBOL(mpt_deregister); +EXPORT_SYMBOL(mpt_event_register); +EXPORT_SYMBOL(mpt_event_deregister); +EXPORT_SYMBOL(mpt_get_msg_frame); +EXPORT_SYMBOL(mpt_put_msg_frame); +EXPORT_SYMBOL(mpt_free_msg_frame); +EXPORT_SYMBOL(mpt_send_handshake_request); +EXPORT_SYMBOL(mpt_adapter_find_first); +EXPORT_SYMBOL(mpt_adapter_find_next); +EXPORT_SYMBOL(mpt_verify_adapter); +EXPORT_SYMBOL(mpt_print_ioc_summary); +EXPORT_SYMBOL(mpt_lan_index); +EXPORT_SYMBOL(mpt_stm_index); + +EXPORT_SYMBOL(mpt_register_ascqops_strings); +EXPORT_SYMBOL(mpt_deregister_ascqops_strings); +EXPORT_SYMBOL(mpt_v_ASCQ_TablePtr); +EXPORT_SYMBOL(mpt_ASCQ_TableSz); +EXPORT_SYMBOL(mpt_ScsiOpcodesPtr); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * fusion_init - Fusion MPT base driver initialization routine. + * + * Returns 0 for success, non-zero for failure. + */ +int __init fusion_init(void) +{ + int i; + + if (FusionInitCalled++) { + dprintk((KERN_INFO MYNAM ": INFO - Driver late-init entry point called\n")); + return 0; + } + + show_mptmod_ver(my_NAME, my_VERSION); + printk(KERN_INFO COPYRIGHT "\n"); + + Q_INIT(&MptAdapters, MPT_ADAPTER); /* set to empty */ + for (i = 0; i < MPT_MAX_PROTOCOL_DRIVERS; i++) { + MptCallbacks[i] = NULL; + MptDriverClass[i] = MPTUNKNOWN_DRIVER; + MptEvHandlers[i] = NULL; + } + + /* NEW! 20010120 -sralston + * Register ourselves (mptbase) in order to facilitate + * EventNotification handling. + */ + mpt_base_index = mpt_register(mpt_base_reply, MPTBASE_DRIVER); + + if ((i = mpt_pci_scan()) < 0) + return i; + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * fusion_exit - Perform driver unload cleanup. + * + * This routine frees all resources associated with each MPT adapter + * and removes all %MPT_PROCFS_MPTBASEDIR entries. + */ +static void fusion_exit(void) +{ + MPT_ADAPTER *this; + int i; + + dprintk((KERN_INFO MYNAM ": fusion_exit() called!\n")); + + /* + * Paranoia; disable interrupts on all MPT adapters. + */ + for (i=0; ichip->IntMask, 0xFFFFFFFF); + /* Clear any lingering interrupt */ + CHIPREG_WRITE32(&this->chip->IntStatus, 0); + this->active = 0; + } + } + + /* Whups? 20010120 -sralston + * Moved this *above* removal of all MptAdapters! + */ +#ifdef CONFIG_PROC_FS + procmpt_destroy(); +#endif + + while (! Q_IS_EMPTY(&MptAdapters)) { + this = MptAdapters.head; + Q_DEL_ITEM(this); + mpt_adapter_dispose(this); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +module_init(fusion_init); +module_exit(fusion_exit); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/mptbase.h linux.ac/drivers/message/fusion/mptbase.h --- linux.vanilla/drivers/message/fusion/mptbase.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/mptbase.h Sat May 26 17:58:28 2001 @@ -0,0 +1,581 @@ +/* + * linux/drivers/message/fusion/mptbase.h + * High performance SCSI + LAN / Fibre Channel device drivers. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Credits: + * (see mptbase.c) + * + * Copyright (c) 1999-2001 LSI Logic Corporation + * Originally By: Steven J. Ralston + * (mailto:Steve.Ralston@lsil.com) + * + * $Id: mptbase.h,v 1.38 2001/03/22 10:54:30 sralston Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + 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; version 2 of the License. + + 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. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MPTBASE_H_INCLUDED +#define MPTBASE_H_INCLUDED +/*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include "linux_compat.h" /* linux-2.2.x (vs. -2.4.x) tweaks */ + +#include "lsi/mpi_type.h" +#include "lsi/mpi.h" /* Fusion MPI(nterface) basic defs */ +#include "lsi/mpi_ioc.h" /* Fusion MPT IOC(ontroller) defs */ +#include "lsi/mpi_cnfg.h" /* IOC configuration support */ +#include "lsi/mpi_init.h" /* SCSI Host (initiator) protocol support */ +#include "lsi/mpi_lan.h" /* LAN over FC protocol support */ + +//#include "lsi/mpi_fc.h" /* Fibre Channel (lowlevel) support */ +//#include "lsi/mpi_targ.h" /* SCSI/FCP Target protcol support */ +#include "lsi/fc_log.h" + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#ifndef MODULEAUTHOR +#define MODULEAUTHOR "LSI Logic Corporation" +#endif + +#ifndef COPYRIGHT +#define COPYRIGHT "Copyright (c) 1999-2001 " MODULEAUTHOR +#endif + +#define MPT_LINUX_VERSION_COMMON "1.00.11" +#define MPT_LINUX_VERSION_EXP "0.09.66-EXP" +#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-1.00.11" +#define WHAT_MAGIC_STRING "@" "(" "#" ")" + +#define show_mptmod_ver(s,ver) \ + printk(KERN_INFO "%s %s\n", s, ver); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Fusion MPT(linux) driver configurable stuff... + */ +#define MPT_MAX_ADAPTERS 16 +#define MPT_MAX_PROTOCOL_DRIVERS 8 + +#define MPT_MISCDEV_BASENAME "mptctl" +#define MPT_MISCDEV_PATHNAME "/dev/" MPT_MISCDEV_BASENAME + +#define MPT_PROCFS_MPTBASEDIR "mpt" + /* chg it to "driver/fusion" ? */ +#define MPT_PROCFS_SUMMARY_NODE MPT_PROCFS_MPTBASEDIR "/summary" +#define MPT_PROCFS_SUMMARY_PATHNAME "/proc/" MPT_PROCFS_SUMMARY_NODE +#define MPT_FW_REV_MAGIC_ID_STRING "FwRev=" + +#ifdef __KERNEL__ /* { */ +#define MPT_MAX_REQ_DEPTH 1023 +#define MPT_REQ_DEPTH 256 +#define MPT_MIN_REQ_DEPTH 128 + +#define MPT_MAX_REPLY_DEPTH MPT_MAX_REQ_DEPTH +#define MPT_REPLY_DEPTH 128 +#define MPT_MIN_REPLY_DEPTH 8 +#define MPT_MAX_REPLIES_PER_ISR 32 + +#define MPT_MAX_FRAME_SIZE 128 +#define MPT_REQ_SIZE 128 +#define MPT_REPLY_SIZE 128 + +#define MPT_SG_BUCKETS_PER_HUNK 1 + +#ifdef MODULE +#define MPT_REQ_DEPTH_RANGE_STR __MODULE_STRING(MPT_MIN_REQ_DEPTH) "-" __MODULE_STRING(MPT_MAX_REQ_DEPTH) +#define MPT_REPLY_DEPTH_RANGE_STR __MODULE_STRING(MPT_MIN_REPLY_DEPTH) "-" __MODULE_STRING(MPT_MAX_REPLY_DEPTH) +#define MPT_REPLY_SIZE_RANGE_STR __MODULE_STRING(MPT_MIN_REPLY_SIZE) "-" __MODULE_STRING(MPT_MAX_FRAME_SIZE) +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * MPT protocol driver defs... + */ +typedef enum { + MPTBASE_DRIVER, /* MPT base class */ + MPTCTL_DRIVER, /* MPT ioctl class */ + MPTSCSIH_DRIVER, /* MPT SCSI host (initiator) class */ + MPTLAN_DRIVER, /* MPT LAN class */ + MPTSTM_DRIVER, /* MPT SCSI target mode class */ + MPTUNKNOWN_DRIVER +} MPT_DRIVER_CLASS; + +/* + * MPT adapter / port / bus / device info structures... + */ + +typedef union _MPT_FRAME_TRACKER { + struct { + struct _MPT_FRAME_HDR *forw; + struct _MPT_FRAME_HDR *back; + u32 arg1; + void *argp1; + } linkage; + /* + * NOTE: On non-32-bit systems, where pointers are LARGE, + * using the linkage pointers destroys our sacred MsgContext + * field contents. But we don't care anymore because these + * are now reset in mpt_put_msg_frame() just prior to sending + * a request off to the IOC. + */ + struct { + u32 __hdr[2]; + /* + * The following _MUST_ match the location of the + * MsgContext field in the MPT message headers. + */ + union { + u32 MsgContext; + struct { + u16 req_idx; /* Request index */ + u8 cb_idx; /* callback function index */ + u8 rsvd; + } fld; + } msgctxu; + } hwhdr; +} MPT_FRAME_TRACKER; + +/* + * We might want to view/access a frame as: + * 1) generic request header + * 2) SCSIIORequest + * 3) SCSIIOReply + * 4) MPIDefaultReply + * 5) frame tracker + */ +typedef struct _MPT_FRAME_HDR { + union { + MPIHeader_t hdr; + SCSIIORequest_t scsireq; + SCSIIOReply_t sreply; + MPIDefaultReply_t reply; + MPT_FRAME_TRACKER frame; + } u; +} MPT_FRAME_HDR; + +typedef struct _MPT_Q_TRACKER { + MPT_FRAME_HDR *head; + MPT_FRAME_HDR *tail; +} MPT_Q_TRACKER; + + +typedef struct _MPT_SGL_HDR { + SGESimple32_t sge[1]; +} MPT_SGL_HDR; + +typedef struct _MPT_SGL64_HDR { + SGESimple64_t sge[1]; +} MPT_SGL64_HDR; + + +typedef struct _Q_ITEM { + struct _Q_ITEM *forw; + struct _Q_ITEM *back; +} Q_ITEM; + +typedef struct _Q_TRACKER { + struct _Q_ITEM *head; + struct _Q_ITEM *tail; +} Q_TRACKER; + + +/* + * Chip-specific stuff... + */ + +typedef enum { + FC909 = 0x0909, + FC919 = 0x0919, + FC929 = 0x0929, + C1030 = 0x1030, + FCUNK = 0xFBAD +} CHIP_TYPE; + +/* + * System interface register set + */ + +typedef struct _SYSIF_REGS +{ + u32 Doorbell; /* 00 System<->IOC Doorbell reg */ + u32 WriteSequence; /* 04 Write Sequence register */ + u32 Diagnostic; /* 08 Diagnostic register */ + u32 TestBase; /* 0C Test Base Address */ + u32 Reserved1[8]; /* 10-2F reserved for future use */ + u32 IntStatus; /* 30 Interrupt Status */ + u32 IntMask; /* 34 Interrupt Mask */ + u32 Reserved2[2]; /* 38-3F reserved for future use */ + u32 RequestFifo; /* 40 Request Post/Free FIFO */ + u32 ReplyFifo; /* 44 Reply Post/Free FIFO */ + u32 Reserved3[2]; /* 48-4F reserved for future use */ + u32 HostIndex; /* 50 Host Index register */ + u32 Reserved4[15]; /* 54-8F */ + u32 Fubar; /* 90 For Fubar usage */ + u32 Reserved5[27]; /* 94-FF */ +} SYSIF_REGS; + +/* + * NOTE: Use MPI_{DOORBELL,WRITESEQ,DIAG}_xxx defs in lsi/mpi.h + * in conjunction with SYSIF_REGS accesses! + */ + + +typedef struct _MPT_ADAPTER +{ + struct _MPT_ADAPTER *forw; + struct _MPT_ADAPTER *back; + int id; /* Unique adapter id {0,1,2,...} */ + int pci_irq; + IOCFactsReply_t facts0; + IOCFactsReply_t factsN; + char name[32]; /* "iocN" */ + char *prod_name; /* "LSIFC9x9" */ + u32 mem_phys; /* == f4020000 (mmap) */ + volatile SYSIF_REGS *chip; /* == c8817000 (mmap) */ + CHIP_TYPE chip_type; + int mem_size; + int alloc_total; + u32 last_state; + int active; + int sod_reset; + unsigned long last_kickstart; + PortFactsReply_t pfacts0; + PortFactsReply_t pfactsN; + LANPage0_t lan_cnfg_page0; + LANPage1_t lan_cnfg_page1; + u8 *reply_alloc; /* Reply frames alloc ptr */ + dma_addr_t reply_alloc_dma; + MPT_FRAME_HDR *reply_frames; /* Reply frames - rounded up! */ + dma_addr_t reply_frames_dma; + int reply_depth; + int reply_sz; + /* We (host driver) get to manage our own RequestQueue! */ + u8 *req_alloc; /* Request frames alloc ptr */ + dma_addr_t req_alloc_dma; + MPT_FRAME_HDR *req_frames; /* Request msg frames for PULL mode! */ + dma_addr_t req_frames_dma; + int req_depth; + int req_sz; + spinlock_t FreeQlock; + MPT_Q_TRACKER FreeQ; + /* Pool of SCSI sense buffers for commands coming from + * the SCSI mid-layer. We have one 256 byte sense buffer + * for each REQ entry. + */ + u8 *sense_buf_pool; + dma_addr_t sense_buf_pool_dma; + int hs_reply_idx; + u32 hs_req[MPT_MAX_FRAME_SIZE/sizeof(u32)]; + u16 hs_reply[MPT_MAX_FRAME_SIZE/sizeof(u16)]; + struct pci_dev *pcidev; + struct _MPT_ADAPTER *alt_ioc; +/* atomic_t userCnt; */ + u8 *memmap; + int mtrr_reg; + struct Scsi_Host *sh; + struct proc_dir_entry *ioc_dentry; +} MPT_ADAPTER; + + +typedef struct _MPT_ADAPTER_TRACKER { + MPT_ADAPTER *head; + MPT_ADAPTER *tail; +} MPT_ADAPTER_TRACKER; + +/* + * New return value convention: + * 1 = Ok to free associated request frame + * 0 = not Ok ... + */ +typedef int (*MPT_CALLBACK)(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply); +typedef int (*MPT_EVHANDLER)(MPT_ADAPTER *ioc, EventNotificationReply_t *evReply); + +/* + * Fibre Channel (SCSI) target device... + */ +typedef struct _FC_TARGET { + struct _FC_TARGET *forw; + struct _FC_TARGET *back; + int bus_id; + int target_id; + int lun_exists[32]; + u8 inquiry_data[36]; + u8 last_sense[256]; +} FC_TARGET; + +typedef struct _FCDEV_TRACKER { + FC_TARGET *head; + FC_TARGET *tail; +} FCDEV_TRACKER; + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Funky (private) macros... + */ +#ifdef MPT_DEBUG +#define dprintk(x) printk x +#else +#define dprintk(x) +#endif + +#ifdef MPT_DEBUG_HANDSHAKE +#define dhsprintk(x) printk x +#else +#define dhsprintk(x) +#endif + +#if defined(MPT_DEBUG) || defined(MPT_DEBUG_MSG_FRAME) +#define dmfprintk(x) printk x +#else +#define dmfprintk(x) +#endif + +#ifdef MPT_DEBUG_IRQ +#define dirqprintk(x) printk x +#else +#define dirqprintk(x) +#endif + +#ifdef MPT_DEBUG_EVENTS +#define deventprintk(x) printk x +#else +#define deventprintk(x) +#endif + +#ifdef MPT_DEBUG_SPINLOCK +#define dslprintk(x) printk x +#else +#define dslprintk(x) +#endif + +#ifdef MPT_DEBUG_SG +#define dsgprintk(x) printk x +#else +#define dsgprintk(x) +#endif + + +#define MPT_INDEX_2_MFPTR(ioc,idx) \ + (MPT_FRAME_HDR*)( (u8*)(ioc)->req_frames + (ioc)->req_sz * (idx) ) + +#define MFPTR_2_MPT_INDEX(ioc,mf) \ + (int)( ((u8*)mf - (u8*)(ioc)->req_frames) / (ioc)->req_sz ) + +#define Q_INIT(q,type) (q)->head = (q)->tail = (type*)(q) +#define Q_IS_EMPTY(q) ((Q_ITEM*)(q)->head == (Q_ITEM*)(q)) + +#define Q_ADD_TAIL(qt,i,type) { \ + Q_TRACKER *_qt = (Q_TRACKER*)(qt); \ + Q_ITEM *oldTail = _qt->tail; \ + (i)->forw = (type*)_qt; \ + (i)->back = (type*)oldTail; \ + oldTail->forw = (Q_ITEM*)(i); \ + _qt->tail = (Q_ITEM*)(i); \ +} + +#define Q_ADD_HEAD(qt,i,type) { \ + Q_TRACKER *_qt = (Q_TRACKER*)(qt); \ + Q_ITEM *oldHead = _qt->head; \ + (i)->forw = (type*)oldHead; \ + (i)->back = (type*)_qt; \ + oldHead->back = (Q_ITEM*)(i); \ + _qt->head = (Q_ITEM*)(i); \ +} + +#define Q_DEL_ITEM(i) { \ + Q_ITEM *_forw = (Q_ITEM*)(i)->forw; \ + Q_ITEM *_back = (Q_ITEM*)(i)->back; \ + _back->forw = _forw; \ + _forw->back = _back; \ +} + + +#define SWAB4(value) \ + (u32)( (((value) & 0x000000ff) << 24) \ + | (((value) & 0x0000ff00) << 8) \ + | (((value) & 0x00ff0000) >> 8) \ + | (((value) & 0xff000000) >> 24) ) + + +#if defined(MPT_DEBUG) || defined(MPT_DEBUG_MSG_FRAME) +#define DBG_DUMP_REPLY_FRAME(mfp) \ + { u32 *m = (u32 *)(mfp); \ + int i, n = (le32_to_cpu(m[0]) & 0x00FF0000) >> 16; \ + printk(KERN_INFO " "); \ + for (i=0; i (b)) ? (a) : (b)) +#endif + +#ifndef offsetof +#define offsetof(t, m) ((size_t) (&((t *)0)->m)) +#endif + +#if defined(__alpha__) || defined(__sparc_v9__) +#define CAST_U32_TO_PTR(x) ((void *)(u64)x) +#define CAST_PTR_TO_U32(x) ((u32)(u64)x) +#else +#define CAST_U32_TO_PTR(x) ((void *)x) +#define CAST_PTR_TO_U32(x) ((u32)x) +#endif + +#define MPT_PROTOCOL_FLAGS_c_c_c_c(pflags) \ + ((pflags) & MPI_PORTFACTS_PROTOCOL_INITIATOR) ? 'I' : 'i', \ + ((pflags) & MPI_PORTFACTS_PROTOCOL_TARGET) ? 'T' : 't', \ + ((pflags) & MPI_PORTFACTS_PROTOCOL_LAN) ? 'L' : 'l', \ + ((pflags) & MPI_PORTFACTS_PROTOCOL_LOGBUSADDR) ? 'B' : 'b' + +/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/mptctl.c linux.ac/drivers/message/fusion/mptctl.c --- linux.vanilla/drivers/message/fusion/mptctl.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/mptctl.c Tue Apr 3 23:12:52 2001 @@ -0,0 +1,1296 @@ +/* + * linux/drivers/message/fusion/mptctl.c + * Fusion MPT misc device (ioctl) driver. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Credits: + * This driver would not exist if not for Alan Cox's development + * of the linux i2o driver. + * + * A huge debt of gratitude is owed to David S. Miller (DaveM) + * for fixing much of the stupid and broken stuff in the early + * driver while porting to sparc64 platform. THANK YOU! + * + * A big THANKS to Eddie C. Dost for fixing the ioctl path + * and most importantly f/w download on sparc64 platform! + * (plus Eddie's other helpful hints and insights) + * + * Thanks to Arnaldo Carvalho de Melo for finding and patching + * a potential memory leak in mpt_ioctl_do_fw_download(), + * and for some kmalloc insight:-) + * + * (see also mptbase.c) + * + * Copyright (c) 1999-2001 LSI Logic Corporation + * Originally By: Steven J. Ralston, Noah Romer + * (mailto:Steve.Ralston@lsil.com) + * + * $Id: mptctl.c,v 1.23 2001/03/21 19:42:31 sralston Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + 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; version 2 of the License. + + 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. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define COPYRIGHT "Copyright (c) 1999-2001 LSI Logic Corporation" +#define MODULEAUTHOR "Steven J. Ralston, Noah Romer" +#include "mptbase.h" + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#define my_NAME "Fusion MPT misc device (ioctl) driver" +#define my_VERSION MPT_LINUX_VERSION_COMMON +#define MYNAM "mptctl" + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR(MODULEAUTHOR); +MODULE_DESCRIPTION(my_NAME); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +static int mptctl_id = -1; +static int rwperf_reset = 0; +static struct semaphore mptctl_syscall_sem_ioc[MPT_MAX_ADAPTERS]; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +static int mpt_ioctl_rwperf(unsigned long arg); +static int mpt_ioctl_rwperf_status(unsigned long arg); +static int mpt_ioctl_rwperf_reset(unsigned long arg); +static int mpt_ioctl_fw_download(unsigned long arg); +static int mpt_ioctl_do_fw_download(int ioc, char *ufwbuf, size_t fwlen); +static int mpt_ioctl_scsi_cmd(unsigned long arg); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Scatter gather list (SGL) sizes and limits... + */ +//#define MAX_SCSI_FRAGS 9 +#define MAX_FRAGS_SPILL1 9 +#define MAX_FRAGS_SPILL2 15 +#define FRAGS_PER_BUCKET (MAX_FRAGS_SPILL2 + 1) + +//#define MAX_CHAIN_FRAGS 64 +//#define MAX_CHAIN_FRAGS (15+15+15+16) +#define MAX_CHAIN_FRAGS (4 * MAX_FRAGS_SPILL2 + 1) + +// Define max sg LIST bytes ( == (#frags + #chains) * 8 bytes each) +// Works out to: 592d bytes! (9+1)*8 + 4*(15+1)*8 +// ^----------------- 80 + 512 +#define MAX_SGL_BYTES ((MAX_FRAGS_SPILL1 + 1 + (4 * FRAGS_PER_BUCKET)) * 8) + +/* linux only seems to ever give 128kB MAX contiguous (GFP_USER) mem bytes */ +#define MAX_KMALLOC_SZ (128*1024) + +struct buflist { + u8 *kptr; + int len; +}; + +#define myMAX_TARGETS (1<<4) +#define myMAX_LUNS (1<<3) +#define myMAX_T_MASK (myMAX_TARGETS-1) +#define myMAX_L_MASK (myMAX_LUNS-1) +static u8 DevInUse[myMAX_TARGETS][myMAX_LUNS] = {{0,0}}; +static u32 DevIosCount[myMAX_TARGETS][myMAX_LUNS] = {{0,0}}; + +static u32 fwReplyBuffer[16]; +static pMPIDefaultReply_t ReplyMsg = NULL; + +/* some private forw protos */ +static SGESimple32_t *kbuf_alloc_2_sgl( int bytes, u32 dir, int *frags, + struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc); +static void kfree_sgl( SGESimple32_t *sgl, dma_addr_t sgl_dma, + struct buflist *buflist, MPT_ADAPTER *ioc); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptctl_syscall_down - Down the MPT adapter syscall semaphore. + * @ioc: Pointer to MPT adapter + * @nonblock: boolean, non-zero if O_NONBLOCK is set + * + * All of the mptctl commands can potentially sleep, which is illegal + * with a spinlock held, thus we perform mutual exclusion here. + * + * Returns negative errno on error, or zero for success. + */ +static inline int +mptctl_syscall_down(MPT_ADAPTER *ioc, int nonblock) +{ + dprintk((KERN_INFO MYNAM "::mpt_syscall_down(%p,%d) called\n", ioc, nonblock)); + + if (nonblock) { + if (down_trylock(&mptctl_syscall_sem_ioc[ioc->id])) + return -EAGAIN; + } else { + if (down_interruptible(&mptctl_syscall_sem_ioc[ioc->id])) + return -ERESTARTSYS; + } + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * This is the callback for any message we have posted. The message itself + * will be returned to the message pool when we return from the IRQ + * + * This runs in irq context so be short and sweet. + */ +static int +mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply) +{ + u8 targ; + + //dprintk((KERN_DEBUG MYNAM ": Got mptctl_reply()!\n")); + + if (req && req->u.hdr.Function == MPI_FUNCTION_SCSI_IO_REQUEST) { + targ = req->u.scsireq.TargetID & myMAX_T_MASK; + DevIosCount[targ][0]--; + } else if (reply && req && req->u.hdr.Function == MPI_FUNCTION_FW_DOWNLOAD) { + // NOTE: Expects/requires non-Turbo reply! + dprintk((KERN_INFO MYNAM ": Caching MPI_FUNCTION_FW_DOWNLOAD reply!\n")); + memcpy(fwReplyBuffer, reply, MIN(sizeof(fwReplyBuffer), 4*reply->u.reply.MsgLength)); + ReplyMsg = (pMPIDefaultReply_t) fwReplyBuffer; + } + + return 1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static loff_t +mptctl_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static ssize_t +mptctl_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + printk(KERN_ERR MYNAM ": ioctl WRITE not yet supported\n"); + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static ssize_t +mptctl_read(struct file *file, char *buf, size_t count, loff_t *ptr) +{ + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * MPT ioctl handler + */ +static int +mpt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct mpt_ioctl_sanity *usanity = (struct mpt_ioctl_sanity *) arg; + struct mpt_ioctl_sanity ksanity; + int iocnum; + unsigned iocnumX; + int nonblock = (file->f_flags & O_NONBLOCK); + int ret; + MPT_ADAPTER *iocp = NULL; + + dprintk((KERN_INFO MYNAM "::mpt_ioctl() called\n")); + + if (copy_from_user(&ksanity, usanity, sizeof(ksanity))) { + printk(KERN_ERR "%s::mpt_ioctl() @%d - " + "Unable to copy mpt_ioctl_sanity data @ %p\n", + __FILE__, __LINE__, (void*)usanity); + return -EFAULT; + } + ret = -ENXIO; /* (-6) No such device or address */ + + /* Verify intended MPT adapter */ + iocnumX = ksanity.iocnum & 0xFF; + if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) || + (iocp == NULL)) { + printk(KERN_ERR "%s::mpt_ioctl() @%d - ioc%d not found!\n", + __FILE__, __LINE__, iocnumX); + return -ENODEV; + } + + if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0) + return ret; + + dprintk((KERN_INFO MYNAM "::mpt_ioctl() - Using %s\n", iocp->name)); + + switch(cmd) { + case MPTRWPERF: + ret = mpt_ioctl_rwperf(arg); + break; + case MPTRWPERF_CHK: + ret = mpt_ioctl_rwperf_status(arg); + break; + case MPTRWPERF_RESET: + ret = mpt_ioctl_rwperf_reset(arg); + break; + case MPTFWDOWNLOAD: + ret = mpt_ioctl_fw_download(arg); + break; + case MPTSCSICMD: + ret = mpt_ioctl_scsi_cmd(arg); + break; + default: + ret = -EINVAL; + } + + up(&mptctl_syscall_sem_ioc[iocp->id]); + + return ret; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int mptctl_open(struct inode *inode, struct file *file) +{ + /* + * Should support multiple management users + */ + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int mptctl_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_ioctl_fw_download(unsigned long arg) +{ + struct mpt_fw_xfer *ufwdl = (struct mpt_fw_xfer *) arg; + struct mpt_fw_xfer kfwdl; + + dprintk((KERN_INFO "mpt_ioctl_fwdl called. mptctl_id = %xh\n", mptctl_id)); //tc + if (copy_from_user(&kfwdl, ufwdl, sizeof(struct mpt_fw_xfer))) { + printk(KERN_ERR "%s@%d::_ioctl_fwdl - " + "Unable to copy mpt_fw_xfer struct @ %p\n", + __FILE__, __LINE__, (void*)ufwdl); + return -EFAULT; + } + + return mpt_ioctl_do_fw_download(kfwdl.iocnum, kfwdl.bufp, kfwdl.fwlen); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * MPT FW Download + */ +static int +mpt_ioctl_do_fw_download(int ioc, char *ufwbuf, size_t fwlen) +{ + FWDownload_t *dlmsg; + MPT_FRAME_HDR *mf; + MPT_ADAPTER *iocp; +// char *fwbuf; +// dma_addr_t fwbuf_dma; + FWDownloadTCSGE_t *fwVoodoo; +// SGEAllUnion_t *fwSgl; + int ret; + + SGESimple32_t *sgl; + SGESimple32_t *sgOut, *sgIn; + dma_addr_t sgl_dma; + struct buflist *buflist; + struct buflist *bl; + int numfrags = 0; + int maxfrags; + int n = 0; + u32 sgdir; + u32 nib; + int fw_bytes_copied = 0; + u16 iocstat; + int i; + + dprintk((KERN_INFO "mpt_ioctl_do_fwdl called. mptctl_id = %xh.\n", mptctl_id)); + + dprintk((KERN_INFO "DbG: kfwdl.bufp = %p\n", ufwbuf)); + dprintk((KERN_INFO "DbG: kfwdl.fwlen = %d\n", (int)fwlen)); + dprintk((KERN_INFO "DbG: kfwdl.ioc = %04xh\n", ioc)); + + if ((ioc = mpt_verify_adapter(ioc, &iocp)) < 0) { + printk("%s@%d::_ioctl_fwdl - ioc%d not found!\n", + __FILE__, __LINE__, ioc); + return -ENXIO; /* (-6) No such device or address */ + } + + if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) + return -EAGAIN; + dlmsg = (FWDownload_t*) mf; + fwVoodoo = (FWDownloadTCSGE_t *) &dlmsg->SGL; + sgOut = (SGESimple32_t *) (fwVoodoo + 1); + + /* + * Construct f/w download request + */ + dlmsg->ImageType = MPI_FW_DOWNLOAD_ITYPE_FW; + dlmsg->Reserved = 0; + dlmsg->ChainOffset = 0; + dlmsg->Function = MPI_FUNCTION_FW_DOWNLOAD; + dlmsg->Reserved1[0] = dlmsg->Reserved1[1] = dlmsg->Reserved1[2] = 0; + dlmsg->MsgFlags = 0; + + fwVoodoo->Reserved = 0; + fwVoodoo->ContextSize = 0; + fwVoodoo->DetailsLength = 12; + fwVoodoo->Flags = MPI_SGE_FLAGS_TRANSACTION_ELEMENT; + fwVoodoo->Reserved1 = 0; + fwVoodoo->ImageOffset = 0; + fwVoodoo->ImageSize = cpu_to_le32(fwlen); + + /* + * Need to kmalloc area(s) for holding firmware image bytes. + * But we need to do it piece meal, using a proper + * scatter gather list (with 128kB MAX hunks). + * + * A practical limit here might be # of sg hunks that fit into + * a single IOC request frame; 12 or 8 (see below), so: + * For FC9xx: 12 x 128kB == 1.5 mB (max) + * For C1030: 8 x 128kB == 1 mB (max) + * We could support chaining, but things get ugly(ier:) + */ + sgdir = 0x04000000; /* IOC will READ from sys mem */ + if ((sgl = kbuf_alloc_2_sgl(fwlen, sgdir, &numfrags, &buflist, &sgl_dma, iocp)) == NULL) + return -ENOMEM; + + /* + * We should only need SGL with 2 simple_32bit entries (up to 256 kB) + * for FC9xx f/w image, but calculate max number of sge hunks + * we can fit into a request frame, and limit ourselves to that. + * (currently no chain support) + * For FC9xx: (128-12-16)/8 = 12.5 = 12 + * For C1030: (96-12-16)/8 = 8.5 = 8 + */ + maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - sizeof(FWDownloadTCSGE_t)) / sizeof(SGESimple32_t); + if (numfrags > maxfrags) { + ret = -EMLINK; + goto fwdl_out; + } + + dprintk((KERN_INFO "DbG: sgl buffer = %p, sgfrags = %d\n", sgl, numfrags)); + + /* + * Parse SG list, copying sgl itself, + * plus f/w image hunks from user space as we go... + */ + ret = -EFAULT; + sgIn = sgl; + bl = buflist; + for (i=0; i < numfrags; i++) { + nib = (sgIn->FlagsLength & 0xF0000000) >> 28; + /* skip ignore/chain. */ + if (nib == 0 || nib == 3) { + ; + } else if (sgIn->Address) { + *sgOut = *sgIn; + n++; + if (copy_from_user(bl->kptr, ufwbuf+fw_bytes_copied, bl->len)) { + printk(KERN_ERR "%s@%d::_ioctl_fwdl - " + "Unable to copy f/w buffer hunk#%d @ %p\n", + __FILE__, __LINE__, n, (void*)ufwbuf); + goto fwdl_out; + } + fw_bytes_copied += bl->len; + } + sgIn++; + bl++; + sgOut++; + } + +#ifdef MPT_DEBUG + { + u32 *m = (u32 *)mf; + printk(KERN_INFO MYNAM ": F/W download request:\n" KERN_INFO " "); + for (i=0; i < 7+numfrags*2; i++) + printk(" %08x", le32_to_cpu(m[i])); + printk("\n"); + } +#endif + + /* + * Finally, perform firmware download. + */ + ReplyMsg = NULL; + mpt_put_msg_frame(mptctl_id, ioc, mf); + + /* + * Wait until the reply has been received + */ + { + int foo = 0; + + while (ReplyMsg == NULL) { + if (!(foo%1000000)) { + dprintk((KERN_INFO "DbG::_do_fwdl: " + "In ReplyMsg loop - iteration %d\n", + foo)); //tc + } + ret = -ETIME; + if (++foo > 60000000) + goto fwdl_out; + mb(); + schedule(); + barrier(); + } + } + + if (sgl) + kfree_sgl(sgl, sgl_dma, buflist, iocp); + + iocstat = le16_to_cpu(ReplyMsg->IOCStatus) & MPI_IOCSTATUS_MASK; + if (iocstat == MPI_IOCSTATUS_SUCCESS) { + printk(KERN_INFO MYNAM ": F/W update successfully sent to %s!\n", iocp->name); + return 0; + } else if (iocstat == MPI_IOCSTATUS_INVALID_FUNCTION) { + printk(KERN_WARNING MYNAM ": ?Hmmm... %s says it doesn't support F/W download!?!\n", + iocp->name); + printk(KERN_WARNING MYNAM ": (time to go bang on somebodies door)\n"); + return -EBADRQC; + } else if (iocstat == MPI_IOCSTATUS_BUSY) { + printk(KERN_WARNING MYNAM ": Warning! %s says: IOC_BUSY!\n", iocp->name); + printk(KERN_WARNING MYNAM ": (try again later?)\n"); + return -EBUSY; + } else { + printk(KERN_WARNING MYNAM "::ioctl_fwdl() ERROR! %s returned [bad] status = %04xh\n", + iocp->name, iocstat); + printk(KERN_WARNING MYNAM ": (bad VooDoo)\n"); + return -ENOMSG; + } + return 0; + +fwdl_out: + kfree_sgl(sgl, sgl_dma, buflist, iocp); + return ret; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * NEW rwperf (read/write performance) stuff starts here... + */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static SGESimple32_t * +kbuf_alloc_2_sgl(int bytes, u32 sgdir, int *frags, + struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc) +{ + SGESimple32_t *sglbuf = NULL; + struct buflist *buflist = NULL; + int numfrags = 0; + int fragcnt = 0; + int alloc_sz = MIN(bytes,MAX_KMALLOC_SZ); // avoid kernel warning msg! + int bytes_allocd = 0; + int this_alloc; + SGESimple32_t *sgl; + u32 pa; // phys addr + SGEChain32_t *last_chain = NULL; + SGEChain32_t *old_chain = NULL; + int chaincnt = 0; + int i, buflist_ent; + int sg_spill = MAX_FRAGS_SPILL1; + int dir; + + *frags = 0; + *blp = NULL; + i = MAX_SGL_BYTES / 8; + buflist = kmalloc(i, GFP_USER); + if (buflist == NULL) + return NULL; + memset(buflist, 0, i); + buflist_ent = 0; + + sglbuf = pci_alloc_consistent(ioc->pcidev, MAX_SGL_BYTES, sglbuf_dma); + if (sglbuf == NULL) + goto free_and_fail; + + if (sgdir & 0x04000000) + dir = PCI_DMA_TODEVICE; + else + dir = PCI_DMA_FROMDEVICE; + + sgl = sglbuf; + while (bytes_allocd < bytes) { + this_alloc = MIN(alloc_sz, bytes-bytes_allocd); + buflist[buflist_ent].len = this_alloc; + buflist[buflist_ent].kptr = pci_alloc_consistent(ioc->pcidev, + this_alloc, + &pa); + if (buflist[buflist_ent].kptr == NULL) { + alloc_sz = alloc_sz / 2; + if (alloc_sz == 0) { + printk(KERN_WARNING MYNAM "-SG: No can do - " + "not enough memory! :-(\n"); + printk(KERN_WARNING MYNAM "-SG: (freeing %d frags)\n", + numfrags); + goto free_and_fail; + } + continue; + } else { + dma_addr_t dma_addr; + + bytes_allocd += this_alloc; + + /* Write one SIMPLE sge */ + sgl->FlagsLength = cpu_to_le32(0x10000000|sgdir|this_alloc); + dma_addr = pci_map_single(ioc->pcidev, buflist[buflist_ent].kptr, this_alloc, dir); + sgl->Address = cpu_to_le32(dma_addr); + + fragcnt++; + numfrags++; + sgl++; + buflist_ent++; + } + + if (bytes_allocd >= bytes) + break; + + /* Need to chain? */ + if (fragcnt == sg_spill) { + dma_addr_t chain_link; + + if (last_chain != NULL) + last_chain->NextChainOffset = 0x1E; + + fragcnt = 0; + sg_spill = MAX_FRAGS_SPILL2; + + /* fixup previous SIMPLE sge */ + sgl[-1].FlagsLength |= cpu_to_le32(0x80000000); + + chain_link = (*sglbuf_dma) + + ((u8 *)(sgl+1) - (u8 *)sglbuf); + + /* Write one CHAIN sge */ + sgl->FlagsLength = cpu_to_le32(0x30000080); + sgl->Address = cpu_to_le32(chain_link); + + old_chain = last_chain; + last_chain = (SGEChain32_t*)sgl; + chaincnt++; + numfrags++; + sgl++; + } + + /* overflow check... */ + if (numfrags*8 > MAX_SGL_BYTES) { + /* GRRRRR... */ + printk(KERN_WARNING MYNAM "-SG: No can do - " + "too many SG frags! :-(\n"); + printk(KERN_WARNING MYNAM "-SG: (freeing %d frags)\n", + numfrags); + goto free_and_fail; + } + } + + /* Last sge fixup: set LE+eol+eob bits */ + sgl[-1].FlagsLength |= cpu_to_le32(0xC1000000); + + /* Chain fixup needed? */ + if (last_chain != NULL && fragcnt < 16) + last_chain->Length = cpu_to_le16(fragcnt * 8); + + *frags = numfrags; + *blp = buflist; + + dprintk((KERN_INFO MYNAM "-SG: kbuf_alloc_2_sgl() - " + "%d SG frags generated! (%d CHAIN%s)\n", + numfrags, chaincnt, chaincnt>1?"s":"")); + + dprintk((KERN_INFO MYNAM "-SG: kbuf_alloc_2_sgl() - " + "last (big) alloc_sz=%d\n", + alloc_sz)); + + return sglbuf; + +free_and_fail: + if (sglbuf != NULL) { + int i; + + for (i = 0; i < numfrags; i++) { + dma_addr_t dma_addr; + u8 *kptr; + int len; + + if ((sglbuf[i].FlagsLength >> 24) == 0x30) + continue; + + dma_addr = le32_to_cpu(sglbuf[i].Address); + kptr = buflist[i].kptr; + len = buflist[i].len; + + pci_free_consistent(ioc->pcidev, len, kptr, dma_addr); + } + pci_free_consistent(ioc->pcidev, MAX_SGL_BYTES, sglbuf, *sglbuf_dma); + } + kfree(buflist); + return NULL; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static void +kfree_sgl(SGESimple32_t *sgl, dma_addr_t sgl_dma, struct buflist *buflist, MPT_ADAPTER *ioc) +{ + SGESimple32_t *sg = sgl; + struct buflist *bl = buflist; + u32 nib; + int dir; + int n = 0; + + if (sg->FlagsLength & 0x04000000) + dir = PCI_DMA_TODEVICE; + else + dir = PCI_DMA_FROMDEVICE; + + nib = (sg->FlagsLength & 0xF0000000) >> 28; + while (! (nib & 0x4)) { /* eob */ + /* skip ignore/chain. */ + if (nib == 0 || nib == 3) { + ; + } else if (sg->Address) { + dma_addr_t dma_addr; + void *kptr; + int len; + + dma_addr = le32_to_cpu(sg->Address); + kptr = bl->kptr; + len = bl->len; + pci_unmap_single(ioc->pcidev, dma_addr, len, dir); + pci_free_consistent(ioc->pcidev, len, kptr, dma_addr); + n++; + } + sg++; + bl++; + nib = (sg->FlagsLength & 0xF0000000) >> 28; + } + + /* we're at eob! */ + if (sg->Address) { + dma_addr_t dma_addr; + void *kptr; + int len; + + dma_addr = le32_to_cpu(sg->Address); + kptr = bl->kptr; + len = bl->len; + pci_unmap_single(ioc->pcidev, dma_addr, len, dir); + pci_free_consistent(ioc->pcidev, len, kptr, dma_addr); + n++; + } + + pci_free_consistent(ioc->pcidev, MAX_SGL_BYTES, sgl, sgl_dma); + kfree(buflist); + dprintk((KERN_INFO MYNAM "-SG: Free'd 1 SGL buf + %d kbufs!\n", n)); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_ioctl_rwperf_init(struct mpt_raw_r_w *dest, unsigned long src, + char *caller, MPT_ADAPTER **iocpp) +{ + char *myname = "_rwperf_init()"; + int ioc; + + /* get copy of structure passed from user space */ + if (copy_from_user(dest, (void*)src, sizeof(*dest))) { + printk(KERN_ERR MYNAM "::%s() @%d - Can't copy mpt_raw_r_w data @ %p\n", + myname, __LINE__, (void*)src); + return -EFAULT; /* (-14) Bad address */ + } else { + dprintk((KERN_INFO MYNAM "-perf: PerfInfo.{ioc,targ,qd,iters,nblks}" + ": %d %d %d %d %d\n", + dest->iocnum, dest->target, + (int)dest->qdepth, dest->iters, dest->nblks )); + dprintk((KERN_INFO MYNAM "-perf: PerfInfo.{cache,skip,range,rdwr,seqran}" + ": %d %d %d %d %d\n", + dest->cache_sz, dest->skip, dest->range, + dest->rdwr, dest->seqran )); + + /* Get the MPT adapter id. */ + if ((ioc = mpt_verify_adapter(dest->iocnum, iocpp)) < 0) { + printk(KERN_ERR MYNAM "::%s() @%d - ioc%d not found!\n", + myname, __LINE__, dest->iocnum); + return -ENXIO; /* (-6) No such device or address */ + } else { + dprintk((MYNAM "-perf: %s using mpt/ioc%x, target %02xh\n", + caller, dest->iocnum, dest->target)); + } + } + + return ioc; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/* Treat first N blocks of disk as sacred! */ +#define SACRED_BLOCKS 100 + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_ioctl_rwperf(unsigned long arg) +{ + struct mpt_raw_r_w kPerfInfo; + /* NOTE: local copy, on stack==KERNEL_SPACE! */ + u8 target, targetM; + u8 lun, lunM; + u8 scsiop; + int qdepth; + int iters; + int cache_sz; + u32 xferbytes; + u32 scsidir; + u32 qtag; + u32 scsictl; + u32 sgdir; + u32 blkno; + u32 sbphys; + SGESimple32_t *sgl; + dma_addr_t sgl_dma; + struct buflist *buflist; + SGESimple32_t *sgOut, *sgIn; + int numfrags; + u32 *msg; + int i; + int ioc; + MPT_FRAME_HDR *mf; + MPT_ADAPTER *iocp; + int sgfragcpycnt; + int blklo, blkhi; + u8 nextchainoffset; + u8 *SenseBuf; + dma_addr_t SenseBufDMA; + char *myname = "_rwperf()"; + + dprintk((KERN_INFO "%s - starting...\n", myname)); + + /* Validate target device */ + if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0) + return ioc; + + /* Allocate DMA'able memory for the sense buffer. */ + SenseBuf = pci_alloc_consistent(iocp->pcidev, 256, &SenseBufDMA); + + /* set perf parameters from input */ + target = kPerfInfo.target & 0x0FF; + targetM = target & myMAX_T_MASK; + lun = kPerfInfo.lun & 0x1F; // LUN=31 max + lunM = lun & myMAX_L_MASK; + qdepth = kPerfInfo.qdepth; + iters = kPerfInfo.iters; + xferbytes = ((u32)kPerfInfo.nblks)<<9; + + DevInUse[targetM][lunM] = 1; + DevIosCount[targetM][lunM] = 0; + + cache_sz = kPerfInfo.cache_sz * 1024; // CacheSz in kB! + + /* ToDo: */ + /* get capacity (?) */ + + + // pre-build, one time, everything we can for speed in the loops below... + + scsiop = 0x28; // default to SCSI READ! + scsidir = MPI_SCSIIO_CONTROL_READ; // DATA IN (host<--ioc<--dev) + // 02000000 + qtag = MPI_SCSIIO_CONTROL_SIMPLEQ; // 00000000 + + if (xferbytes == 0) { + // Do 0-byte READ!!! + // IMPORTANT! Need to set no SCSI DIR for this! + scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER; + } + + scsictl = scsidir | qtag; + + /* + * Set sgdir for DMA transfer. + */ +// sgdir = 0x04000000; // SCSI WRITE + sgdir = 0x00000000; // SCSI READ + + if ((sgl = kbuf_alloc_2_sgl(MAX(512,xferbytes), sgdir, &numfrags, &buflist, &sgl_dma, iocp)) == NULL) + return -ENOMEM; + + sgfragcpycnt = MIN(10,numfrags); + nextchainoffset = 0; + if (numfrags > 10) + nextchainoffset = 0x1E; + + sbphys = SenseBufDMA; + + rwperf_reset = 0; + +// do { // target-loop + + blkno = SACRED_BLOCKS; // Treat first N blocks as sacred! + // FIXME! Skip option + blklo = blkno; + blkhi = blkno; + + do { // inner-loop + + while ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) { + mb(); + schedule(); + barrier(); + } + msg = (u32*)mf; + + /* Start piecing the SCSIIORequest together */ + msg[0] = 0x00000000 | nextchainoffset<<16 | target; + msg[1] = 0x0000FF0A; // 255 sense bytes, 10-byte CDB! + msg[3] = lun << 8; + msg[4] = 0; + msg[5] = scsictl; + + // 16 bytes of CDB @ msg[6,7,8,9] are below... + + msg[6] = ( ((blkno & 0xFF000000) >> 8) + | ((blkno & 0x00FF0000) << 8) + | scsiop ); + msg[7] = ( (((u32)kPerfInfo.nblks & 0x0000FF00) << 16) + | ((blkno & 0x000000FF) << 8) + | ((blkno & 0x0000FF00) >> 8) ); + msg[8] = (kPerfInfo.nblks & 0x00FF); + msg[9] = 0; + + msg[10] = xferbytes; + +// msg[11] = 0xD0000100; +// msg[12] = sbphys; +// msg[13] = 0; + msg[11] = sbphys; + + // Copy the SGL... + if (xferbytes) { + sgOut = (SGESimple32_t*)&msg[12]; + sgIn = sgl; + for (i=0; i < sgfragcpycnt; i++) + *sgOut++ = *sgIn++; + } + + // fubar! QueueDepth issue!!! + while ( !rwperf_reset + && (DevIosCount[targetM][lunM] >= MIN(qdepth,64)) ) + { + mb(); + schedule(); + barrier(); + } + +// blkno += kPerfInfo.nblks; +// EXP Stuff! +// Try optimizing to certain cache size for the target! +// by keeping blkno within cache range if at all possible +#if 0 + if ( cache_sz + && ((2 * kPerfInfo.nblks) <= (cache_sz>>9)) + && ((blkno + kPerfInfo.nblks) > ((cache_sz>>9) + SACRED_BLOCKS)) ) + blkno = SACRED_BLOCKS; + else + blkno += kPerfInfo.nblks; +#endif +// Ok, cheat! + if (cache_sz && ((blkno + kPerfInfo.nblks) > ((cache_sz>>9) + SACRED_BLOCKS)) ) + blkno = SACRED_BLOCKS; + else + blkno += kPerfInfo.nblks; + + if (blkno > blkhi) + blkhi = blkno; + + DevIosCount[targetM][lunM]++; + + /* + * Finally, post the request + */ + mpt_put_msg_frame(mptctl_id, ioc, mf); + + + /* let linux breath! */ + mb(); + schedule(); + barrier(); + + //dprintk((KERN_DEBUG MYNAM "-perf: inner-loop, cnt=%d\n", iters)); + + } while ((--iters > 0) && !rwperf_reset); + + dprintk((KERN_INFO MYNAM "-perf: DbG: blklo=%d, blkhi=%d\n", blklo, blkhi)); + dprintk((KERN_INFO MYNAM "-perf: target-loop, thisTarget=%d\n", target)); + +// // TEMPORARY! +// target = 0; + +// } while (target); + + + if (DevIosCount[targetM][lunM]) { + dprintk((KERN_INFO " DbG: DevIosCount[%d][%d]=%d\n", + targetM, lunM, DevIosCount[targetM][lunM])); + } + + while (DevIosCount[targetM][lunM]) { + //dprintk((KERN_DEBUG " DbG: Waiting... DevIosCount[%d][%d]=%d\n", + // targetM, lunM, DevIosCount[targetM][lunM])); + mb(); + schedule(); + barrier(); + } + DevInUse[targetM][lunM] = 0; + + pci_free_consistent(iocp->pcidev, 256, SenseBuf, SenseBufDMA); + + if (sgl) + kfree_sgl(sgl, sgl_dma, buflist, iocp); + + dprintk((KERN_INFO " *** done ***\n")); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_ioctl_rwperf_status(unsigned long arg) +{ + struct mpt_raw_r_w kPerfInfo; + /* NOTE: local copy, on stack==KERNEL_SPACE! */ + MPT_ADAPTER *iocp; + int ioc; +// u8 targ; +// u8 lun; + int T, L; + char *myname = "_rwperf_status()"; + + + dprintk((KERN_INFO "%s - starting...\n", myname)); + + /* Get a pointer to the MPT adapter. */ + if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0) + return ioc; + + /* set perf parameters from input */ +// targ = kPerfInfo.target & 0xFF; +// lun = kPerfInfo.lun & 0x1F; + + for (T=0; T < myMAX_TARGETS; T++) + for (L=0; L < myMAX_LUNS; L++) + if (DevIosCount[T][L]) { + printk(KERN_INFO "%s: ioc%d->00:%02x:%02x" + ", IosCnt=%d\n", + myname, ioc, T, L, DevIosCount[T][L] ); + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_ioctl_rwperf_reset(unsigned long arg) +{ + struct mpt_raw_r_w kPerfInfo; + /* NOTE: local copy, on stack==KERNEL_SPACE! */ + MPT_ADAPTER *iocp; + int ioc; +// u8 targ; +// u8 lun; + int T, L; + int i; + char *myname = "_rwperf_reset()"; + + dprintk((KERN_INFO "%s - starting...\n", myname)); + + /* Get MPT adapter id. */ + if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0) + return ioc; + + /* set perf parameters from input */ +// targ = kPerfInfo.target & 0xFF; +// lun = kPerfInfo.lun & 0x1F; + + rwperf_reset = 1; + for (i=0; i < 1000000; i++) { + mb(); + schedule(); + barrier(); + } + rwperf_reset = 0; + + for (T=0; T < myMAX_TARGETS; T++) + for (L=0; L < myMAX_LUNS; L++) + if (DevIosCount[T][L]) { + printk(KERN_INFO "%s: ioc%d->00:%02x:%02x, " + "IosCnt RESET! (from %d to 0)\n", + myname, ioc, T, L, DevIosCount[T][L] ); + DevIosCount[T][L] = 0; + DevInUse[T][L] = 0; + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_ioctl_scsi_cmd(unsigned long arg) +{ + return -ENOSYS; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +static struct file_operations mptctl_fops = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,51) + owner: THIS_MODULE, +#endif + llseek: mptctl_llseek, + read: mptctl_read, + write: mptctl_write, + ioctl: mpt_ioctl, + open: mptctl_open, + release: mptctl_release, +}; + +static struct miscdevice mptctl_miscdev = { + MPT_MINOR, + MYNAM, + &mptctl_fops +}; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#if defined(__sparc__) && defined(__sparc_v9__) /*{*/ + +/* The dynamic ioctl32 compat. registry only exists in >2.3.x sparc64 kernels */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/ +extern int register_ioctl32_conversion(unsigned int cmd, + int (*handler)(unsigned int, + unsigned int, + unsigned long, + struct file *)); +int unregister_ioctl32_conversion(unsigned int cmd); + +struct mpt_fw_xfer32 { + unsigned int iocnum; + unsigned int fwlen; + u32 bufp; +}; + +#define MPTFWDOWNLOAD32 _IOWR(MPT_MAGIC_NUMBER,15,struct mpt_fw_xfer32) + +extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +sparc32_mptfwxfer_ioctl(unsigned int fd, unsigned int cmd, + unsigned long arg, struct file *filp) +{ + struct mpt_fw_xfer32 kfw32; + struct mpt_fw_xfer kfw; + MPT_ADAPTER *iocp = NULL; + int iocnum, iocnumX; + int nonblock = (filp->f_flags & O_NONBLOCK); + int ret; + + dprintk((KERN_INFO MYNAM "::sparc32_mptfwxfer_ioctl() called\n")); + + if (copy_from_user(&kfw32, (char *)arg, sizeof(kfw32))) + return -EFAULT; + + /* Verify intended MPT adapter */ + iocnumX = kfw32.iocnum & 0xFF; + if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) || + (iocp == NULL)) { + printk(KERN_ERR MYNAM "::sparc32_mptfwxfer_ioctl @%d - ioc%d not found!\n", + __LINE__, iocnumX); + return -ENODEV; + } + + if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0) + return ret; + + kfw.iocnum = iocnum; + kfw.fwlen = kfw32.fwlen; + kfw.bufp = (void *)(unsigned long)kfw32.bufp; + + ret = mpt_ioctl_do_fw_download(kfw.iocnum, kfw.bufp, kfw.fwlen); + + up(&mptctl_syscall_sem_ioc[iocp->id]); + + return ret; +} + +#if 0 /* { */ +static int +sparc32_mptfwxfer_ioctl(unsigned int fd, unsigned int cmd, + unsigned long arg, struct file *filp) +{ + struct mpt_fw_xfer32 kfw32; + struct mpt_fw_xfer kfw; + mm_segment_t old_fs; + int ret; + + dprintk((KERN_INFO MYNAM "::sparc32_mptfwxfer_ioctl() called\n")); + + if (copy_from_user(&kfw32, (char *)arg, sizeof(kfw32))) + return -EFAULT; + + /* Verify intended MPT adapter */ + iocnumX = kfw32.iocnum & 0xFF; + if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) || + (iocp == NULL)) { + printk(KERN_ERR MYNAM "::sparc32_mptfwxfer_ioctl @%d - ioc%d not found!\n", + __LINE__, iocnumX); + return -ENODEV; + } + + if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0) + return ret; + + kfw.iocnum = iocnum; + kfw.fwlen = kfw32.fwlen; + kfw.bufp = (void *)(unsigned long)kfw32.bufp; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, MPTFWDOWNLOAD, (unsigned long)&kfw); + set_fs(old_fs); + + up(&mptctl_syscall_sem_ioc[iocp->id]); + + return ret; +} +#endif /* #if 0 } */ + +#endif /*} linux >= 2.3.x */ +#endif /*} sparc */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +int __init mptctl_init(void) +{ + int err; + int i; + int where = 1; + + show_mptmod_ver(my_NAME, my_VERSION); + + for (i=0; i= KERNEL_VERSION(2,3,0) /*{*/ + err = register_ioctl32_conversion(MPTRWPERF, NULL); + if (++where && err) goto out_fail; + err = register_ioctl32_conversion(MPTRWPERF_CHK, NULL); + if (++where && err) goto out_fail; + err = register_ioctl32_conversion(MPTRWPERF_RESET, NULL); + if (++where && err) goto out_fail; + err = register_ioctl32_conversion(MPTFWDOWNLOAD32, + sparc32_mptfwxfer_ioctl); + if (++where && err) goto out_fail; +#endif /*} linux >= 2.3.x */ +#endif /*} sparc */ + + if (misc_register(&mptctl_miscdev) == -1) { + printk(KERN_ERR MYNAM ": Can't register misc device [minor=%d].\n", MPT_MINOR); + err = -EBUSY; + goto out_fail; + } + printk(KERN_INFO MYNAM ": Registered with Fusion MPT base driver\n"); + printk(KERN_INFO MYNAM ": /dev/%s @ (major,minor=%d,%d)\n", + mptctl_miscdev.name, MISC_MAJOR, mptctl_miscdev.minor); + + /* + * Install our handler + */ + ++where; + if ((mptctl_id = mpt_register(mptctl_reply, MPTCTL_DRIVER)) < 0) { + printk(KERN_ERR MYNAM ": ERROR: Failed to register with Fusion MPT base driver\n"); + misc_deregister(&mptctl_miscdev); + err = -EBUSY; + goto out_fail; + } + + return 0; + +out_fail: + +#if defined(__sparc__) && defined(__sparc_v9__) /*{*/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/ + printk(KERN_ERR MYNAM ": ERROR: Failed to register ioctl32_conversion!" + " (%d:err=%d)\n", where, err); + unregister_ioctl32_conversion(MPTRWPERF); + unregister_ioctl32_conversion(MPTRWPERF_CHK); + unregister_ioctl32_conversion(MPTRWPERF_RESET); + unregister_ioctl32_conversion(MPTFWDOWNLOAD32); +#endif /*} linux >= 2.3.x */ +#endif /*} sparc */ + + return err; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +void mptctl_exit(void) +{ + misc_deregister(&mptctl_miscdev); + printk(KERN_INFO MYNAM ": /dev/%s @ (major,minor=%d,%d)\n", + mptctl_miscdev.name, MISC_MAJOR, mptctl_miscdev.minor); + printk(KERN_INFO MYNAM ": Deregistered from Fusion MPT base driver\n"); + + mpt_deregister(mptctl_id); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +module_init(mptctl_init); +module_exit(mptctl_exit); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/mptlan.c linux.ac/drivers/message/fusion/mptlan.c --- linux.vanilla/drivers/message/fusion/mptlan.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/mptlan.c Tue Apr 3 17:54:47 2001 @@ -0,0 +1,1462 @@ +/* + * linux/drivers/message/fusion/mptlan.c + * IP Over Fibre Channel device driver. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Credits: + * This driver would not exist if not for Alan Cox's development + * of the linux i2o driver. + * + * Special thanks goes to the I2O LAN driver people at the + * University of Helsinki, who, unbeknownst to them, provided + * the inspiration and initial structure for this driver. + * + * A huge debt of gratitude is owed to David S. Miller (DaveM) + * for fixing much of the stupid and broken stuff in the early + * driver while porting to sparc64 platform. THANK YOU! + * + * A really huge debt of gratitude is owed to Eddie C. Dost + * for gobs of hard work fixing and optimizing LAN code. + * THANK YOU! + * + * (see also mptbase.c) + * + * Copyright (c) 2000-2001 LSI Logic Corporation + * Originally By: Noah Romer + * + * $Id: mptlan.c,v 1.25 2001/03/02 22:12:04 sralston Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + 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; version 2 of the License. + + 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. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Define statements used for debugging + */ +//#define MPT_LAN_IO_DEBUG + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include "mptlan.h" +#include +#include +#include + +#define MYNAM "mptlan" + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * MPT LAN message sizes without variable part. + */ +#define MPT_LAN_RECEIVE_POST_REQUEST_SIZE \ + (sizeof(LANReceivePostRequest_t) - sizeof(SGE_MPI_UNION)) + +#define MPT_LAN_TRANSACTION32_SIZE \ + (sizeof(SGETransaction32_t) - sizeof(u32)) + +/* + * Fusion MPT LAN private structures + */ + +struct BufferControl { + struct sk_buff *skb; + dma_addr_t dma; + unsigned int len; +}; + +struct mpt_lan_priv { + MPT_ADAPTER *mpt_dev; + u8 pnum; /* Port number in the IOC. This is not a Unix network port! */ + + atomic_t buckets_out; /* number of unused buckets on IOC */ + int bucketthresh; /* Send more when this many used */ + + int *mpt_txfidx; /* Free Tx Context list */ + int mpt_txfidx_tail; + spinlock_t txfidx_lock; + + int *mpt_rxfidx; /* Free Rx Context list */ + int mpt_rxfidx_tail; + spinlock_t rxfidx_lock; + + struct BufferControl *RcvCtl; /* Receive BufferControl structs */ + struct BufferControl *SendCtl; /* Send BufferControl structs */ + + int max_buckets_out; /* Max buckets to send to IOC */ + int tx_max_out; /* IOC's Tx queue len */ + + u32 total_posted; + u32 total_received; + struct net_device_stats stats; /* Per device statistics */ + + struct tq_struct post_buckets_task; + unsigned long post_buckets_active; +}; + +struct mpt_lan_ohdr { + u16 dtype; + u8 daddr[FC_ALEN]; + u16 stype; + u8 saddr[FC_ALEN]; +}; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/* + * Forward protos... + */ +static int lan_reply (MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, + MPT_FRAME_HDR *reply); +static int mpt_lan_open(struct net_device *dev); +static int mpt_lan_reset(struct net_device *dev); +static int mpt_lan_close(struct net_device *dev); +static void mpt_lan_post_receive_buckets(void *dev_id); +static void mpt_lan_wake_post_buckets_task(struct net_device *dev); +static int mpt_lan_receive_post_turbo(struct net_device *dev, u32 tmsg); +static int mpt_lan_receive_post_reply(struct net_device *dev, + LANReceivePostReply_t *pRecvRep); +static int mpt_lan_send_turbo(struct net_device *dev, u32 tmsg); +static int mpt_lan_send_reply(struct net_device *dev, + LANSendReply_t *pSendRep); +static int mpt_lan_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply); +static unsigned short mpt_lan_type_trans(struct sk_buff *skb, + struct net_device *dev); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Fusion MPT LAN private data + */ +static int LanCtx = -1; + +static u32 max_buckets_out = 127; +static u32 tx_max_out_p = 127 - 16; + +static struct net_device *mpt_landev[MPT_MAX_ADAPTERS+1]; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Fusion MPT LAN external data + */ +extern int mpt_lan_index; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * lan_reply - Handle all data sent from the hardware. + * @ioc: Pointer to MPT_ADAPTER structure + * @mf: Pointer to original MPT request frame (NULL if TurboReply) + * @reply: Pointer to MPT reply frame + * + * Returns 1 indicating original alloc'd request frame ptr + * should be freed, or 0 if it shouldn't. + */ +static int +lan_reply (MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply) +{ + struct net_device *dev = mpt_landev[ioc->id]; + int FreeReqFrame = 0; + + dioprintk((KERN_INFO MYNAM ": %s/%s: Got reply.\n", + IOC_AND_NETDEV_NAMES_s_s(dev))); + +// dioprintk((KERN_INFO MYNAM "@lan_reply: mf = %p, reply = %p\n", +// mf, reply)); + + if (mf == NULL) { + u32 tmsg = CAST_PTR_TO_U32(reply); + + dioprintk((KERN_INFO MYNAM ": %s/%s: @lan_reply, tmsg %08x\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + tmsg)); + + switch (GET_LAN_FORM(tmsg)) { + + // NOTE! (Optimization) First case here is now caught in + // mptbase.c::mpt_interrupt() routine and callcack here + // is now skipped for this case! 20001218 -sralston +#if 0 + case LAN_REPLY_FORM_MESSAGE_CONTEXT: +// dioprintk((KERN_INFO MYNAM "/lan_reply: " +// "MessageContext turbo reply received\n")); + FreeReqFrame = 1; + break; +#endif + + case LAN_REPLY_FORM_SEND_SINGLE: +// dioprintk((MYNAM "/lan_reply: " +// "calling mpt_lan_send_reply (turbo)\n")); + + // Potential BUG here? -sralston + // FreeReqFrame = mpt_lan_send_turbo(dev, tmsg); + // If/when mpt_lan_send_turbo would return 1 here, + // calling routine (mptbase.c|mpt_interrupt) + // would Oops because mf has already been set + // to NULL. So after return from this func, + // mpt_interrupt() will attempt to put (NULL) mf ptr + // item back onto it's adapter FreeQ - Oops!:-( + // It's Ok, since mpt_lan_send_turbo() *currently* + // always returns 0, but..., just in case: + + (void) mpt_lan_send_turbo(dev, tmsg); + FreeReqFrame = 0; + + break; + + case LAN_REPLY_FORM_RECEIVE_SINGLE: +// dioprintk((KERN_INFO MYNAM "@lan_reply: " +// "rcv-Turbo = %08x\n", tmsg)); + mpt_lan_receive_post_turbo(dev, tmsg); + break; + + default: + printk (KERN_ERR MYNAM "/lan_reply: Got a turbo reply " + "that I don't know what to do with\n"); + + /* CHECKME! Hmmm... FreeReqFrame is 0 here; is that right? */ + + break; + } + + return FreeReqFrame; + } + +// msg = (u32 *) reply; +// dioprintk((KERN_INFO MYNAM "@lan_reply: msg = %08x %08x %08x %08x\n", +// le32_to_cpu(msg[0]), le32_to_cpu(msg[1]), +// le32_to_cpu(msg[2]), le32_to_cpu(msg[3]))); +// dioprintk((KERN_INFO MYNAM "@lan_reply: Function = %02xh\n", +// reply->u.hdr.Function)); + + switch (reply->u.hdr.Function) { + + case MPI_FUNCTION_LAN_SEND: + { + LANSendReply_t *pSendRep; + + pSendRep = (LANSendReply_t *) reply; + FreeReqFrame = mpt_lan_send_reply(dev, pSendRep); + break; + } + + case MPI_FUNCTION_LAN_RECEIVE: + { + LANReceivePostReply_t *pRecvRep; + + pRecvRep = (LANReceivePostReply_t *) reply; + if (pRecvRep->NumberOfContexts) { + mpt_lan_receive_post_reply(dev, pRecvRep); + if (!(pRecvRep->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)) + FreeReqFrame = 1; + } else + dioprintk((KERN_INFO MYNAM "@lan_reply: zero context " + "ReceivePostReply received.\n")); + break; + } + + case MPI_FUNCTION_LAN_RESET: + /* Just a default reply. Might want to check it to + * make sure that everything went ok. + */ + FreeReqFrame = 1; + break; + + case MPI_FUNCTION_EVENT_NOTIFICATION: + case MPI_FUNCTION_EVENT_ACK: + /* UPDATE! 20010120 -sralston + * _EVENT_NOTIFICATION should NOT come down this path any more. + * Should be routed to mpt_lan_event_process(), but just in case... + */ + FreeReqFrame = 1; + break; + + default: + printk (KERN_ERR MYNAM "/lan_reply: Got a non-turbo " + "reply that I don't know what to do with\n"); + + /* CHECKME! Hmmm... FreeReqFrame is 0 here; is that right? */ + FreeReqFrame = 1; + + break; + } + + return FreeReqFrame; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_lan_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) +{ + dprintk((KERN_INFO MYNAM ": MPT event routed to LAN driver!\n")); + + switch (le32_to_cpu(pEvReply->Event)) { + case MPI_EVENT_NONE: /* 00 */ + case MPI_EVENT_LOG_DATA: /* 01 */ + case MPI_EVENT_STATE_CHANGE: /* 02 */ + case MPI_EVENT_UNIT_ATTENTION: /* 03 */ + case MPI_EVENT_IOC_BUS_RESET: /* 04 */ + case MPI_EVENT_EXT_BUS_RESET: /* 05 */ + case MPI_EVENT_RESCAN: /* 06 */ + /* Ok, do we need to do anything here? As far as + I can tell, this is when a new device gets added + to the loop. */ + case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */ + case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */ + case MPI_EVENT_LOGOUT: /* 09 */ + case MPI_EVENT_EVENT_CHANGE: /* 0A */ + default: + break; + } + + /* + * NOTE: pEvent->AckRequired handling now done in mptbase.c; + * Do NOT do it here now! + */ + + return 1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_lan_open(struct net_device *dev) +{ + struct mpt_lan_priv *priv = (struct mpt_lan_priv *) dev->priv; + int i; + + mpt_lan_reset(dev); + + priv->mpt_txfidx = kmalloc(priv->tx_max_out * sizeof(int), GFP_KERNEL); + if (priv->mpt_txfidx == NULL) + goto out; + priv->mpt_txfidx_tail = -1; + + priv->SendCtl = kmalloc(priv->tx_max_out * sizeof(struct BufferControl), + GFP_KERNEL); + if (priv->SendCtl == NULL) + goto out_mpt_txfidx; + for (i = 0; i < priv->tx_max_out; i++) { + memset(&priv->SendCtl[i], 0, sizeof(struct BufferControl)); + priv->mpt_txfidx[++priv->mpt_txfidx_tail] = i; + } + + dprintk((KERN_INFO MYNAM "@lo: Finished initializing SendCtl\n")); + + priv->mpt_rxfidx = kmalloc(priv->max_buckets_out * sizeof(int), + GFP_KERNEL); + if (priv->mpt_rxfidx == NULL) + goto out_SendCtl; + priv->mpt_rxfidx_tail = -1; + + priv->RcvCtl = kmalloc(priv->max_buckets_out * + sizeof(struct BufferControl), + GFP_KERNEL); + if (priv->RcvCtl == NULL) + goto out_mpt_rxfidx; + for (i = 0; i < priv->max_buckets_out; i++) { + memset(&priv->RcvCtl[i], 0, sizeof(struct BufferControl)); + priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = i; + } + +/**/ dprintk((KERN_INFO MYNAM "/lo: txfidx contains - ")); +/**/ for (i = 0; i < priv->tx_max_out; i++) +/**/ dprintk((" %xh", priv->mpt_txfidx[i])); +/**/ dprintk(("\n")); + + dprintk((KERN_INFO MYNAM "/lo: Finished initializing RcvCtl\n")); + + mpt_lan_post_receive_buckets(dev); + printk(KERN_INFO MYNAM ": %s/%s: interface up & active\n", + IOC_AND_NETDEV_NAMES_s_s(dev)); + + if (mpt_event_register(LanCtx, mpt_lan_event_process) != 0) { + /* FIXME! */ + } + + netif_start_queue(dev); + dprintk((KERN_INFO MYNAM "/lo: Done.\n")); + + return 0; +out_mpt_rxfidx: + kfree(priv->mpt_rxfidx); + priv->mpt_rxfidx = NULL; +out_SendCtl: + kfree(priv->SendCtl); + priv->SendCtl = NULL; +out_mpt_txfidx: + kfree(priv->mpt_txfidx); + priv->mpt_txfidx = NULL; +out: return -ENOMEM; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_lan_reset(struct net_device *dev) +{ + MPT_FRAME_HDR *mf; + LANResetRequest_t *pResetReq; + struct mpt_lan_priv *priv = (struct mpt_lan_priv *)dev->priv; + + mf = mpt_get_msg_frame(LanCtx, priv->mpt_dev->id); + + if (mf == NULL) { +/* dprintk((KERN_ERR MYNAM "/reset: Evil funkiness abounds! " + "Unable to allocate a request frame.\n")); +*/ + return -1; + } + + pResetReq = (LANResetRequest_t *) mf; + + pResetReq->Function = MPI_FUNCTION_LAN_RESET; + pResetReq->ChainOffset = 0; + pResetReq->Reserved = 0; + pResetReq->PortNumber = priv->pnum; + pResetReq->MsgFlags = 0; + pResetReq->Reserved2 = 0; + + mpt_put_msg_frame(LanCtx, priv->mpt_dev->id, mf); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_lan_close(struct net_device *dev) +{ + struct mpt_lan_priv *priv = (struct mpt_lan_priv *) dev->priv; + MPT_ADAPTER *mpt_dev = priv->mpt_dev; + unsigned int timeout; + int i; + + dprintk((KERN_INFO MYNAM ": mpt_lan_close called\n")); + + mpt_event_deregister(LanCtx); + + dprintk((KERN_INFO MYNAM ":lan_close: Posted %d buckets " + "since driver was loaded, %d still out\n", + priv->total_posted,atomic_read(&priv->buckets_out))); + + netif_stop_queue(dev); + + mpt_lan_reset(dev); + + timeout = 2 * HZ; + while (atomic_read(&priv->buckets_out) && --timeout) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + for (i = 0; i < priv->max_buckets_out; i++) { + if (priv->RcvCtl[i].skb != NULL) { +/**/ dprintk((KERN_INFO MYNAM "/lan_close: bucket %05x " +/**/ "is still out\n", i)); + pci_unmap_single(mpt_dev->pcidev, priv->RcvCtl[i].dma, + priv->RcvCtl[i].len, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(priv->RcvCtl[i].skb); + } + } + + kfree (priv->RcvCtl); + kfree (priv->mpt_rxfidx); + + for (i = 0; i < priv->tx_max_out; i++) { + if (priv->SendCtl[i].skb != NULL) { + pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[i].dma, + priv->SendCtl[i].len, + PCI_DMA_TODEVICE); + dev_kfree_skb(priv->SendCtl[i].skb); + } + } + + kfree(priv->SendCtl); + kfree(priv->mpt_txfidx); + + atomic_set(&priv->buckets_out, 0); + + printk(KERN_INFO MYNAM ": %s/%s: interface down & inactive\n", + IOC_AND_NETDEV_NAMES_s_s(dev)); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static struct net_device_stats * +mpt_lan_get_stats(struct net_device *dev) +{ + struct mpt_lan_priv *priv = (struct mpt_lan_priv *)dev->priv; + + return (struct net_device_stats *) &priv->stats; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_lan_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < MPT_LAN_MIN_MTU) || (new_mtu > MPT_LAN_MAX_MTU)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* Tx timeout handler. */ +static void +mpt_lan_tx_timeout(struct net_device *dev) +{ + netif_wake_queue(dev); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +//static inline int +static int +mpt_lan_send_turbo(struct net_device *dev, u32 tmsg) +{ + struct mpt_lan_priv *priv = (struct mpt_lan_priv *) dev->priv; + MPT_ADAPTER *mpt_dev = priv->mpt_dev; + struct sk_buff *sent; + unsigned long flags; + u32 ctx; + + ctx = GET_LAN_BUFFER_CONTEXT(tmsg); + sent = priv->SendCtl[ctx].skb; + + priv->stats.tx_packets++; + priv->stats.tx_bytes += sent->len; + + dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + __FUNCTION__, sent)); + + priv->SendCtl[ctx].skb = NULL; + pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[ctx].dma, + priv->SendCtl[ctx].len, PCI_DMA_TODEVICE); + dev_kfree_skb_irq(sent); + + spin_lock_irqsave(&priv->txfidx_lock, flags); + priv->mpt_txfidx[++priv->mpt_txfidx_tail] = ctx; + spin_unlock_irqrestore(&priv->txfidx_lock, flags); + + netif_wake_queue(dev); + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_lan_send_reply(struct net_device *dev, LANSendReply_t *pSendRep) +{ + struct mpt_lan_priv *priv = (struct mpt_lan_priv *) dev->priv; + MPT_ADAPTER *mpt_dev = priv->mpt_dev; + struct sk_buff *sent; + unsigned long flags; + int FreeReqFrame = 0; + u32 *pContext; + u32 ctx; + u8 count; + + count = pSendRep->NumberOfContexts; + + dioprintk((KERN_INFO MYNAM ": send_reply: IOCStatus: %04x\n", + le16_to_cpu(pSendRep->IOCStatus))); + + /* Add check for Loginfo Flag in IOCStatus */ + + switch (le16_to_cpu(pSendRep->IOCStatus)) { + case MPI_IOCSTATUS_SUCCESS: + priv->stats.tx_packets += count; + break; + + case MPI_IOCSTATUS_LAN_CANCELED: + case MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED: + break; + + case MPI_IOCSTATUS_INVALID_SGL: + priv->stats.tx_errors += count; + printk (KERN_ERR MYNAM ": %s/%s: ERROR - Invalid SGL sent to IOC!\n", + IOC_AND_NETDEV_NAMES_s_s(dev)); + goto out; + + default: + priv->stats.tx_errors += count; + break; + } + + pContext = &pSendRep->BufferContext; + + spin_lock_irqsave(&priv->txfidx_lock, flags); + while (count > 0) { + ctx = GET_LAN_BUFFER_CONTEXT(le32_to_cpu(*pContext)); + + sent = priv->SendCtl[ctx].skb; + priv->stats.tx_bytes += sent->len; + + dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + __FUNCTION__, sent)); + + priv->SendCtl[ctx].skb = NULL; + pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[ctx].dma, + priv->SendCtl[ctx].len, PCI_DMA_TODEVICE); + dev_kfree_skb_irq(sent); + + priv->mpt_txfidx[++priv->mpt_txfidx_tail] = ctx; + + pContext++; + count--; + } + spin_unlock_irqrestore(&priv->txfidx_lock, flags); + +out: + if (!(pSendRep->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)) + FreeReqFrame = 1; + + netif_wake_queue(dev); + return FreeReqFrame; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev) +{ + struct mpt_lan_priv *priv = (struct mpt_lan_priv *) dev->priv; + MPT_ADAPTER *mpt_dev = priv->mpt_dev; + MPT_FRAME_HDR *mf; + LANSendRequest_t *pSendReq; + SGETransaction32_t *pTrans; + SGESimple64_t *pSimple; + dma_addr_t dma; + unsigned long flags; + int ctx; + + dioprintk((KERN_INFO MYNAM ": %s called, skb_addr = %p\n", + __FUNCTION__, skb)); + + spin_lock_irqsave(&priv->txfidx_lock, flags); + if (priv->mpt_txfidx_tail < 0) { + netif_stop_queue(dev); + spin_unlock_irqrestore(&priv->txfidx_lock, flags); + + printk (KERN_ERR "%s: no tx context available: %u\n", + __FUNCTION__, priv->mpt_txfidx_tail); + return 1; + } + + mf = mpt_get_msg_frame(LanCtx, mpt_dev->id); + if (mf == NULL) { + netif_stop_queue(dev); + dev_kfree_skb(skb); + spin_unlock_irqrestore(&priv->txfidx_lock, flags); + + printk (KERN_ERR "%s: Unable to alloc request frame\n", + __FUNCTION__); + return 1; + } + + ctx = priv->mpt_txfidx[priv->mpt_txfidx_tail--]; + spin_unlock_irqrestore(&priv->txfidx_lock, flags); + +// dioprintk((KERN_INFO MYNAM ": %s/%s: Creating new msg frame (send).\n", +// IOC_AND_NETDEV_NAMES_s_s(dev))); + + pSendReq = (LANSendRequest_t *) mf; + + /* Set the mac.raw pointer, since this apparently isn't getting + * done before we get the skb. Pull the data pointer past the mac data. + */ + skb->mac.raw = skb->data; + skb_pull(skb, 12); + + dma = pci_map_single(mpt_dev->pcidev, skb->data, skb->len, + PCI_DMA_TODEVICE); + + priv->SendCtl[ctx].skb = skb; + priv->SendCtl[ctx].dma = dma; + priv->SendCtl[ctx].len = skb->len; + + /* Message Header */ + pSendReq->Function = MPI_FUNCTION_LAN_SEND; + pSendReq->ChainOffset = 0; + pSendReq->MsgFlags = 0; + pSendReq->PortNumber = priv->pnum; + + /* Transaction Context Element */ + pTrans = (SGETransaction32_t *) pSendReq->SG_List; + + /* No Flags, 8 bytes of Details, 32bit Context (bloody turbo replies) */ + pTrans->ContextSize = sizeof(u32); + pTrans->DetailsLength = 2 * sizeof(u32); + pTrans->Flags = 0; + pTrans->TransactionContext[0] = cpu_to_le32(ctx); + +// dioprintk((KERN_INFO MYNAM ": %s/%s: BC = %08x, skb = %p, buff = %p\n", +// IOC_AND_NETDEV_NAMES_s_s(dev), +// ctx, skb, skb->data)); + + pTrans->TransactionDetails[0] = cpu_to_le32((0x1000 << 16) | + (skb->mac.raw[0] << 8) | + (skb->mac.raw[1] << 0)); + pTrans->TransactionDetails[1] = cpu_to_le32((skb->mac.raw[2] << 24) | + (skb->mac.raw[3] << 16) | + (skb->mac.raw[4] << 8) | + (skb->mac.raw[5] << 0)); + + pSimple = (SGESimple64_t *) &pTrans->TransactionDetails[2]; + + pSimple->FlagsLength = cpu_to_le32( + ((MPI_SGE_FLAGS_END_OF_BUFFER | + MPI_SGE_FLAGS_SIMPLE_ELEMENT | + MPI_SGE_FLAGS_64_BIT_ADDRESSING | + MPI_SGE_FLAGS_END_OF_LIST) << MPI_SGE_FLAGS_SHIFT) | + skb->len); + pSimple->Address.Low = cpu_to_le32((u32) dma); + if (sizeof(dma_addr_t) > sizeof(u32)) + pSimple->Address.High = cpu_to_le32((u32) ((u64) dma >> 32)); + else + pSimple->Address.High = 0; + + mpt_put_msg_frame (LanCtx, mpt_dev->id, mf); + dev->trans_start = jiffies; + + dioprintk((KERN_INFO MYNAM ": %s/%s: Sending packet. FlagsLength = %08x.\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + le32_to_cpu(pSimple->FlagsLength))); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static inline void +mpt_lan_wake_post_buckets_task(struct net_device *dev) +{ + struct mpt_lan_priv *priv = dev->priv; + + if (test_and_set_bit(0, &priv->post_buckets_active) == 0) { + queue_task(&priv->post_buckets_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + dioprintk((KERN_INFO MYNAM ": %s/%s: Queued post_buckets task.\n", + IOC_AND_NETDEV_NAMES_s_s(dev) )); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static inline int +mpt_lan_receive_skb(struct net_device *dev, struct sk_buff *skb) +{ + struct mpt_lan_priv *priv = dev->priv; + + skb->protocol = mpt_lan_type_trans(skb, dev); + + dioprintk((KERN_INFO MYNAM ": %s/%s: Incoming packet (%d bytes) " + "delivered to upper level.\n", + IOC_AND_NETDEV_NAMES_s_s(dev), skb->len)); + + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + skb->dev = dev; + netif_rx(skb); + + dioprintk((MYNAM "/receive_skb: %d buckets remaining\n", + atomic_read(&priv->buckets_out))); + + if (atomic_read(&priv->buckets_out) < priv->bucketthresh) + mpt_lan_wake_post_buckets_task(dev); + + dioprintk((KERN_INFO MYNAM "/receive_post_reply: %d buckets " + "remaining, %d received back since sod\n", + atomic_read(&priv->buckets_out), priv->total_received)); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +//static inline int +static int +mpt_lan_receive_post_turbo(struct net_device *dev, u32 tmsg) +{ + struct mpt_lan_priv *priv = dev->priv; + MPT_ADAPTER *mpt_dev = priv->mpt_dev; + struct sk_buff *skb, *old_skb; + unsigned long flags; + u32 ctx, len; + + ctx = GET_LAN_BUCKET_CONTEXT(tmsg); + skb = priv->RcvCtl[ctx].skb; + + len = GET_LAN_PACKET_LENGTH(tmsg); + + if (len < MPT_LAN_RX_COPYBREAK) { + old_skb = skb; + + skb = (struct sk_buff *)dev_alloc_skb(len); + if (!skb) { + printk (KERN_ERR MYNAM ": %s/%s: ERROR - Can't allocate skb! (%s@%d)\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + __FILE__, __LINE__); + return -ENOMEM; + } + + pci_dma_sync_single(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); + + memcpy(skb_put(skb, len), old_skb->data, len); + + goto out; + } + + skb_put(skb, len); + + priv->RcvCtl[ctx].skb = NULL; + + pci_unmap_single(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); + +out: + spin_lock_irqsave(&priv->rxfidx_lock, flags); + priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx; + spin_unlock_irqrestore(&priv->rxfidx_lock, flags); + + atomic_dec(&priv->buckets_out); + priv->total_received++; + + return mpt_lan_receive_skb(dev, skb); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_lan_receive_post_free(struct net_device *dev, + LANReceivePostReply_t *pRecvRep) +{ + struct mpt_lan_priv *priv = dev->priv; + MPT_ADAPTER *mpt_dev = priv->mpt_dev; + unsigned long flags; + struct sk_buff *skb; + u32 ctx; + u8 count; + int i; + + count = pRecvRep->NumberOfContexts; + +/**/ dprintk((KERN_INFO MYNAM "/receive_post_reply: " + "IOC returned %d buckets, freeing them...\n", count)); + + spin_lock_irqsave(&priv->rxfidx_lock, flags); + for (i = 0; i < count; i++) { + ctx = le32_to_cpu(pRecvRep->BucketContext[i]); + + skb = priv->RcvCtl[ctx].skb; + +// dprintk((KERN_INFO MYNAM ": %s: dev_name = %s\n", +// IOC_AND_NETDEV_NAMES_s_s(dev))); +// dprintk((KERN_INFO MYNAM "@rpr[2], priv = %p, buckets_out addr = %p", +// priv, &(priv->buckets_out))); +// dprintk((KERN_INFO MYNAM "@rpr[2] TC + 3\n")); + + priv->RcvCtl[ctx].skb = NULL; + pci_unmap_single(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + + priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx; + } + spin_unlock_irqrestore(&priv->rxfidx_lock, flags); + + atomic_sub(count, &priv->buckets_out); + +// for (i = 0; i < priv->max_buckets_out; i++) +// if (priv->RcvCtl[i].skb != NULL) +// dprintk((KERN_INFO MYNAM "@rpr: bucket %03x " +// "is still out\n", i)); + +/* dprintk((KERN_INFO MYNAM "/receive_post_reply: freed %d buckets\n", + count)); +*/ +/**/ dprintk((KERN_INFO MYNAM "@receive_post_reply: %d buckets " +/**/ "remaining, %d received back since sod.\n", +/**/ atomic_read(&priv->buckets_out), priv->total_received)); + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mpt_lan_receive_post_reply(struct net_device *dev, + LANReceivePostReply_t *pRecvRep) +{ + struct mpt_lan_priv *priv = dev->priv; + MPT_ADAPTER *mpt_dev = priv->mpt_dev; + struct sk_buff *skb, *old_skb; + unsigned long flags; + u32 len, ctx; + u32 offset; + u8 count; + int i, l; + + dioprintk((KERN_INFO MYNAM ": mpt_lan_receive_post_reply called\n")); + dioprintk((KERN_INFO MYNAM ": receive_post_reply: IOCStatus: %04x\n", + le16_to_cpu(pRecvRep->IOCStatus))); + + if (le16_to_cpu(pRecvRep->IOCStatus) & MPI_IOCSTATUS_LAN_CANCELED) + return mpt_lan_receive_post_free(dev, pRecvRep); + + len = le32_to_cpu(pRecvRep->PacketLength); + if (len == 0) { + printk (KERN_ERR MYNAM ": %s/%s: ERROR - Got a non-TURBO " + "ReceivePostReply w/ PacketLength zero!\n", + IOC_AND_NETDEV_NAMES_s_s(dev)); + printk (KERN_ERR MYNAM ": MsgFlags = %02x, IOCStatus = %04x\n", + pRecvRep->MsgFlags, le16_to_cpu(pRecvRep->IOCStatus)); + return -1; + } + + ctx = le32_to_cpu(pRecvRep->BucketContext[0]); + count = pRecvRep->NumberOfContexts; + skb = priv->RcvCtl[ctx].skb; + + offset = le32_to_cpu(pRecvRep->PacketOffset); +// if (offset != 0) { +// printk (KERN_INFO MYNAM ": %s/%s: Got a ReceivePostReply " +// "w/ PacketOffset %u\n", +// IOC_AND_NETDEV_NAMES_s_s(dev), +// offset); +// } + + dioprintk((KERN_INFO MYNAM ": %s/%s: @rpr, offset = %d, len = %d\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + offset, len)); + + if (count > 1) { + int szrem = len; + +// dioprintk((KERN_INFO MYNAM ": %s/%s: Multiple buckets returned " +// "for single packet, concatenating...\n", +// IOC_AND_NETDEV_NAMES_s_s(dev))); + + skb = (struct sk_buff *)dev_alloc_skb(len); + if (!skb) { + printk (KERN_ERR MYNAM ": %s/%s: ERROR - Can't allocate skb! (%s@%d)\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + __FILE__, __LINE__); + return -ENOMEM; + } + + spin_lock_irqsave(&priv->rxfidx_lock, flags); + for (i = 0; i < count; i++) { + + ctx = le32_to_cpu(pRecvRep->BucketContext[i]); + old_skb = priv->RcvCtl[ctx].skb; + + l = priv->RcvCtl[ctx].len; + if (szrem < l) + l = szrem; + +// dioprintk((KERN_INFO MYNAM ": %s/%s: Buckets = %d, len = %u\n", +// IOC_AND_NETDEV_NAMES_s_s(dev), +// i, l)); + + pci_dma_sync_single(mpt_dev->pcidev, + priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, + PCI_DMA_FROMDEVICE); + memcpy(skb_put(skb, l), old_skb->data, l); + + priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx; + szrem -= l; + } + spin_unlock_irqrestore(&priv->rxfidx_lock, flags); + + } else if (len < MPT_LAN_RX_COPYBREAK) { + + old_skb = skb; + + skb = (struct sk_buff *)dev_alloc_skb(len); + if (!skb) { + printk (KERN_ERR MYNAM ": %s/%s: ERROR - Can't allocate skb! (%s@%d)\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + __FILE__, __LINE__); + return -ENOMEM; + } + + pci_dma_sync_single(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); + + memcpy(skb_put(skb, len), old_skb->data, len); + + spin_lock_irqsave(&priv->rxfidx_lock, flags); + priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx; + spin_unlock_irqrestore(&priv->rxfidx_lock, flags); + + } else { + spin_lock_irqsave(&priv->rxfidx_lock, flags); + + priv->RcvCtl[ctx].skb = NULL; + + pci_unmap_single(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); + priv->RcvCtl[ctx].dma = 0; + + priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx; + spin_unlock_irqrestore(&priv->rxfidx_lock, flags); + + skb_put(skb,len); + } + + atomic_sub(count, &priv->buckets_out); + priv->total_received += count; + + if (priv->mpt_rxfidx_tail >= MPT_LAN_MAX_BUCKETS_OUT) { + printk (KERN_ERR MYNAM ": %s/%s: Yoohoo! mpt_rxfidx_tail = %d, " + "MPT_LAN_MAX_BUCKETS_OUT = %d\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + priv->mpt_rxfidx_tail, + MPT_LAN_MAX_BUCKETS_OUT); + + panic("Damn it Jim! I'm a doctor, not a programmer! " + "Oh, wait a sec, I am a programmer. " + "And, who's Jim?!?!\n" + "Arrgghh! We've done it again!\n"); + } + +#if 0 + { + u32 remaining = le32_to_cpu(pRecvRep->BucketsRemaining); + if (remaining < priv->bucketthresh) + mpt_lan_wake_post_buckets_task(dev); + + if (remaining == 0) + printk (KERN_WARNING MYNAM ": %s/%s: WARNING - IOC out of buckets! " + "(priv->buckets_out = %d)\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + atomic_read(&priv->buckets_out)); + else + printk (KERN_INFO MYNAM ": %s/%s: IOC says %d buckets left. " + "(priv->buckets_out = %d)\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + remaining, atomic_read(&priv->buckets_out)); + } +#endif + + return mpt_lan_receive_skb(dev, skb); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* Simple SGE's only at the moment */ + +static void +mpt_lan_post_receive_buckets(void *dev_id) +{ + struct net_device *dev = dev_id; + struct mpt_lan_priv *priv = dev->priv; + MPT_ADAPTER *mpt_dev = priv->mpt_dev; + MPT_FRAME_HDR *mf; + LANReceivePostRequest_t *pRecvReq; + SGETransaction32_t *pTrans; + SGESimple64_t *pSimple; + struct sk_buff *skb; + dma_addr_t dma; + u32 curr, buckets, count, max; + u32 len = (dev->mtu + dev->hard_header_len + 4); + unsigned long flags; + int i; + + curr = atomic_read(&priv->buckets_out); + buckets = (priv->max_buckets_out - curr); + + dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, Start_buckets = %u, buckets_out = %u\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + __FUNCTION__, buckets, curr)); + + max = (mpt_dev->req_sz - MPT_LAN_RECEIVE_POST_REQUEST_SIZE) / + (MPT_LAN_TRANSACTION32_SIZE + sizeof(SGESimple64_t)); + + while (buckets) { + mf = mpt_get_msg_frame(LanCtx, mpt_dev->id); + if (mf == NULL) { + printk (KERN_ERR "%s: Unable to alloc request frame\n", + __FUNCTION__); + dioprintk((KERN_ERR "%s: %u buckets remaining\n", + __FUNCTION__, buckets)); + goto out; + } + pRecvReq = (LANReceivePostRequest_t *) mf; + + count = buckets; + if (count > max) + count = max; + + pRecvReq->Function = MPI_FUNCTION_LAN_RECEIVE; + pRecvReq->ChainOffset = 0; + pRecvReq->MsgFlags = 0; + pRecvReq->PortNumber = priv->pnum; + + pTrans = (SGETransaction32_t *) pRecvReq->SG_List; + pSimple = NULL; + + for (i = 0; i < count; i++) { + int ctx; + + spin_lock_irqsave(&priv->rxfidx_lock, flags); + if (priv->mpt_rxfidx_tail < 0) { + printk (KERN_ERR "%s: Can't alloc context\n", + __FUNCTION__); + spin_unlock_irqrestore(&priv->rxfidx_lock, + flags); + break; + } + + ctx = priv->mpt_rxfidx[priv->mpt_rxfidx_tail--]; + + skb = priv->RcvCtl[ctx].skb; + if (skb && (priv->RcvCtl[ctx].len != len)) { + pci_unmap_single(mpt_dev->pcidev, + priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(priv->RcvCtl[ctx].skb); + skb = priv->RcvCtl[ctx].skb = NULL; + } + + if (skb == NULL) { + skb = dev_alloc_skb(len); + if (skb == NULL) { +/**/ printk (KERN_WARNING +/**/ MYNAM "/%s: Can't alloc skb\n", +/**/ __FUNCTION__); + priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx; + spin_unlock_irqrestore(&priv->rxfidx_lock, flags); + break; + } + + dma = pci_map_single(mpt_dev->pcidev, skb->data, + len, PCI_DMA_FROMDEVICE); + + priv->RcvCtl[ctx].skb = skb; + priv->RcvCtl[ctx].dma = dma; + priv->RcvCtl[ctx].len = len; + } + + spin_unlock_irqrestore(&priv->rxfidx_lock, flags); + + pTrans->ContextSize = sizeof(u32); + pTrans->DetailsLength = 0; + pTrans->Flags = 0; + pTrans->TransactionContext[0] = cpu_to_le32(ctx); + + pSimple = (SGESimple64_t *) pTrans->TransactionDetails; + + pSimple->FlagsLength = cpu_to_le32( + ((MPI_SGE_FLAGS_END_OF_BUFFER | + MPI_SGE_FLAGS_SIMPLE_ELEMENT | + MPI_SGE_FLAGS_64_BIT_ADDRESSING) << MPI_SGE_FLAGS_SHIFT) | len); + pSimple->Address.Low = cpu_to_le32((u32) priv->RcvCtl[ctx].dma); + if (sizeof(dma_addr_t) > sizeof(u32)) + pSimple->Address.High = cpu_to_le32((u32) ((u64) priv->RcvCtl[ctx].dma >> 32)); + else + pSimple->Address.High = 0; + + pTrans = (SGETransaction32_t *) (pSimple + 1); + } + + if (pSimple == NULL) { +/**/ printk (KERN_WARNING MYNAM "/%s: No buckets posted\n", +/**/ __FUNCTION__); + mpt_free_msg_frame(LanCtx, mpt_dev->id, mf); + goto out; + } + + pSimple->FlagsLength |= cpu_to_le32(MPI_SGE_FLAGS_END_OF_LIST << MPI_SGE_FLAGS_SHIFT); + + pRecvReq->BucketCount = cpu_to_le32(i); + +/* printk(KERN_INFO MYNAM ": posting buckets\n "); + * for (i = 0; i < j + 2; i ++) + * printk (" %08x", le32_to_cpu(msg[i])); + * printk ("\n"); + */ + + mpt_put_msg_frame(LanCtx, mpt_dev->id, mf); + + priv->total_posted += i; + buckets -= i; + atomic_add(i, &priv->buckets_out); + } + +out: + dioprintk((KERN_INFO MYNAM "/%s: End_buckets = %u, priv->buckets_out = %u\n", + __FUNCTION__, buckets, atomic_read(&priv->buckets_out))); + dioprintk((KERN_INFO MYNAM "/%s: Posted %u buckets and received %u back\n", + __FUNCTION__, priv->total_posted, priv->total_received)); + + clear_bit(0, &priv->post_buckets_active); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +struct net_device * +mpt_register_lan_device (MPT_ADAPTER *mpt_dev, int pnum) +{ + struct net_device *dev = NULL; + struct mpt_lan_priv *priv = NULL; + u8 HWaddr[FC_ALEN], *a; + + dev = init_fcdev(NULL, sizeof(struct mpt_lan_priv)); + if (!dev) + return (NULL); + dev->mtu = MPT_LAN_MTU; + + priv = (struct mpt_lan_priv *) dev->priv; + + priv->mpt_dev = mpt_dev; + priv->pnum = pnum; + + memset(&priv->post_buckets_task, 0, sizeof(struct tq_struct)); + priv->post_buckets_task.routine = mpt_lan_post_receive_buckets; + priv->post_buckets_task.data = dev; + priv->post_buckets_active = 0; + + dprintk((KERN_INFO MYNAM "@%d: bucketlen = %d\n", + __LINE__, dev->mtu + dev->hard_header_len + 4)); + + atomic_set(&priv->buckets_out, 0); + priv->total_posted = 0; + priv->total_received = 0; + priv->max_buckets_out = max_buckets_out; + if (mpt_dev->pfacts0.MaxLanBuckets < max_buckets_out) + priv->max_buckets_out = mpt_dev->pfacts0.MaxLanBuckets; + + dprintk((KERN_INFO MYNAM "@%d: MaxLanBuckets=%d, max_buckets_out/priv=%d/%d\n", + __LINE__, + mpt_dev->pfacts0.MaxLanBuckets, + max_buckets_out, + priv->max_buckets_out)); + + priv->bucketthresh = priv->max_buckets_out * 2 / 3; + priv->txfidx_lock = SPIN_LOCK_UNLOCKED; + priv->rxfidx_lock = SPIN_LOCK_UNLOCKED; + + memset(&priv->stats, 0, sizeof(priv->stats)); + + /* Grab pre-fetched LANPage1 stuff. :-) */ + a = (u8 *) &mpt_dev->lan_cnfg_page1.HardwareAddressLow; + + HWaddr[0] = a[5]; + HWaddr[1] = a[4]; + HWaddr[2] = a[3]; + HWaddr[3] = a[2]; + HWaddr[4] = a[1]; + HWaddr[5] = a[0]; + + dev->addr_len = FC_ALEN; + memcpy(dev->dev_addr, HWaddr, FC_ALEN); + memset(dev->broadcast, 0xff, FC_ALEN); + + /* The Tx queue is 127 deep on the 909. + * Give ourselves some breathing room. + */ + priv->tx_max_out = (tx_max_out_p <= MPT_TX_MAX_OUT_LIM) ? + tx_max_out_p : MPT_TX_MAX_OUT_LIM; + + dev->open = mpt_lan_open; + dev->stop = mpt_lan_close; + dev->get_stats = mpt_lan_get_stats; + dev->set_multicast_list = NULL; + dev->change_mtu = mpt_lan_change_mtu; + dev->hard_start_xmit = mpt_lan_sdu_send; + +/* Not in 2.3.42. Need 2.3.45+ */ + dev->tx_timeout = mpt_lan_tx_timeout; + dev->watchdog_timeo = MPT_LAN_TX_TIMEOUT; + + dprintk((KERN_INFO MYNAM ": Finished registering dev " + "and setting initial values\n")); + + SET_MODULE_OWNER(dev); + + return dev; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +int __init +mpt_lan_init (void) +{ + struct net_device *dev; + MPT_ADAPTER *curadapter; + int i = 0, j; + + show_mptmod_ver(LANAME, LANVER); + + if ((LanCtx = mpt_register(lan_reply, MPTLAN_DRIVER)) < 0) { + printk (KERN_ERR MYNAM ": Failed to register with MPT base driver\n"); + return -EBUSY; + } + + /* Set the callback index to be used by driver core for turbo replies */ + mpt_lan_index = LanCtx; + + dprintk((KERN_INFO MYNAM ": assigned context of %d\n", LanCtx)); + + for (j = 0; j < MPT_MAX_ADAPTERS; j++) { + mpt_landev[j] = NULL; + } + j = 0; + + curadapter = mpt_adapter_find_first(); + while (curadapter != NULL) { + for (i = 0; i < curadapter->facts0.NumberOfPorts; i++) { + printk (KERN_INFO MYNAM ": %s: PortNum=%x, ProtocolFlags=%02Xh (%c%c%c%c)\n", + curadapter->name, + curadapter->pfacts0.PortNumber, + curadapter->pfacts0.ProtocolFlags, + MPT_PROTOCOL_FLAGS_c_c_c_c(curadapter->pfacts0.ProtocolFlags)); + + if (curadapter->pfacts0.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) { + dev = mpt_register_lan_device (curadapter, i); + if (dev != NULL) { + printk (KERN_INFO MYNAM ": %s: Fusion MPT LAN device registered as '%s'\n", + curadapter->name, dev->name); + printk (KERN_INFO MYNAM ": %s/%s: LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5]); +// printk (KERN_INFO MYNAM ": %s/%s: Max_TX_outstanding = %d\n", +// IOC_AND_NETDEV_NAMES_s_s(dev), +// NETDEV_TO_LANPRIV_PTR(dev)->tx_max_out); + mpt_landev[j] = dev; + dprintk((KERN_INFO MYNAM "/init: dev_addr=%p, mpt_landev[%d]=%p\n", + dev, j, mpt_landev[j])); + + j++; + } else { + printk (KERN_ERR MYNAM ": %s: Unable to register port%d as a LAN device\n", + curadapter->name, + curadapter->pfacts0.PortNumber); + } + } else { + printk (KERN_INFO MYNAM ": %s: Hmmm... LAN protocol seems to be disabled on this adapter port!\n", + curadapter->name); + } + } + curadapter = mpt_adapter_find_next(curadapter); + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +void __init mpt_lan_exit(void) +{ + int i; + + for (i = 0; mpt_landev[i] != NULL; i++) { + struct net_device *dev = mpt_landev[i]; + + printk (KERN_INFO MYNAM ": %s/%s: Fusion MPT LAN device unregistered\n", + IOC_AND_NETDEV_NAMES_s_s(dev)); + unregister_fcdev(dev); + mpt_landev[i] = (struct net_device *) 0xdeadbeef; /* Debug */ + } + + if (LanCtx >= 0) { + mpt_deregister(LanCtx); + LanCtx = -1; + mpt_lan_index = 0; + } + + /* deregister any send/receive handler structs. I2Oism? */ +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +MODULE_PARM(tx_max_out_p, "i"); +MODULE_PARM(max_buckets_out, "i"); // Debug stuff. FIXME! + +module_init(mpt_lan_init); +module_exit(mpt_lan_exit); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static unsigned short +mpt_lan_type_trans(struct sk_buff *skb, struct net_device *dev) +{ + struct mpt_lan_ohdr *fch = (struct mpt_lan_ohdr *)skb->data; + struct fcllc *fcllc; + + skb->mac.raw = skb->data; + skb_pull(skb, sizeof(struct mpt_lan_ohdr)); + + if (fch->dtype == htons(0xffff)) { + u32 *p = (u32 *) fch; + + swab32s(p + 0); + swab32s(p + 1); + swab32s(p + 2); + swab32s(p + 3); + + printk (KERN_WARNING MYNAM ": %s: WARNING - Broadcast swap F/W bug detected!\n", + NETDEV_PTR_TO_IOC_NAME_s(dev)); + printk (KERN_WARNING MYNAM ": Please update sender @ MAC_addr = %02x:%02x:%02x:%02x:%02x:%02x\n", + fch->saddr[0], fch->saddr[1], fch->saddr[2], + fch->saddr[3], fch->saddr[4], fch->saddr[5]); + } + + if (*fch->daddr & 1) { + if (!memcmp(fch->daddr, dev->broadcast, FC_ALEN)) { + skb->pkt_type = PACKET_BROADCAST; + } else { + skb->pkt_type = PACKET_MULTICAST; + } + } else { + if (memcmp(fch->daddr, dev->dev_addr, FC_ALEN)) { + skb->pkt_type = PACKET_OTHERHOST; + } else { + skb->pkt_type = PACKET_HOST; + } + } + + /* Strip the SNAP header from ARP packets since we don't + * pass them through to the 802.2/SNAP layers. + */ + fcllc = (struct fcllc *)skb->data; + + if (fcllc->dsap == EXTENDED_SAP && + (fcllc->ethertype == htons(ETH_P_IP) || + fcllc->ethertype == htons(ETH_P_ARP))) { + skb_pull(skb, sizeof(struct fcllc)); + return fcllc->ethertype; + } + + return htons(ETH_P_802_2); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/mptlan.h linux.ac/drivers/message/fusion/mptlan.h --- linux.vanilla/drivers/message/fusion/mptlan.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/mptlan.h Sat May 26 17:58:37 2001 @@ -0,0 +1,72 @@ +/* mptlan.h */ + +#ifndef LINUX_MPTLAN_H_INCLUDED +#define LINUX_MPTLAN_H_INCLUDED +/*****************************************************************************/ + +#if !defined(__GENKSYMS__) +#include +#endif + +#include +#include +// #include +#include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +// #include + +#include +#include + + /* Override mptbase.h by pre-defining these! */ + #define MODULEAUTHOR "Noah Romer, Eddie C. Dost" + +#include "mptbase.h" + +/*****************************************************************************/ +#define LANAME "Fusion MPT LAN driver" +#define LANVER MPT_LINUX_VERSION_COMMON + +#ifdef MODULE +MODULE_AUTHOR(MODULEAUTHOR); +MODULE_DESCRIPTION(LANAME); +#endif +/*****************************************************************************/ + +#define MPT_LAN_MAX_BUCKETS_OUT 256 +#define MPT_LAN_BUCKET_THRESH 18 /* 9 buckets in one message */ +#define MPT_LAN_RX_COPYBREAK 200 +#define MPT_LAN_TX_TIMEOUT (1*HZ) +#define MPT_TX_MAX_OUT_LIM 127 + +#define MPT_LAN_MIN_MTU 96 /* RFC2625 */ +#define MPT_LAN_MAX_MTU 65280 /* RFC2625 */ +#define MPT_LAN_MTU 16128 /* be nice to slab allocator */ + +/* MPT LAN Reset and Suspend Resource Flags Defines */ + +#define MPT_LAN_RESOURCE_FLAG_RETURN_POSTED_BUCKETS 0x01 +#define MPT_LAN_RESOURCE_FLAG_RETURN_PEND_TRANSMITS 0x02 + +/*****************************************************************************/ +#ifdef MPT_LAN_IO_DEBUG +#define dioprintk(x) printk x +#else +#define dioprintk(x) +#endif + +#define NETDEV_TO_LANPRIV_PTR(d) ((struct mpt_lan_priv *)(d)->priv) +#define NETDEV_PTR_TO_IOC_NAME_s(d) (NETDEV_TO_LANPRIV_PTR(d)->mpt_dev->name) +#define IOC_AND_NETDEV_NAMES_s_s(d) NETDEV_PTR_TO_IOC_NAME_s(d), (d)->name + +/*****************************************************************************/ +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/mptscsih.c linux.ac/drivers/message/fusion/mptscsih.c --- linux.vanilla/drivers/message/fusion/mptscsih.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/mptscsih.c Tue Apr 3 23:12:52 2001 @@ -0,0 +1,2508 @@ +/* + * linux/drivers/message/fusion/mptscsih.c + * High performance SCSI / Fibre Channel SCSI Host device driver. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Credits: + * This driver would not exist if not for Alan Cox's development + * of the linux i2o driver. + * + * A huge debt of gratitude is owed to David S. Miller (DaveM) + * for fixing much of the stupid and broken stuff in the early + * driver while porting to sparc64 platform. THANK YOU! + * + * (see mptbase.c) + * + * Copyright (c) 1999-2001 LSI Logic Corporation + * Original author: Steven J. Ralston + * (mailto:Steve.Ralston@lsil.com) + * + * $Id: mptscsih.c,v 1.24 2001/03/22 08:45:08 sralston Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + 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; version 2 of the License. + + 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. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include +#include +#include +#include +#include +#include +#include +#include /* for io_request_lock (spinlock) decl */ +#include "../../scsi/scsi.h" +#include "../../scsi/hosts.h" +#include "../../scsi/sd.h" + +#include "mptbase.h" +#include "mptscsih.h" +#include "isense.h" + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#define my_NAME "Fusion MPT SCSI Host driver" +#define my_VERSION MPT_LINUX_VERSION_COMMON +#define MYNAM "mptscsih" + +MODULE_AUTHOR(MODULEAUTHOR); +MODULE_DESCRIPTION(my_NAME); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +typedef struct _BIG_SENSE_BUF { + u8 data[256]; +} BIG_SENSE_BUF; + +typedef struct _MPT_SCSI_HOST { + MPT_ADAPTER *ioc; + int port; + struct scsi_cmnd **ScsiLookup; + u8 *SgHunks; + dma_addr_t SgHunksDMA; + u32 qtag_tick; + FCDEV_TRACKER TargetsQ; +} MPT_SCSI_HOST; + +typedef struct _MPT_SCSI_DEV { + struct _MPT_SCSI_DEV *forw; + struct _MPT_SCSI_DEV *back; + MPT_ADAPTER *ioc; + int sense_sz; + BIG_SENSE_BUF CachedSense; + unsigned long io_cnt; + unsigned long read_cnt; +} MPT_SCSI_DEV; + +/* + * Other private/forward protos... + */ + +static int mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); +static void mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq); +static int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); +static int mptscsih_io_direction(Scsi_Cmnd *cmd); +static void copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply); +static u32 SCPNT_TO_MSGCTX(Scsi_Cmnd *sc); + +static int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply); + + +static int mpt_scsi_hosts = 0; +static atomic_t queue_depth; + +static int ScsiDoneCtx = -1; +static int ScsiTaskCtx = -1; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,28) +static struct proc_dir_entry proc_mpt_scsihost = +{ + low_ino: PROC_SCSI_MPT, + namelen: 8, + name: "mptscsih", + mode: S_IFDIR | S_IRUGO | S_IXUGO, + nlink: 2, +}; +#endif + +#define SNS_LEN(scp) sizeof((scp)->sense_buffer) + +#ifndef MPT_SCSI_USE_NEW_EH +/* + * Stuff to handle single-threading SCSI TaskMgmt + * (abort/reset) requests... + */ +static spinlock_t mpt_scsih_taskQ_lock = SPIN_LOCK_UNLOCKED; +static MPT_Q_TRACKER mpt_scsih_taskQ = { + (MPT_FRAME_HDR*) &mpt_scsih_taskQ, + (MPT_FRAME_HDR*) &mpt_scsih_taskQ +}; +static int mpt_scsih_taskQ_cnt = 0; +static int mpt_scsih_taskQ_bh_active = 0; +static MPT_FRAME_HDR *mpt_scsih_active_taskmgmt_mf = NULL; +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_io_done - Main SCSI IO callback routine registered to + * Fusion MPT (base) driver + * @ioc: Pointer to MPT_ADAPTER structure + * @mf: Pointer to original MPT request frame + * @r: Pointer to MPT reply frame (NULL if TurboReply) + * + * This routine is called from mpt.c::mpt_interrupt() at the completion + * of any SCSI IO request. + * This routine is registered with the Fusion MPT (base) driver at driver + * load/init time via the mpt_register() API call. + * + * Returns 1 indicating alloc'd request frame ptr should be freed. + */ +static int +mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r) +{ + Scsi_Cmnd *sc; + MPT_SCSI_HOST *hd; + MPT_SCSI_DEV *mpt_sdev = NULL; + u16 req_idx; + + if ((mf == NULL) || + (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) { + printk(KERN_ERR MYNAM ": ERROR! NULL or BAD req frame ptr (=%p)!\n", mf); + return 1; + } + + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + sc = hd->ScsiLookup[req_idx]; + hd->ScsiLookup[req_idx] = NULL; + + dmfprintk((KERN_INFO MYNAM ": ScsiDone (req:sc:reply=%p:%p:%p)\n", mf, sc, r)); + + atomic_dec(&queue_depth); + + /* + * Check for {1st} {IO} completion to "new" device. + * How do we know it's a new device? + * If we haven't set SDpnt->hostdata I guess... + */ + if (sc && sc->device) { + mpt_sdev = (MPT_SCSI_DEV*)sc->device->hostdata; + if (!mpt_sdev) { + dprintk((KERN_INFO MYNAM ": *NEW* SCSI device (%d:%d:%d)!\n", + sc->device->id, sc->device->lun, sc->device->channel)); + if ((sc->device->hostdata = kmalloc(sizeof(MPT_SCSI_DEV), GFP_ATOMIC)) == NULL) { + printk(KERN_ERR MYNAM ": ERROR: kmalloc(%d) FAILED!\n", (int)sizeof(MPT_SCSI_DEV)); + } else { + memset(sc->device->hostdata, 0, sizeof(MPT_SCSI_DEV)); + mpt_sdev = (MPT_SCSI_DEV *) sc->device->hostdata; + mpt_sdev->ioc = ioc; + } + } else { + if (++mpt_sdev->io_cnt && mptscsih_io_direction(sc) < 0) { + if (++mpt_sdev->read_cnt == 3) { + dprintk((KERN_INFO MYNAM ": 3rd DATA_IN, CDB[0]=%02x\n", + sc->cmnd[0])); + } + } +#if 0 + if (mpt_sdev->sense_sz) { + /* + * Completion of first IO down this path + * *should* invalidate device SenseData... + */ + mpt_sdev->sense_sz = 0; + } +#endif + } + } + +#if 0 +{ + MPT_FRAME_HDR *mf_chk; + + /* This, I imagine, is a costly check, but... + * If abort/reset active, check to see if this is a IO + * that completed while ABORT/RESET for it is waiting + * on our taskQ! + */ + if (! Q_IS_EMPTY(&mpt_scsih_taskQ)) { + /* If ABORT for this IO is queued, zap it! */ + mf_chk = search_taskQ(1,sc,MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK); + if (mf_chk != NULL) { + sc->result = DID_ABORT << 16; + spin_lock_irqsave(&io_request_lock, flags); + sc->scsi_done(sc); + spin_unlock_irqrestore(&io_request_lock, flags); + return 1; + } + } +} +#endif + + if (r != NULL && sc != NULL) { + SCSIIOReply_t *pScsiReply; + SCSIIORequest_t *pScsiReq; + u16 status; + + pScsiReply = (SCSIIOReply_t *) r; + pScsiReq = (SCSIIORequest_t *) mf; + + status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK; + + dprintk((KERN_NOTICE MYNAM ": Uh-Oh! (req:sc:reply=%p:%p:%p)\n", mf, sc, r)); + dprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh" + ", SCSIStatus=%02xh, IOCLogInfo=%08xh\n", + status, pScsiReply->SCSIState, pScsiReply->SCSIStatus, + le32_to_cpu(pScsiReply->IOCLogInfo))); + + /* + * Look for + dump FCP ResponseInfo[]! + */ + if (pScsiReply->SCSIState & MPI_SCSI_STATE_RESPONSE_INFO_VALID) { + dprintk((KERN_NOTICE " FCP_ResponseInfo=%08xh\n", + le32_to_cpu(pScsiReply->ResponseInfo))); + } + + switch(status) { + case MPI_IOCSTATUS_BUSY: /* 0x0002 */ + /*sc->result = DID_BUS_BUSY << 16;*/ /* YIKES! - Seems to + * kill linux interrupt + * handler + */ + sc->result = STS_BUSY; /* Try SCSI BUSY! */ + break; + + case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */ + /* Not real sure here... */ + sc->result = DID_OK << 16; + break; + + case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */ + case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */ + sc->result = DID_BAD_TARGET << 16; + break; + + case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */ + /* Spoof to SCSI Selection Timeout! */ + sc->result = DID_NO_CONNECT << 16; + break; + + case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */ + /* + * YIKES! I just discovered that SCSI IO which + * returns check condition, SenseKey=05 (ILLEGAL REQUEST) + * and ASC/ASCQ=94/01 (LSI Logic RAID vendor specific), + * comes down this path! + * Do upfront check for valid SenseData and give it + * precedence! + */ + if (pScsiReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) { + copy_sense_data(sc, hd, mf, pScsiReply); + sc->result = pScsiReply->SCSIStatus; + break; + } + + dprintk((KERN_NOTICE MYNAM ": sc->underflow={report ERR if < %02xh bytes xfer'd}\n", sc->underflow)); + dprintk((KERN_NOTICE MYNAM ": ActBytesXferd=%02xh\n", le32_to_cpu(pScsiReply->TransferCount))); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + sc->resid = sc->request_bufflen - le32_to_cpu(pScsiReply->TransferCount); + dprintk((KERN_NOTICE MYNAM ": SET sc->resid=%02xh\n", sc->resid)); +#endif + +#if 0 + if (sc->underflow && (le32_to_cpu(pScsiReply->TransferCount) < sc->underflow)) { + sc->result = DID_ERROR << 16; + sc->resid = sc->request_bufflen - le32_to_cpu(pScsiReply->TransferCount); + } else { + sc->result = 0; + } +#endif + + /* workaround attempts... */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + if (sc->resid >= 0x200) { + /* GRRRRR... + * //sc->result = DID_SOFT_ERROR << 16; + * Try spoofing to BUSY + */ + sc->result = STS_BUSY; + } else { + sc->result = 0; + } +#else + sc->result = 0; +#endif + break; + + case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */ + sc->result = DID_ABORT << 16; + break; + + case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */ + case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */ + sc->result = DID_RESET << 16; + break; + + case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */ + sc->result = pScsiReply->SCSIStatus; + + if (pScsiReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) { + copy_sense_data(sc, hd, mf, pScsiReply); + + /* If running agains circa 200003dd 909 MPT f/w, + * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL + * (QUEUE_FULL) returned from device! --> get 0x0000?128 + * and with SenseBytes set to 0. + */ + if (pScsiReply->SCSIStatus == MPI_SCSI_STATUS_TASK_SET_FULL) + mptscsih_report_queue_full(sc, pScsiReply, pScsiReq); + } + else if (pScsiReply->SCSIState & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) { + /* + * What to do? + */ + sc->result = DID_SOFT_ERROR << 16; + } + else if (pScsiReply->SCSIState & MPI_SCSI_STATE_TERMINATED) { + /* Not real sure here either... */ + sc->result = DID_ABORT << 16; + } + + if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL) + mptscsih_report_queue_full(sc, pScsiReply, pScsiReq); + + break; + + case MPI_IOCSTATUS_INVALID_FUNCTION: /* 0x0001 */ + case MPI_IOCSTATUS_INVALID_SGL: /* 0x0003 */ + case MPI_IOCSTATUS_INTERNAL_ERROR: /* 0x0004 */ + case MPI_IOCSTATUS_RESERVED: /* 0x0005 */ + case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */ + case MPI_IOCSTATUS_INVALID_FIELD: /* 0x0007 */ + case MPI_IOCSTATUS_INVALID_STATE: /* 0x0008 */ + case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: /* 0x0044 */ + case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */ + case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */ + case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */ + case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: /* 0x004A */ + default: + /* + * What to do? + */ + sc->result = DID_SOFT_ERROR << 16; + break; + + } /* switch(status) */ + + dprintk((KERN_NOTICE MYNAM ": sc->result set to %08xh\n", sc->result)); + } + + if (sc != NULL) { + unsigned long flags; + + /* Unmap the DMA buffers, if any. */ + if (sc->use_sg) { + pci_unmap_sg(ioc->pcidev, + (struct scatterlist *) sc->request_buffer, + sc->use_sg, + scsi_to_pci_dma_dir(sc->sc_data_direction)); + } else if (sc->request_bufflen) { + pci_unmap_single(ioc->pcidev, + (dma_addr_t)((long)sc->SCp.ptr), + sc->request_bufflen, + scsi_to_pci_dma_dir(sc->sc_data_direction)); + } + + spin_lock_irqsave(&io_request_lock, flags); + sc->scsi_done(sc); + spin_unlock_irqrestore(&io_request_lock, flags); + } + + return 1; +} + +#ifndef MPT_SCSI_USE_NEW_EH +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * search_taskQ - Search SCSI task mgmt request queue for specific + * request type + * @remove: (Boolean) Should request be removed if found? + * @sc: Pointer to Scsi_Cmnd structure + * @task_type: Task type to search for + * + * Returns pointer to MPT request frame if found, or %NULL if request + * was not found. + */ +static MPT_FRAME_HDR * +search_taskQ(int remove, Scsi_Cmnd *sc, u8 task_type) +{ + MPT_FRAME_HDR *mf = NULL; + unsigned long flags; + int count = 0; + int list_sz; + + dslprintk((KERN_INFO MYNAM ": spinlock#1\n")); + spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags); + list_sz = mpt_scsih_taskQ_cnt; + if (! Q_IS_EMPTY(&mpt_scsih_taskQ)) { + mf = mpt_scsih_taskQ.head; + do { + count++; + if (mf->u.frame.linkage.argp1 == sc && + mf->u.frame.linkage.arg1 == task_type) { + if (remove) { + Q_DEL_ITEM(&mf->u.frame.linkage); + mpt_scsih_taskQ_cnt--; + } + break; + } + } while ((mf = mf->u.frame.linkage.forw) != (MPT_FRAME_HDR*)&mpt_scsih_taskQ); + if (mf == (MPT_FRAME_HDR*)&mpt_scsih_taskQ) { + mf = NULL; + } + } + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); + + if (list_sz) { + dprintk((KERN_INFO MYNAM ": search_taskQ(%d,%p,%d) results=%p (%sFOUND%s)!\n", + remove, sc, task_type, + mf, + mf ? "" : "NOT_", + (mf && remove) ? "+REMOVED" : "" )); + dprintk((KERN_INFO MYNAM ": (searched thru %d of %d items on taskQ)\n", + count, + list_sz )); + } + + return mf; +} + +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/* + * Hack! I'd like to report if a device is returning QUEUE_FULL + * but maybe not each and every time... + */ +static long last_queue_full = 0; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_report_queue_full - Report QUEUE_FULL status returned + * from a SCSI target device. + * @sc: Pointer to Scsi_Cmnd structure + * @pScsiReply: Pointer to SCSIIOReply_t + * @pScsiReq: Pointer to original SCSI request + * + * This routine periodically reports QUEUE_FULL status returned from a + * SCSI target device. It reports this to the console via kernel + * printk() API call, not more than once every 10 seconds. + */ +static void +mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq) +{ + long time = jiffies; + + if (time - last_queue_full > 10 * HZ) { + printk(KERN_WARNING MYNAM ": Device reported QUEUE_FULL! SCSI bus:target:lun = %d:%d:%d\n", + 0, sc->target, sc->lun); + last_queue_full = time; + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int BeenHereDoneThat = 0; + +/* SCSI fops start here... */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_detect - Register MPT adapter(s) as SCSI host(s) with + * linux scsi mid-layer. + * @tpnt: Pointer to Scsi_Host_Template structure + * + * (linux Scsi_Host_Template.detect routine) + * + * Returns number of SCSI host adapters that were successfully + * registered with the linux scsi mid-layer via the scsi_register() + * API call. + */ +int +mptscsih_detect(Scsi_Host_Template *tpnt) +{ + struct Scsi_Host *sh = NULL; + MPT_SCSI_HOST *hd = NULL; + MPT_ADAPTER *this; + unsigned long flags; + int sz; + u8 *mem; + + if (! BeenHereDoneThat++) { + show_mptmod_ver(my_NAME, my_VERSION); + + ScsiDoneCtx = mpt_register(mptscsih_io_done, MPTSCSIH_DRIVER); + ScsiTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSCSIH_DRIVER); + +#ifndef MPT_SCSI_USE_NEW_EH + Q_INIT(&mpt_scsih_taskQ, MPT_FRAME_HDR); + spin_lock_init(&mpt_scsih_taskQ_lock); +#endif + + if (mpt_event_register(ScsiDoneCtx, mptscsih_event_process) == 0) { + dprintk((KERN_INFO MYNAM ": Registered for IOC event notifications\n")); + } else { + /* FIXME! */ + } + } + + dprintk((KERN_INFO MYNAM ": mpt_scsih_detect()\n")); + + this = mpt_adapter_find_first(); + while (this != NULL) { + /* FIXME! Multi-port (aka FC929) support... + * for (i = 0; i < this->facts.NumberOfPorts; i++) + */ + + /* 20010215 -sralston + * Added sanity check on SCSI Initiator-mode enabled + * for this MPT adapter. + */ + if (!(this->pfacts0.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR)) { + printk(KERN_ERR MYNAM ": Skipping %s because SCSI Initiator mode is NOT enabled!\n", + this->name); + this = mpt_adapter_find_next(this); + continue; + } + + /* 20010202 -sralston + * Added sanity check on readiness of the MPT adapter. + */ + if (this->last_state != MPI_IOC_STATE_OPERATIONAL) { + printk(KERN_ERR MYNAM ": ERROR - Skipping %s because it's not operational!\n", + this->name); + this = mpt_adapter_find_next(this); + continue; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + tpnt->proc_dir = &proc_mpt_scsihost; +#endif + sh = scsi_register(tpnt, sizeof(MPT_SCSI_HOST)); + if (sh != NULL) { + save_flags(flags); + cli(); + sh->io_port = 0; + sh->n_io_port = 0; + sh->irq = 0; + + /* Yikes! This is important! + * Otherwise, by default, linux only scans target IDs 0-7! + */ + sh->max_id = this->pfacts0.MaxDevices - 1; + + sh->this_id = this->pfacts0.PortSCSIID; + + restore_flags(flags); + + hd = (MPT_SCSI_HOST *) sh->hostdata; + hd->ioc = this; + hd->port = 0; /* FIXME! */ + + /* SCSI needs Scsi_Cmnd lookup table! + * (with size equal to req_depth*PtrSz!) + */ + sz = hd->ioc->req_depth * sizeof(void *); + mem = kmalloc(sz, GFP_KERNEL); + if (mem == NULL) + return mpt_scsi_hosts; + + memset(mem, 0, sz); + hd->ScsiLookup = (struct scsi_cmnd **) mem; + + dprintk((KERN_INFO MYNAM ": ScsiLookup @ %p, sz=%d\n", + hd->ScsiLookup, sz)); + + /* SCSI also needs SG buckets/hunk management! + * (with size equal to N * req_sz * req_depth!) + * (where N is number of SG buckets per hunk) + */ + sz = MPT_SG_BUCKETS_PER_HUNK * hd->ioc->req_sz * hd->ioc->req_depth; + mem = pci_alloc_consistent(hd->ioc->pcidev, sz, + &hd->SgHunksDMA); + if (mem == NULL) + return mpt_scsi_hosts; + + memset(mem, 0, sz); + hd->SgHunks = (u8*)mem; + + dprintk((KERN_INFO MYNAM ": SgHunks @ %p(%08x), sz=%d\n", + hd->SgHunks, hd->SgHunksDMA, sz)); + + hd->qtag_tick = jiffies; + + this->sh = sh; + mpt_scsi_hosts++; + } + this = mpt_adapter_find_next(this); + } + + return mpt_scsi_hosts; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + static char *info_kbuf = NULL; +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_release - Unregister SCSI host from linux scsi mid-layer + * @host: Pointer to Scsi_Host structure + * + * (linux Scsi_Host_Template.release routine) + * This routine releases all resources associated with the SCSI host + * adapter. + * + * Returns 0 for success. + */ +int +mptscsih_release(struct Scsi_Host *host) +{ + MPT_SCSI_HOST *hd; +#ifndef MPT_SCSI_USE_NEW_EH + unsigned long flags; + + spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags); + if (mpt_scsih_taskQ_bh_active) { + int count = 10 * HZ; + + dprintk((KERN_INFO MYNAM ": Info: Zapping TaskMgmt thread!\n")); + + /* Zap the taskQ! */ + Q_INIT(&mpt_scsih_taskQ, MPT_FRAME_HDR); + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); + + while(mpt_scsih_taskQ_bh_active && --count) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + if (!count) + printk(KERN_ERR MYNAM ": ERROR! TaskMgmt thread still active!\n"); + } + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); +#endif + + hd = (MPT_SCSI_HOST *) host->hostdata; + if (hd != NULL) { + int sz1, sz2; + + sz1 = sz2 = 0; + if (hd->ScsiLookup != NULL) { + sz1 = hd->ioc->req_depth * sizeof(void *); + kfree(hd->ScsiLookup); + hd->ScsiLookup = NULL; + } + + if (hd->SgHunks != NULL) { + + sz2 = MPT_SG_BUCKETS_PER_HUNK * hd->ioc->req_sz * hd->ioc->req_depth; + pci_free_consistent(hd->ioc->pcidev, sz2, + hd->SgHunks, hd->SgHunksDMA); + hd->SgHunks = NULL; + } + dprintk((KERN_INFO MYNAM ": Free'd ScsiLookup (%d) and SgHunks (%d) memory\n", sz1, sz2)); + } + + if (mpt_scsi_hosts) { + if (--mpt_scsi_hosts == 0) { +#if 0 + mptscsih_flush_pending(); +#endif + mpt_event_deregister(ScsiDoneCtx); + dprintk((KERN_INFO MYNAM ": Deregistered for IOC event notifications\n")); + + mpt_deregister(ScsiDoneCtx); + mpt_deregister(ScsiTaskCtx); + + if (info_kbuf != NULL) + kfree(info_kbuf); + } + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_info - Return information about MPT adapter + * @SChost: Pointer to Scsi_Host structure + * + * (linux Scsi_Host_Template.info routine) + * + * Returns pointer to buffer where information was written. + */ +const char * +mptscsih_info(struct Scsi_Host *SChost) +{ + MPT_SCSI_HOST *h; + int size = 0; + + if (info_kbuf == NULL) + if ((info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL) + return info_kbuf; + + h = (MPT_SCSI_HOST *)SChost->hostdata; + info_kbuf[0] = '\0'; + mpt_print_ioc_summary(h->ioc, info_kbuf, &size, 0); + info_kbuf[size-1] = '\0'; + + return info_kbuf; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + static int max_qd = 1; +#ifdef MPT_DEBUG + static int max_sges = 0; + static int max_xfer = 0; +#endif +#if 0 + static int max_num_sges = 0; + static int max_sgent_len = 0; +#endif +#if 0 +static int index_log[128]; +static int index_ent = 0; +static __inline__ void ADD_INDEX_LOG(int req_ent) +{ + int i = index_ent++; + + index_log[i & (128 - 1)] = req_ent; +} +#else +#define ADD_INDEX_LOG(req_ent) do { } while(0) +#endif +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine. + * @SCpnt: Pointer to Scsi_Cmnd structure + * @done: Pointer SCSI mid-layer IO completion function + * + * (linux Scsi_Host_Template.queuecommand routine) + * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest + * from a linux Scsi_Cmnd request and send it to the IOC. + * + * Returns 0. (rtn value discarded by linux scsi mid-layer) + */ +int +mptscsih_qcmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +{ + struct Scsi_Host *host; + MPT_SCSI_HOST *hd; + MPT_FRAME_HDR *mf; + SCSIIORequest_t *pScsiReq; + int datadir; + u32 len; + u32 sgdir; + u32 scsictl; + u32 scsidir; + u32 qtag; + u32 *mptr; + int sge_spill1; + int frm_sz; + int sges_left; + u32 chain_offset; + int my_idx; + int i; + + dmfprintk((KERN_INFO MYNAM "_qcmd: SCpnt=%p, done()=%p\n", + SCpnt, done)); + + host = SCpnt->host; + hd = (MPT_SCSI_HOST *) host->hostdata; + +#if 0 + if (host->host_busy >= 60) { + MPT_ADAPTER *ioc = hd->ioc; + u16 pci_command, pci_status; + + /* The IOC is probably hung, investigate status. */ + printk("MPI: IOC probably hung IOCSTAT[%08x] INTSTAT[%08x] REPLYFIFO[%08x]\n", + readl(&ioc->chip.fc9xx->DoorbellValue), + readl(&ioc->chip.fc9xx->IntStatus), + readl(&ioc->chip.fc9xx->ReplyFifo)); + pci_read_config_word(ioc->pcidev, PCI_COMMAND, &pci_command); + pci_read_config_word(ioc->pcidev, PCI_STATUS, &pci_status); + printk("MPI: PCI command[%04x] status[%04x]\n", pci_command, pci_status); + { + /* DUMP req index logger. */ + int begin, end; + + begin = (index_ent - 65) & (128 - 1); + end = index_ent & (128 - 1); + printk("MPI: REQ_INDEX_HIST["); + while (begin != end) { + printk("(%04x)", index_log[begin]); + begin = (begin + 1) & (128 - 1); + } + printk("\n"); + } + sti(); + while(1) + barrier(); + } +#endif + + SCpnt->scsi_done = done; + + /* 20000617 -sralston + * GRRRRR... Shouldn't have to do this but... + * Do explicit check for REQUEST_SENSE and cached SenseData. + * If yes, return cached SenseData. + */ +#ifdef MPT_SCSI_CACHE_AUTOSENSE + { + MPT_SCSI_DEV *mpt_sdev; + + mpt_sdev = (MPT_SCSI_DEV *) SCpnt->device->hostdata; + if (mpt_sdev && SCpnt->cmnd[0] == REQUEST_SENSE) { + u8 *dest = NULL; + + if (!SCpnt->use_sg) + dest = SCpnt->request_buffer; + else { + struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; + if (sg) + dest = (u8 *) (unsigned long)sg_dma_address(sg); + } + + if (dest && mpt_sdev->sense_sz) { + memcpy(dest, mpt_sdev->CachedSense.data, mpt_sdev->sense_sz); +#ifdef MPT_DEBUG + { + int i; + u8 *sb; + + sb = mpt_sdev->CachedSense.data; + if (sb && ((sb[0] & 0x70) == 0x70)) { + printk(KERN_WARNING MYNAM ": Returning last cached SCSI (hex) SenseData:\n"); + printk(KERN_WARNING " "); + for (i = 0; i < (8 + sb[7]); i++) + printk("%s%02x", i == 13 ? "-" : " ", sb[i]); + printk("\n"); + } + } +#endif + } + SCpnt->resid = SCpnt->request_bufflen - mpt_sdev->sense_sz; + SCpnt->result = 0; +/* spin_lock(&io_request_lock); */ + SCpnt->scsi_done(SCpnt); +/* spin_unlock(&io_request_lock); */ + return 0; + } + } +#endif + + if ((mf = mpt_get_msg_frame(ScsiDoneCtx, hd->ioc->id)) == NULL) { +/* SCpnt->result = DID_SOFT_ERROR << 16; */ + SCpnt->result = STS_BUSY; + SCpnt->scsi_done(SCpnt); +/* return 1; */ + return 0; + } + pScsiReq = (SCSIIORequest_t *) mf; + + my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + + ADD_INDEX_LOG(my_idx); + + /* Map the data portion, if any. */ + sges_left = SCpnt->use_sg; + if (sges_left) { + sges_left = pci_map_sg(hd->ioc->pcidev, + (struct scatterlist *) SCpnt->request_buffer, + sges_left, + scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + } else if (SCpnt->request_bufflen) { + dma_addr_t buf_dma_addr; + + buf_dma_addr = pci_map_single(hd->ioc->pcidev, + SCpnt->request_buffer, + SCpnt->request_bufflen, + scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + + /* We hide it here for later unmap. */ + SCpnt->SCp.ptr = (char *)(unsigned long) buf_dma_addr; + } + + /* + * Put together a MPT SCSI request... + */ + + /* Assume SimpleQ, NO DATA XFER for now */ + + len = SCpnt->request_bufflen; + sgdir = 0x00000000; /* SGL IN (host<--ioc) */ + scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER; + + /* + * The scsi layer should be handling this stuff + * (In 2.3.x it does -DaveM) + */ + + /* BUG FIX! 19991030 -sralston + * TUR's being issued with scsictl=0x02000000 (DATA_IN)! + * Seems we may receive a buffer (len>0) even when there + * will be no data transfer! GRRRRR... + */ + datadir = mptscsih_io_direction(SCpnt); + if (datadir < 0) { + scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */ + } else if (datadir > 0) { + sgdir = 0x04000000; /* SGL OUT (host-->ioc) */ + scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */ + } else { + len = 0; + } + + qtag = MPI_SCSIIO_CONTROL_SIMPLEQ; + + /* + * Attach tags to the devices + */ + if (SCpnt->device->tagged_supported) { + /* + * Some drives are too stupid to handle fairness issues + * with tagged queueing. We throw in the odd ordered + * tag to stop them starving themselves. + */ + if ((jiffies - hd->qtag_tick) > (5*HZ)) { + qtag = MPI_SCSIIO_CONTROL_ORDEREDQ; + hd->qtag_tick = jiffies; + +#if 0 + /* These are ALWAYS zero! + * (Because this is a place for the device driver to dynamically + * assign tag numbers any way it sees fit. That's why -DaveM) + */ + dprintk((KERN_DEBUG MYNAM ": sc->device->current_tag = %08x\n", + SCpnt->device->current_tag)); + dprintk((KERN_DEBUG MYNAM ": sc->tag = %08x\n", + SCpnt->tag)); +#endif + } +#if 0 + else { + /* Hmmm... I always see value of 0 here, + * of which {HEAD_OF, ORDERED, SIMPLE} are NOT! -sralston + * (Because this is a place for the device driver to dynamically + * assign tag numbers any way it sees fit. That's why -DaveM) + * + * if (SCpnt->tag == HEAD_OF_QUEUE_TAG) + */ + if (SCpnt->device->current_tag == HEAD_OF_QUEUE_TAG) + qtag = MPI_SCSIIO_CONTROL_HEADOFQ; + else if (SCpnt->tag == ORDERED_QUEUE_TAG) + qtag = MPI_SCSIIO_CONTROL_ORDEREDQ; + } +#endif + } + + scsictl = scsidir | qtag; + + frm_sz = hd->ioc->req_sz; + + /* Ack! + * sge_spill1 = 9; + */ + sge_spill1 = (frm_sz - (sizeof(SCSIIORequest_t) - sizeof(SGEIOUnion_t) + sizeof(SGEChain32_t))) / 8; + /* spill1: for req_sz == 128 (128-48==80, 80/8==10 SGEs max, first time!), --> use 9 + * spill1: for req_sz == 96 ( 96-48==48, 48/8== 6 SGEs max, first time!), --> use 5 + */ + dsgprintk((KERN_INFO MYNAM ": SG: %x spill1 = %d\n", + my_idx, sge_spill1)); + +#ifdef MPT_DEBUG + if (sges_left > max_sges) { + max_sges = sges_left; + dprintk((KERN_INFO MYNAM ": MPT_MaxSges = %d\n", max_sges)); + } +#endif +#if 0 + if (sges_left > max_num_sges) { + max_num_sges = sges_left; + printk(KERN_INFO MYNAM ": MPT_MaxNumSges = %d\n", max_num_sges); + } +#endif + + dsgprintk((KERN_INFO MYNAM ": SG: %x sges_left = %d (initially)\n", + my_idx, sges_left)); + + chain_offset = 0; + if (sges_left > (sge_spill1+1)) { +#if 0 + chain_offset = 0x1E; +#endif + chain_offset = (frm_sz - 8) / 4; + } + + pScsiReq->TargetID = SCpnt->target; + pScsiReq->Bus = hd->port; + pScsiReq->ChainOffset = chain_offset; + pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST; + pScsiReq->CDBLength = SCpnt->cmd_len; + +/* We have 256 bytes alloc'd per IO; let's use it. */ +/* pScsiReq->SenseBufferLength = SNS_LEN(SCpnt); */ + pScsiReq->SenseBufferLength = 255; + + pScsiReq->Reserved = 0; + pScsiReq->MsgFlags = 0; + pScsiReq->LUN[0] = 0; + pScsiReq->LUN[1] = SCpnt->lun; + pScsiReq->LUN[2] = 0; + pScsiReq->LUN[3] = 0; + pScsiReq->LUN[4] = 0; + pScsiReq->LUN[5] = 0; + pScsiReq->LUN[6] = 0; + pScsiReq->LUN[7] = 0; + pScsiReq->Control = cpu_to_le32(scsictl); + + /* + * Write SCSI CDB into the message + */ + for (i = 0; i < 12; i++) + pScsiReq->CDB[i] = SCpnt->cmnd[i]; + for (i = 12; i < 16; i++) + pScsiReq->CDB[i] = 0; + + /* DataLength */ + pScsiReq->DataLength = cpu_to_le32(len); + + /* SenseBuffer low address */ + pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_pool_dma + (my_idx * 256)); + + mptr = (u32 *) &pScsiReq->SGL; + + /* + * Now fill in the SGList... + * NOTES: For 128 byte req_sz, we can hold up to 10 simple SGE's + * in the remaining request frame. We -could- do unlimited chains + * but each chain buffer can only be req_sz bytes in size, and + * we lose one SGE whenever we chain. + * For 128 req_sz, we can hold up to 16 SGE's per chain buffer. + * For practical reasons, limit ourselves to 1 overflow chain buffer; + * giving us 9 + 16 == 25 SGE's max. + * At 4 Kb per SGE, that yields 100 Kb max transfer. + * + * (This code needs to be completely changed when/if 64-bit DMA + * addressing is used, since we will be able to fit much less than + * 10 embedded SG entries. -DaveM) + */ + if (sges_left) { + struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; + u32 v1, v2; + int sge_spill2; + int sge_cur_spill; + int sgCnt; + u8 *pSgBucket; + int chain_sz; + + len = 0; + + /* sge_spill2 = 15; + * spill2: for req_sz == 128 (128/8==16 SGEs max, first time!), --> use 15 + * spill2: for req_sz == 96 ( 96/8==12 SGEs max, first time!), --> use 11 + */ + sge_spill2 = frm_sz / 8 - 1; + dsgprintk((KERN_INFO MYNAM ": SG: %x spill2 = %d\n", + my_idx, sge_spill2)); + + pSgBucket = NULL; + sgCnt = 0; + sge_cur_spill = sge_spill1; + while (sges_left) { +#if 0 + if (sg_dma_len(sg) > max_sgent_len) { + max_sgent_len = sg_dma_len(sg); + printk(KERN_INFO MYNAM ": MPT_MaxSgentLen = %d\n", max_sgent_len); + } +#endif + /* Write one simple SGE */ + v1 = sgdir | 0x10000000 | sg_dma_len(sg); + len += sg_dma_len(sg); + v2 = sg_dma_address(sg); + dsgprintk((KERN_INFO MYNAM ": SG: %x Writing SGE @%p: %08x %08x, sges_left=%d\n", + my_idx, mptr, v1, v2, sges_left)); + *mptr++ = cpu_to_le32(v1); + *mptr++ = cpu_to_le32(v2); + sg++; + sgCnt++; + + if (--sges_left == 0) { + /* re-write 1st word of previous SGE with SIMPLE, + * LE, EOB, and EOL bits! + */ + v1 = 0xD1000000 | sgdir | sg_dma_len(sg-1); + dsgprintk((KERN_INFO MYNAM ": SG: %x (re)Writing SGE @%p: %08x (VERY LAST SGE!)\n", + my_idx, mptr-2, v1)); + *(mptr - 2) = cpu_to_le32(v1); + } else { + if ((sges_left > 1) && ((sgCnt % sge_cur_spill) == 0)) { + dsgprintk((KERN_INFO MYNAM ": SG: %x SG spill at modulo 0!\n", + my_idx)); + + /* Fixup previous SGE with LE bit! */ + v1 = sgdir | 0x90000000 | sg_dma_len(sg-1); + dsgprintk((KERN_INFO MYNAM ": SG: %x (re)Writing SGE @%p: %08x (LAST BUCKET SGE!)\n", + my_idx, mptr-2, v1)); + *(mptr - 2) = cpu_to_le32(v1); + + chain_offset = 0; + /* Going to need another chain? */ + if (sges_left > (sge_spill2+1)) { +#if 0 + chain_offset = 0x1E; +#endif + chain_offset = (frm_sz - 8) / 4; + chain_sz = frm_sz; + } else { + chain_sz = sges_left * 8; + } + + /* write chain SGE at mptr. */ + v1 = 0x30000000 | chain_offset<<16 | chain_sz; + if (pSgBucket == NULL) { + pSgBucket = hd->SgHunks + + (my_idx * frm_sz * MPT_SG_BUCKETS_PER_HUNK); + } else { + pSgBucket += frm_sz; + } + v2 = (hd->SgHunksDMA + + ((u8 *)pSgBucket - (u8 *)hd->SgHunks)); + dsgprintk((KERN_INFO MYNAM ": SG: %x Writing SGE @%p: %08x %08x (CHAIN!)\n", + my_idx, mptr, v1, v2)); + *(mptr++) = cpu_to_le32(v1); + *(mptr) = cpu_to_le32(v2); + + mptr = (u32 *) pSgBucket; + sgCnt = 0; + sge_cur_spill = sge_spill2; + } + } + } + } else { + dsgprintk((KERN_INFO MYNAM ": SG: non-SG for %p, len=%d\n", + SCpnt, SCpnt->request_bufflen)); + + if (len > 0) { + dma_addr_t buf_dma_addr; + + buf_dma_addr = (dma_addr_t) (unsigned long)SCpnt->SCp.ptr; + *(mptr++) = cpu_to_le32(0xD1000000|sgdir|SCpnt->request_bufflen); + *(mptr++) = cpu_to_le32(buf_dma_addr); + } + } + +#ifdef MPT_DEBUG + /* if (SCpnt->request_bufflen > max_xfer) */ + if (len > max_xfer) { + max_xfer = len; + dprintk((KERN_INFO MYNAM ": MPT_MaxXfer = %d\n", max_xfer)); + } +#endif + + hd->ScsiLookup[my_idx] = SCpnt; + + /* Main banana... */ + mpt_put_msg_frame(ScsiDoneCtx, hd->ioc->id, mf); + + atomic_inc(&queue_depth); + if (atomic_read(&queue_depth) > max_qd) { + max_qd = atomic_read(&queue_depth); + dprintk((KERN_INFO MYNAM ": Queue depth now %d.\n", max_qd)); + } + + mb(); + dmfprintk((KERN_INFO MYNAM ": Issued SCSI cmd (%p)\n", SCpnt)); + + return 0; +} + +#ifdef MPT_SCSI_USE_NEW_EH /* { */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + mptscsih_abort + Returns: 0=SUCCESS, else FAILED +*/ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_abort - Abort linux Scsi_Cmnd routine, new_eh variant + * @SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted + * + * (linux Scsi_Host_Template.eh_abort_handler routine) + * + * Returns SUCCESS or FAILED. + */ +int +mptscsih_abort(Scsi_Cmnd * SCpnt) +{ + MPT_FRAME_HDR *mf; + SCSITaskMgmt_t *pScsiTm; + MPT_SCSI_HOST *hd; + u32 *msg; + u32 ctx2abort; + int i; + + printk(KERN_WARNING MYNAM ": Attempting _ABORT SCSI IO (=%p)\n", SCpnt); + printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth)); + + hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata; + + if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) { +/* SCpnt->result = DID_SOFT_ERROR << 16; */ + SCpnt->result = STS_BUSY; + SCpnt->scsi_done(SCpnt); + return FAILED; + } + + pScsiTm = (SCSITaskMgmt_t *) mf; + msg = (u32 *) mf; + + pScsiTm->TargetID = SCpnt->target; + pScsiTm->Bus = hd->port; + pScsiTm->ChainOffset = 0; + pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; + + pScsiTm->Reserved = 0; + pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK; + pScsiTm->Reserved1 = 0; + pScsiTm->MsgFlags = 0; + + for (i = 0; i < 8; i++) { + u8 val = 0; + if (i == 1) + val = SCpnt->lun; + pScsiTm->LUN[i] = val; + } + + for (i = 0; i < 7; i++) + pScsiTm->Reserved2[i] = 0; + + /* Most important! Set TaskMsgContext to SCpnt's MsgContext! + * (the IO to be ABORT'd) + * + * NOTE: Since we do not byteswap MsgContext, we do not + * swap it here either. It is an opaque cookie to + * the controller, so it does not matter. -DaveM + */ + ctx2abort = SCPNT_TO_MSGCTX(SCpnt); + dprintk((KERN_INFO MYNAM ":DbG: ctx2abort = %08x\n", ctx2abort)); + pScsiTm->TaskMsgContext = ctx2abort; + + wmb(); + +/* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake + mpt_put_msg_frame(hd->ioc->id, mf); +*/ +/* FIXME! Check return status! */ + (void) mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id, sizeof(SCSITaskMgmt_t), msg); + + wmb(); + + return SUCCESS; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_bus_reset - Perform a SCSI BUS_RESET! new_eh variant + * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to + * + * (linux Scsi_Host_Template.eh_bus_reset_handler routine) + * + * Returns SUCCESS or FAILED. + */ +int +mptscsih_bus_reset(Scsi_Cmnd * SCpnt) +{ + MPT_FRAME_HDR *mf; + SCSITaskMgmt_t *pScsiTm; + MPT_SCSI_HOST *hd; + u32 *msg; + int i; + + printk(KERN_WARNING MYNAM ": Attempting _BUS_RESET (%p)\n", SCpnt); + printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth)); + + hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata; + + if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) { +/* SCpnt->result = DID_SOFT_ERROR << 16; */ + SCpnt->result = STS_BUSY; + SCpnt->scsi_done(SCpnt); + return FAILED; + } + + pScsiTm = (SCSITaskMgmt_t *) mf; + msg = (u32 *) mf; + + pScsiTm->TargetID = SCpnt->target; + pScsiTm->Bus = hd->port; + pScsiTm->ChainOffset = 0; + pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; + + pScsiTm->Reserved = 0; + pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS; + pScsiTm->Reserved1 = 0; + pScsiTm->MsgFlags = 0; + + for (i = 0; i < 8; i++) + pScsiTm->LUN[i] = 0; + + /* Control: No data direction, set task mgmt bit? */ + for (i = 0; i < 7; i++) + pScsiTm->Reserved2[i] = 0; + + pScsiTm->TaskMsgContext = cpu_to_le32(0); + + wmb(); + +/* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake + mpt_put_msg_frame(hd->ioc->id, mf); +*/ +/* FIXME! Check return status! */ + (void) mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id, sizeof(SCSITaskMgmt_t), msg); + + wmb(); + + return SUCCESS; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_dev_reset - Perform a SCSI TARGET_RESET! new_eh variant + * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to + * + * (linux Scsi_Host_Template.eh_dev_reset_handler routine) + * + * Returns SUCCESS or FAILED. + */ +int +mptscsih_dev_reset(Scsi_Cmnd * SCpnt) +{ + MPT_FRAME_HDR *mf; + SCSITaskMgmt_t *pScsiTm; + MPT_SCSI_HOST *hd; + u32 *msg; + int i; + + printk(KERN_WARNING MYNAM ": Attempting _TARGET_RESET (%p)\n", SCpnt); + printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth)); + + hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata; + + if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) { +/* SCpnt->result = DID_SOFT_ERROR << 16; */ + SCpnt->result = STS_BUSY; + SCpnt->scsi_done(SCpnt); + return FAILED; + } + + pScsiTm = (SCSITaskMgmt_t *) mf; + msg = (u32*)mf; + + pScsiTm->TargetID = SCpnt->target; + pScsiTm->Bus = hd->port; + pScsiTm->ChainOffset = 0; + pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; + + pScsiTm->Reserved = 0; + pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET; + pScsiTm->Reserved1 = 0; + pScsiTm->MsgFlags = 0; + + /* _TARGET_RESET goes to LUN 0 always! */ + for (i = 0; i < 8; i++) + pScsiTm->LUN[i] = 0; + + /* Control: No data direction, set task mgmt bit? */ + for (i = 0; i < 7; i++) + pScsiTm->Reserved2[i] = 0; + + pScsiTm->TaskMsgContext = cpu_to_le32(0); + + wmb(); + +/* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake + mpt_put_msg_frame(hd->ioc->id, mf); +*/ +/* FIXME! Check return status! */ + (void) mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id, sizeof(SCSITaskMgmt_t), msg); + + wmb(); + + return SUCCESS; +} + +#if 0 /* { */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_host_reset - Perform a SCSI host adapter RESET! + * new_eh variant + * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to + * + * (linux Scsi_Host_Template.eh_host_reset_handler routine) + * + * Returns SUCCESS or FAILED. + */ +int +mptscsih_host_reset(Scsi_Cmnd * SCpnt) +{ + return FAILED; +} +#endif /* } */ + +#else /* MPT_SCSI old EH stuff... */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_old_abort - Abort linux Scsi_Cmnd routine + * @SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted + * + * (linux Scsi_Host_Template.abort routine) + * + * Returns SCSI_ABORT_{SUCCESS,BUSY,PENDING}. + */ +int +mptscsih_old_abort(Scsi_Cmnd *SCpnt) +{ + MPT_SCSI_HOST *hd; + MPT_FRAME_HDR *mf; + struct tq_struct *ptaskfoo; + unsigned long flags; + + printk(KERN_WARNING MYNAM ": Scheduling _ABORT SCSI IO (=%p)\n", SCpnt); + printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth)); + + if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) { + SCpnt->result = DID_ABORT << 16; + SCpnt->scsi_done(SCpnt); + return SCSI_ABORT_SUCCESS; + } + + /* + * Check to see if there's already an ABORT queued for this guy. + */ + mf = search_taskQ(0,SCpnt,MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK); + if (mf != NULL) { + return SCSI_ABORT_PENDING; + } + + if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) { +/* SCpnt->result = DID_SOFT_ERROR << 16; */ + SCpnt->result = STS_BUSY; + SCpnt->scsi_done(SCpnt); + return SCSI_ABORT_BUSY; + } + + /* + * Add ourselves to (end of) mpt_scsih_taskQ. + * Check to see if our _bh is running. If NOT, schedule it. + */ + dslprintk((KERN_INFO MYNAM ": spinlock#2\n")); + spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags); + Q_ADD_TAIL(&mpt_scsih_taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR); + mpt_scsih_taskQ_cnt++; + /* Yikes - linkage! */ +/* SCpnt->host_scribble = (unsigned char *)mf; */ + mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK; + mf->u.frame.linkage.argp1 = SCpnt; + if (! mpt_scsih_taskQ_bh_active) { + mpt_scsih_taskQ_bh_active = 1; + /* + * Oh how cute, no alloc/free/mgmt needed if we use + * (bottom/unused portion of) MPT request frame. + */ + ptaskfoo = (struct tq_struct *) ((u8*)mf + hd->ioc->req_sz - sizeof(*ptaskfoo)); + ptaskfoo->sync = 0; + ptaskfoo->routine = mptscsih_taskmgmt_bh; + ptaskfoo->data = SCpnt; + + SCHEDULE_TASK(ptaskfoo); + } + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); + + return SCSI_ABORT_PENDING; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_old_reset - Perform a SCSI BUS_RESET! + * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to + * @reset_flags: (not used?) + * + * (linux Scsi_Host_Template.reset routine) + * + * Returns SCSI_RESET_{SUCCESS,PUNT,PENDING}. + */ +int +mptscsih_old_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) +{ + MPT_SCSI_HOST *hd; + MPT_FRAME_HDR *mf; + struct tq_struct *ptaskfoo; + unsigned long flags; + + printk(KERN_WARNING MYNAM ": Scheduling _BUS_RESET (=%p)\n", SCpnt); + printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth)); + + if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) { + SCpnt->result = DID_RESET << 16; + SCpnt->scsi_done(SCpnt); + return SCSI_RESET_SUCCESS; + } + + /* + * Check to see if there's already a BUS_RESET queued for this guy. + */ + mf = search_taskQ(0,SCpnt,MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS); + if (mf != NULL) { + return SCSI_RESET_PENDING; + } + + if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) { +/* SCpnt->result = DID_SOFT_ERROR << 16; */ + SCpnt->result = STS_BUSY; + SCpnt->scsi_done(SCpnt); + return SCSI_RESET_PUNT; + } + + /* + * Add ourselves to (end of) mpt_scsih_taskQ. + * Check to see if our _bh is running. If NOT, schedule it. + */ + dslprintk((KERN_INFO MYNAM ": spinlock#3\n")); + spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags); + Q_ADD_TAIL(&mpt_scsih_taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR); + mpt_scsih_taskQ_cnt++; + /* Yikes - linkage! */ +/* SCpnt->host_scribble = (unsigned char *)mf; */ + mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS; + mf->u.frame.linkage.argp1 = SCpnt; + if (! mpt_scsih_taskQ_bh_active) { + mpt_scsih_taskQ_bh_active = 1; + /* + * Oh how cute, no alloc/free/mgmt needed if we use + * (bottom/unused portion of) MPT request frame. + */ + ptaskfoo = (struct tq_struct *) ((u8*)mf + hd->ioc->req_sz - sizeof(*ptaskfoo)); + ptaskfoo->sync = 0; + ptaskfoo->routine = mptscsih_taskmgmt_bh; + ptaskfoo->data = SCpnt; + + SCHEDULE_TASK(ptaskfoo); + } + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); + + return SCSI_RESET_PENDING; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_taskmgmt_bh - SCSI task mgmt bottom half handler + * @sc: (unused) + * + * This routine (thread) is active whenever there are any outstanding + * SCSI task management requests for a SCSI host adapter. + * IMPORTANT! This routine is scheduled therefore should never be + * running in ISR context. i.e., it's safe to sleep here. + */ +void +mptscsih_taskmgmt_bh(void *sc) +{ + Scsi_Cmnd *SCpnt; + MPT_FRAME_HDR *mf; + SCSITaskMgmt_t *pScsiTm; + MPT_SCSI_HOST *hd; + u32 ctx2abort = 0; + int i; + unsigned long flags; + u8 task_type; + + dslprintk((KERN_INFO MYNAM ": spinlock#4\n")); + spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags); + mpt_scsih_taskQ_bh_active = 1; + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); + + while (1) { + /* + * We MUST remove item from taskQ *before* we format the + * frame as a SCSITaskMgmt request and send it down to the IOC. + */ + dslprintk((KERN_INFO MYNAM ": spinlock#5\n")); + spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags); + if (Q_IS_EMPTY(&mpt_scsih_taskQ)) { + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); + break; + } + mf = mpt_scsih_taskQ.head; + Q_DEL_ITEM(&mf->u.frame.linkage); + mpt_scsih_taskQ_cnt--; + mpt_scsih_active_taskmgmt_mf = mf; + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); + + SCpnt = (Scsi_Cmnd*)mf->u.frame.linkage.argp1; + if (SCpnt == NULL) { + printk(KERN_ERR MYNAM ": ERROR: TaskMgmt has NULL SCpnt! (%p:%p)\n", mf, SCpnt); + continue; + } + pScsiTm = (SCSITaskMgmt_t *) mf; + + for (i = 0; i < 8; i++) { + pScsiTm->LUN[i] = 0; + } + + task_type = mf->u.frame.linkage.arg1; + if (task_type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) + { + printk(KERN_WARNING MYNAM ": Attempting _ABORT SCSI IO! (mf:sc=%p:%p)\n", mf, SCpnt); + + /* Most important! Set TaskMsgContext to SCpnt's MsgContext! + * (the IO to be ABORT'd) + * + * NOTE: Since we do not byteswap MsgContext, we do not + * swap it here either. It is an opaque cookie to + * the controller, so it does not matter. -DaveM + */ + ctx2abort = SCPNT_TO_MSGCTX(SCpnt); + pScsiTm->LUN[1] = SCpnt->lun; + } + else if (task_type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) + { + printk(KERN_WARNING MYNAM ": Attempting _BUS_RESET! (against SCSI IO mf:sc=%p:%p)\n", mf, SCpnt); + } +#if 0 + else if (task_type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {} + else if (task_type == MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET) {} +#endif + + printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth)); + + hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata; + + pScsiTm->TargetID = SCpnt->target; + pScsiTm->Bus = hd->port; + pScsiTm->ChainOffset = 0; + pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; + + pScsiTm->Reserved = 0; + pScsiTm->TaskType = task_type; + pScsiTm->Reserved1 = 0; + pScsiTm->MsgFlags = 0; + + for (i = 0; i < 7; i++) + pScsiTm->Reserved2[i] = 0; + + dprintk((KERN_INFO MYNAM ":DbG: ctx2abort = %08x\n", ctx2abort)); + pScsiTm->TaskMsgContext = ctx2abort; + + /* Control: No data direction, set task mgmt bit? */ + + /* + * As of MPI v0.10 this request can NOT be sent (normally) + * via FIFOs. So we can't: + * mpt_put_msg_frame(ScsiTaskCtx, hd->ioc->id, mf); + * SCSITaskMgmt requests MUST be sent ONLY via + * Doorbell/handshake now. :-( + * + * FIXME! Check return status! + */ + (void) mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id, sizeof(SCSITaskMgmt_t), (u32*)mf); + + /* Spin-Wait for TaskMgmt complete!!! */ + while (mpt_scsih_active_taskmgmt_mf != NULL) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/2); + } + } + + dslprintk((KERN_INFO MYNAM ": spinlock#6\n")); + spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags); + mpt_scsih_taskQ_bh_active = 0; + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); + + return; +} + +#endif /* } */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_taskmgmt_complete - Callback routine, gets registered to + * Fusion MPT base driver + * @ioc: Pointer to MPT_ADAPTER structure + * @mf: Pointer to SCSI task mgmt request frame + * @r: Pointer to SCSI task mgmt reply frame + * + * This routine is called from mptbase.c::mpt_interrupt() at the completion + * of any SCSI task management request. + * This routine is registered with the MPT (base) driver at driver + * load/init time via the mpt_register() API call. + * + * Returns 1 indicating alloc'd request frame ptr should be freed. + */ +static int +mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r) +{ + SCSITaskMgmtReply_t *pScsiTmReply; + SCSITaskMgmt_t *pScsiTmReq; + u8 tmType; +#ifndef MPT_SCSI_USE_NEW_EH + unsigned long flags; +#endif + + dprintk((KERN_INFO MYNAM ": SCSI TaskMgmt completed mf=%p, r=%p\n", + mf, r)); + +#ifndef MPT_SCSI_USE_NEW_EH + dslprintk((KERN_INFO MYNAM ": spinlock#7\n")); + spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags); + /* It better be the active one! */ + if (mf != mpt_scsih_active_taskmgmt_mf) { + printk(KERN_ERR MYNAM ": ERROR! Non-active TaskMgmt (=%p) completed!\n", mf); + mpt_scsih_active_taskmgmt_mf = NULL; + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); + return 1; + } + +#ifdef MPT_DEBUG + if ((mf == NULL) || + (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) { + printk(KERN_ERR MYNAM ": ERROR! NULL or BAD TaskMgmt ptr (=%p)!\n", mf); + mpt_scsih_active_taskmgmt_mf = NULL; + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); + return 1; + } +#endif + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); +#endif + + if (r != NULL) { + pScsiTmReply = (SCSITaskMgmtReply_t*)r; + pScsiTmReq = (SCSITaskMgmt_t*)mf; + + /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */ + tmType = pScsiTmReq->TaskType; + + dprintk((KERN_INFO MYNAM ": TaskType = %d\n", tmType)); + dprintk((KERN_INFO MYNAM ": TerminationCount = %d\n", + le32_to_cpu(pScsiTmReply->TerminationCount))); + + /* Error? (anything non-zero?) */ + if (*(u32 *)&pScsiTmReply->Reserved2[0]) { + dprintk((KERN_INFO MYNAM ": SCSI TaskMgmt (%d) - Oops!\n", tmType)); + dprintk((KERN_INFO MYNAM ": IOCStatus = %04xh\n", + le16_to_cpu(pScsiTmReply->IOCStatus))); + dprintk((KERN_INFO MYNAM ": IOCLogInfo = %08xh\n", + le32_to_cpu(pScsiTmReply->IOCLogInfo))); + } else { + dprintk((KERN_INFO MYNAM ": SCSI TaskMgmt (%d) SUCCESS!\n", tmType)); + } + } + +#ifndef MPT_SCSI_USE_NEW_EH + /* + * Signal to _bh thread that we finished. + */ + dslprintk((KERN_INFO MYNAM ": spinlock#8\n")); + spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags); + mpt_scsih_active_taskmgmt_mf = NULL; + spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags); +#endif + + return 1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * This is anyones guess quite frankly. + */ + +int +mptscsih_bios_param(Disk * disk, kdev_t dev, int *ip) +{ + int size; + + size = disk->capacity; + ip[0] = 64; /* heads */ + ip[1] = 32; /* sectors */ + if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */ + ip[0] = 255; /* heads */ + ip[1] = 63; /* sectors */ + ip[2] = size / (255 * 63); /* cylinders */ + } + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Private routines... + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* 19991030 -sralston + * Return absolute SCSI data direction: + * 1 = _DATA_OUT + * 0 = _DIR_NONE + * -1 = _DATA_IN + */ +static int +mptscsih_io_direction(Scsi_Cmnd *cmd) +{ + switch (cmd->cmnd[0]) { + /* _DATA_OUT commands */ + case WRITE_6: case WRITE_10: case WRITE_12: + case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER: + case WRITE_VERIFY: case WRITE_VERIFY_12: + case COMPARE: case COPY: case COPY_VERIFY: + case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: + case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: + case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: + case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK: + case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: + case REASSIGN_BLOCKS: + case PERSISTENT_RESERVE_OUT: + case 0xea: + return 1; + + /* No data transfer commands */ + case SEEK_6: case SEEK_10: + case RESERVE: case RELEASE: + case TEST_UNIT_READY: + case START_STOP: + case ALLOW_MEDIUM_REMOVAL: + return 0; + + /* Conditional data transfer commands */ + case FORMAT_UNIT: + if (cmd->cmnd[1] & 0x10) /* FmtData (data out phase)? */ + return 1; + else + return 0; + + case VERIFY: + if (cmd->cmnd[1] & 0x02) /* VERIFY:BYTCHK (data out phase)? */ + return 1; + else + return 0; + + case RESERVE_10: + if (cmd->cmnd[1] & 0x03) /* RESERSE:{LongID|Extent} (data out phase)? */ + return 1; + else + return 0; + +#if 0 + case REZERO_UNIT: /* (or REWIND) */ + case SPACE: + case ERASE: case ERASE_10: + case SYNCHRONIZE_CACHE: + case LOCK_UNLOCK_CACHE: +#endif + + /* Must be data _IN! */ + default: + return -1; + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static void +copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply) +{ + MPT_SCSI_DEV *mpt_sdev = NULL; + u32 sense_count = le32_to_cpu(pScsiReply->SenseCount); + char devFoo[32]; + IO_Info_t thisIo; + + if (sc && sc->device) + mpt_sdev = (MPT_SCSI_DEV*) sc->device->hostdata; + + if (sense_count) { + u8 *sense_data; + int req_index; + + /* Copy the sense received into the scsi command block. */ + req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * 256)); + memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc)); + /* Cache SenseData for this SCSI device! */ + if (mpt_sdev) { + memcpy(mpt_sdev->CachedSense.data, sense_data, sense_count); + mpt_sdev->sense_sz = sense_count; + } + } else { + dprintk((KERN_INFO MYNAM ": Hmmm... SenseData len=0! (?)\n")); + } + + + thisIo.cdbPtr = sc->cmnd; + thisIo.sensePtr = sc->sense_buffer; + thisIo.SCSIStatus = pScsiReply->SCSIStatus; + thisIo.DoDisplay = 1; + sprintf(devFoo, "ioc%d,scsi%d:%d", hd->ioc->id, sc->target, sc->lun); + thisIo.DevIDStr = devFoo; +/* fubar */ + thisIo.dataPtr = NULL; + thisIo.inqPtr = NULL; + if (sc->device) { + thisIo.inqPtr = sc->device->vendor-8; /* FIXME!!! */ + } + (void) mpt_ScsiHost_ErrorReport(&thisIo); + + return; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static u32 +SCPNT_TO_MSGCTX(Scsi_Cmnd *sc) +{ + MPT_SCSI_HOST *hd; + MPT_FRAME_HDR *mf; + int i; + + hd = (MPT_SCSI_HOST *) sc->host->hostdata; + + for (i = 0; i < hd->ioc->req_depth; i++) { + if (hd->ScsiLookup[i] == sc) { + mf = MPT_INDEX_2_MFPTR(hd->ioc, i); + return mf->u.frame.hwhdr.msgctxu.MsgContext; + } + } + + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/* see mptscsih.h */ + +#ifdef MPT_SCSIHOST_NEED_ENTRY_EXIT_HOOKUPS + static Scsi_Host_Template driver_template = MPT_SCSIHOST; +# include "../../scsi/scsi_module.c" +#endif + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) +{ + u8 event = le32_to_cpu(pEvReply->Event) & 0xFF; + + dprintk((KERN_INFO MYNAM ": MPT event (=%02Xh) routed to SCSI host driver!\n", event)); + + switch (event) { + case MPI_EVENT_UNIT_ATTENTION: /* 03 */ + /* FIXME! */ + break; + case MPI_EVENT_IOC_BUS_RESET: /* 04 */ + /* FIXME! */ + break; + case MPI_EVENT_EXT_BUS_RESET: /* 05 */ + /* FIXME! */ + break; + case MPI_EVENT_LOGOUT: /* 09 */ + /* FIXME! */ + break; + + /* + * CHECKME! Don't think we need to do + * anything for these, but... + */ + case MPI_EVENT_RESCAN: /* 06 */ + case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */ + case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */ + /* + * CHECKME! Falling thru... + */ + + case MPI_EVENT_NONE: /* 00 */ + case MPI_EVENT_LOG_DATA: /* 01 */ + case MPI_EVENT_STATE_CHANGE: /* 02 */ + case MPI_EVENT_EVENT_CHANGE: /* 0A */ + default: + dprintk((KERN_INFO MYNAM ": Ignoring event (=%02Xh)\n", event)); + break; + } + + return 1; /* currently means nothing really */ +} + +#if 0 /* { */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * scsiherr.c - Fusion MPT SCSI Host driver error handling/reporting. + * + * drivers/message/fusion/scsiherr.c + */ + +//extern const char **mpt_ScsiOpcodesPtr; /* needed by mptscsih.c */ +//extern ASCQ_Table_t *mpt_ASCQ_TablePtr; +//extern int mpt_ASCQ_TableSz; + +/* Lie! */ +#define MYNAM "mptscsih" + +#endif /* } */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Private data... + */ +static ASCQ_Table_t *mptscsih_ASCQ_TablePtr; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* old symsense.c stuff... */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Private data... + * To protect ourselves against those that would pass us bogus pointers + */ +static u8 dummyInqData[SCSI_STD_INQUIRY_BYTES] + = { 0x1F, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static u8 dummySenseData[SCSI_STD_SENSE_BYTES] + = { 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 }; +static u8 dummyCDB[16] + = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static u8 dummyScsiData[16] + = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +#if 0 +static const char *PeripheralDeviceTypeString[32] = { + "Direct-access", /* 00h */ + "Sequential-access", /* 01h */ + "Printer", /* 02h */ + "Processor", /* 03h */ + /*"Write-Once-Read-Multiple",*/ /* 04h */ + "WORM", /* 04h */ + "CD-ROM", /* 05h */ + "Scanner", /* 06h */ + "Optical memory", /* 07h */ + "Media Changer", /* 08h */ + "Communications", /* 09h */ + "(Graphics arts pre-press)", /* 0Ah */ + "(Graphics arts pre-press)", /* 0Bh */ + "Array controller", /* 0Ch */ + "Enclosure services", /* 0Dh */ + "Simplified direct-access", /* 0Eh */ + "Reserved-0Fh", /* 0Fh */ + "Reserved-10h", /* 10h */ + "Reserved-11h", /* 11h */ + "Reserved-12h", /* 12h */ + "Reserved-13h", /* 13h */ + "Reserved-14h", /* 14h */ + "Reserved-15h", /* 15h */ + "Reserved-16h", /* 16h */ + "Reserved-17h", /* 17h */ + "Reserved-18h", /* 18h */ + "Reserved-19h", /* 19h */ + "Reserved-1Ah", /* 1Ah */ + "Reserved-1Bh", /* 1Bh */ + "Reserved-1Ch", /* 1Ch */ + "Reserved-1Dh", /* 1Dh */ + "Reserved-1Eh", /* 1Eh */ + "Unknown" /* 1Fh */ +}; +#endif + +static char *ScsiStatusString[] = { + "GOOD", /* 00h */ + NULL, /* 01h */ + "CHECK CONDITION", /* 02h */ + NULL, /* 03h */ + "CONDITION MET", /* 04h */ + NULL, /* 05h */ + NULL, /* 06h */ + NULL, /* 07h */ + "BUSY", /* 08h */ + NULL, /* 09h */ + NULL, /* 0Ah */ + NULL, /* 0Bh */ + NULL, /* 0Ch */ + NULL, /* 0Dh */ + NULL, /* 0Eh */ + NULL, /* 0Fh */ + "INTERMEDIATE", /* 10h */ + NULL, /* 11h */ + NULL, /* 12h */ + NULL, /* 13h */ + "INTERMEDIATE-CONDITION MET", /* 14h */ + NULL, /* 15h */ + NULL, /* 16h */ + NULL, /* 17h */ + "RESERVATION CONFLICT", /* 18h */ + NULL, /* 19h */ + NULL, /* 1Ah */ + NULL, /* 1Bh */ + NULL, /* 1Ch */ + NULL, /* 1Dh */ + NULL, /* 1Eh */ + NULL, /* 1Fh */ + NULL, /* 20h */ + NULL, /* 21h */ + "COMMAND TERMINATED", /* 22h */ + NULL, /* 23h */ + NULL, /* 24h */ + NULL, /* 25h */ + NULL, /* 26h */ + NULL, /* 27h */ + "TASK SET FULL", /* 28h */ + NULL, /* 29h */ + NULL, /* 2Ah */ + NULL, /* 2Bh */ + NULL, /* 2Ch */ + NULL, /* 2Dh */ + NULL, /* 2Eh */ + NULL, /* 2Fh */ + "ACA ACTIVE", /* 30h */ + NULL +}; + +static const char *ScsiCommonOpString[] = { + "TEST UNIT READY", /* 00h */ + "REZERO UNIT (REWIND)", /* 01h */ + NULL, /* 02h */ + "REQUEST_SENSE", /* 03h */ + "FORMAT UNIT (MEDIUM)", /* 04h */ + "READ BLOCK LIMITS", /* 05h */ + NULL, /* 06h */ + "REASSIGN BLOCKS", /* 07h */ + "READ(6)", /* 08h */ + NULL, /* 09h */ + "WRITE(6)", /* 0Ah */ + "SEEK(6)", /* 0Bh */ + NULL, /* 0Ch */ + NULL, /* 0Dh */ + NULL, /* 0Eh */ + "READ REVERSE", /* 0Fh */ + "WRITE_FILEMARKS", /* 10h */ + "SPACE(6)", /* 11h */ + "INQUIRY", /* 12h */ + NULL +}; + +static const char *SenseKeyString[] = { + "NO SENSE", /* 0h */ + "RECOVERED ERROR", /* 1h */ + "NOT READY", /* 2h */ + "MEDIUM ERROR", /* 3h */ + "HARDWARE ERROR", /* 4h */ + "ILLEGAL REQUEST", /* 5h */ + "UNIT ATTENTION", /* 6h */ + "DATA PROTECT", /* 7h */ + "BLANK CHECK", /* 8h */ + "VENDOR-SPECIFIC", /* 9h */ + "ABORTED COPY", /* Ah */ + "ABORTED COMMAND", /* Bh */ + "EQUAL (obsolete)", /* Ch */ + "VOLUME OVERFLOW", /* Dh */ + "MISCOMPARE", /* Eh */ + "RESERVED", /* Fh */ + NULL +}; + +#define SPECIAL_ASCQ(c,q) \ + (((c) == 0x40 && (q) != 0x00) || ((c) == 0x4D) || ((c) == 0x70)) + +#if 0 +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Sense_Key_Specific() - If Sense_Key_Specific_Valid bit is set, + * then print additional information via + * a call to SDMS_SystemAlert(). + * + * Return: nothing + */ +static void Sense_Key_Specific(IO_Info_t *ioop, char *msg1) +{ + u8 *sd; + u8 BadValue; + u8 SenseKey; + int Offset; + int len = strlen(msg1); + + sd = ioop->sensePtr; + if (SD_Additional_Sense_Length(sd) < 8) + return; + + SenseKey = SD_Sense_Key(sd); + + if (SD_Sense_Key_Specific_Valid(sd)) { + if (SenseKey == SK_ILLEGAL_REQUEST) { + Offset = SD_Bad_Byte(sd); + if (SD_Was_Illegal_Request(sd)) { + BadValue = ioop->cdbPtr[Offset]; + len += sprintf(msg1+len, "\n Illegal CDB value=%02Xh found at CDB ", + BadValue); + } else { + BadValue = ioop->dataPtr[Offset]; + len += sprintf(msg1+len, "\n Illegal DATA value=%02Xh found at DATA ", + BadValue); + } + len += sprintf(msg1+len, "byte=%02Xh", Offset); + if (SD_SKS_Bit_Pointer_Valid(sd)) + len += sprintf(msg1+len, "/bit=%1Xh", SD_SKS_Bit_Pointer(sd)); + } else if ((SenseKey == SK_RECOVERED_ERROR) || + (SenseKey == SK_HARDWARE_ERROR) || + (SenseKey == SK_MEDIUM_ERROR)) { + len += sprintf(msg1+len, "\n Recovery algorithm Actual_Retry_Count=%02Xh", + SD_Actual_Retry_Count(sd)); + } + } +} +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int dump_cdb(char *foo, unsigned char *cdb) +{ + int i, grpCode, cdbLen; + int l = 0; + + grpCode = cdb[0] >> 5; + if (grpCode < 1) + cdbLen = 6; + else if (grpCode < 3) + cdbLen = 10; + else if (grpCode == 5) + cdbLen = 12; + else + cdbLen = 16; + + for (i=0; i < cdbLen; i++) + l += sprintf(foo+l, " %02X", cdb[i]); + + return l; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int dump_sd(char *foo, unsigned char *sd) +{ + int snsLen = 8 + SD_Additional_Sense_Length(sd); + int l = 0; + int i; + + for (i=0; i < MIN(snsLen,18); i++) + l += sprintf(foo+l, " %02X", sd[i]); + l += sprintf(foo+l, "%s", snsLen>18 ? " ..." : ""); + + return l; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* Do ASC/ASCQ lookup/grindage to English readable string(s) */ +static const char * ascq_set_strings_4max( + u8 ASC, u8 ASCQ, + const char **s1, const char **s2, const char **s3, const char **s4) +{ + static const char *asc_04_part1_string = "LOGICAL UNIT "; + static const char *asc_04_part2a_string = "NOT READY, "; + static const char *asc_04_part2b_string = "IS "; + static const char *asc_04_ascq_NN_part3_strings[] = { /* ASC ASCQ (hex) */ + "CAUSE NOT REPORTABLE", /* 04 00 */ + "IN PROCESS OF BECOMING READY", /* 04 01 */ + "INITIALIZING CMD. REQUIRED", /* 04 02 */ + "MANUAL INTERVENTION REQUIRED", /* 04 03 */ + /* Add " IN PROGRESS" to all the following... */ + "FORMAT", /* 04 04 */ + "REBUILD", /* 04 05 */ + "RECALCULATION", /* 04 06 */ + "OPERATION", /* 04 07 */ + "LONG WRITE", /* 04 08 */ + "SELF-TEST", /* 04 09 */ + NULL + }; + static char *asc_04_part4_string = " IN PROGRESS"; + + static char *asc_29_ascq_NN_strings[] = { /* ASC ASCQ (hex) */ + "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED", /* 29 00 */ + "POWER ON OCCURRED", /* 29 01 */ + "SCSI BUS RESET OCCURRED", /* 29 02 */ + "BUS DEVICE RESET FUNCTION OCCURRED", /* 29 03 */ + "DEVICE INTERNAL RESET", /* 29 04 */ + "TRANSCEIVER MODE CHANGED TO SINGLE-ENDED", /* 29 05 */ + "TRANSCEIVER MODE CHANGED TO LVD", /* 29 06 */ + NULL + }; + static char *ascq_vendor_uniq = "(Vendor Unique)"; + static char *ascq_noone = "(no matching ASC/ASCQ description found)"; + int idx; + + *s1 = *s2 = *s3 = *s4 = ""; /* set'em all to the empty "" string */ + + /* CHECKME! Need lock/sem? + * Update and examine for isense module presense. + */ + mptscsih_ASCQ_TablePtr = (ASCQ_Table_t *)mpt_v_ASCQ_TablePtr; + + if (mptscsih_ASCQ_TablePtr == NULL) { + /* 2nd chances... */ + if (ASC == 0x04 && (ASCQ < sizeof(asc_04_ascq_NN_part3_strings)/sizeof(char*)-1)) { + *s1 = asc_04_part1_string; + *s2 = (ASCQ == 0x01) ? asc_04_part2b_string : asc_04_part2a_string; + *s3 = asc_04_ascq_NN_part3_strings[ASCQ]; + /* check for " IN PROGRESS" ones */ + if (ASCQ >= 0x04) + *s4 = asc_04_part4_string; + } else if (ASC == 0x29 && (ASCQ < sizeof(asc_29_ascq_NN_strings)/sizeof(char*)-1)) + *s1 = asc_29_ascq_NN_strings[ASCQ]; + /* + * else { leave all *s[1-4] values pointing to the empty "" string } + */ + return *s1; + } + + /* + * Need to check ASC here; if it is "special," then + * the ASCQ is variable, and indicates failed component number. + * We must treat the ASCQ as a "don't care" while searching the + * mptscsih_ASCQ_Table[] by masking it off, and then restoring it later + * on when we actually need to identify the failed component. + */ + if (SPECIAL_ASCQ(ASC,ASCQ)) + ASCQ = 0xFF; + + /* OK, now search mptscsih_ASCQ_Table[] for a matching entry */ + for (idx = 0; mptscsih_ASCQ_TablePtr && idx < mpt_ASCQ_TableSz; idx++) + if ((ASC == mptscsih_ASCQ_TablePtr[idx].ASC) && (ASCQ == mptscsih_ASCQ_TablePtr[idx].ASCQ)) + return (*s1 = mptscsih_ASCQ_TablePtr[idx].Description); + + if ((ASC >= 0x80) || (ASCQ >= 0x80)) + *s1 = ascq_vendor_uniq; + else + *s1 = ascq_noone; + + return *s1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SCSI Error Report; desired output format... + *--- +SCSI Error Report =-=-=-=-=-=-=-=-=-=-=-=-=-= (ioc0,scsi0:0) + SCSI_Status=02h (CHECK CONDITION) + Original_CDB[]: 00 00 00 00 00 00 - TestUnitReady + SenseData[12h]: 70 00 06 00 00 00 00 0A 00 00 00 00 29 00 03 00 00 00 + SenseKey=6h (UNIT ATTENTION); FRU=03h + ASC/ASCQ=29h/00h, "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED" + *--- + */ + +int mpt_ScsiHost_ErrorReport(IO_Info_t *ioop) +{ + char foo[512]; + char buf2[32]; + char *statstr; + const char *opstr; + int sk = SD_Sense_Key(ioop->sensePtr); + const char *skstr = SenseKeyString[sk]; + unsigned char asc = SD_ASC(ioop->sensePtr); + unsigned char ascq = SD_ASCQ(ioop->sensePtr); + int l; + + /* + * More quiet mode. + * Filter out common, repetitive, warning-type errors... like: + * POWER ON (06,29/00 or 06,29/01), + * SPINNING UP (02,04/01), + * LOGICAL UNIT NOT SUPPORTED (05,25/00), etc. + */ + if ( (sk==SK_UNIT_ATTENTION && asc==0x29 && (ascq==0x00 || ascq==0x01)) + || (sk==SK_NOT_READY && asc==0x04 && ascq==0x01) + || (sk==SK_ILLEGAL_REQUEST && asc==0x25 && ascq==0x00) + ) + { + /* Do nothing! */ + return 0; + } + + /* + * Protect ourselves... + */ + if (ioop->cdbPtr == NULL) + ioop->cdbPtr = dummyCDB; + if (ioop->sensePtr == NULL) + ioop->sensePtr = dummySenseData; + if (ioop->inqPtr == NULL) + ioop->inqPtr = dummyInqData; + if (ioop->dataPtr == NULL) + ioop->dataPtr = dummyScsiData; + + statstr = NULL; + if ((ioop->SCSIStatus >= sizeof(ScsiStatusString)/sizeof(char*)-1) || + ((statstr = (char*)ScsiStatusString[ioop->SCSIStatus]) == NULL)) { + (void) sprintf(buf2, "Bad-Reserved-%02Xh", ioop->SCSIStatus); + statstr = buf2; + } + + opstr = NULL; + if (1+ioop->cdbPtr[0] <= sizeof(ScsiCommonOpString)/sizeof(char*)) + opstr = ScsiCommonOpString[ioop->cdbPtr[0]]; + else if (mpt_ScsiOpcodesPtr) + opstr = mpt_ScsiOpcodesPtr[ioop->cdbPtr[0]]; + + l = sprintf(foo, "SCSI Error Report =-=-= (%s)\n" + " SCSI_Status=%02Xh (%s)\n" + " Original_CDB[]:", + ioop->DevIDStr, + ioop->SCSIStatus, + statstr); + l += dump_cdb(foo+l, ioop->cdbPtr); + if (opstr) + l += sprintf(foo+l, " - \"%s\"", opstr); + l += sprintf(foo+l, "\n SenseData[%02Xh]:", 8+SD_Additional_Sense_Length(ioop->sensePtr)); + l += dump_sd(foo+l, ioop->sensePtr); + l += sprintf(foo+l, "\n SenseKey=%Xh (%s); FRU=%02Xh\n ASC/ASCQ=%02Xh/%02Xh", + sk, skstr, SD_FRU(ioop->sensePtr), asc, ascq ); + + { + const char *x1, *x2, *x3, *x4; + x1 = x2 = x3 = x4 = ""; + x1 = ascq_set_strings_4max(asc, ascq, &x1, &x2, &x3, &x4); + if (x1 != NULL) { + if (x1[0] != '(') + l += sprintf(foo+l, " \"%s%s%s%s\"", x1,x2,x3,x4); + else + l += sprintf(foo+l, " %s%s%s%s", x1,x2,x3,x4); + } + } + +#if 0 + if (SPECIAL_ASCQ(asc,ascq)) + l += sprintf(foo+l, " (%02Xh)", ascq); +#endif + + PrintF(("%s\n", foo)); + + return l; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/mptscsih.h linux.ac/drivers/message/fusion/mptscsih.h --- linux.vanilla/drivers/message/fusion/mptscsih.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/mptscsih.h Sat May 26 17:58:31 2001 @@ -0,0 +1,247 @@ +/* + * linux/drivers/message/fusion/mptscsih.h + * High performance SCSI / Fibre Channel SCSI Host device driver. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Credits: + * This driver would not exist if not for Alan Cox's development + * of the linux i2o driver. + * + * A huge debt of gratitude is owed to David S. Miller (DaveM) + * for fixing much of the stupid and broken stuff in the early + * driver while porting to sparc64 platform. THANK YOU! + * + * (see also mptbase.c) + * + * Copyright (c) 1999-2001 LSI Logic Corporation + * Originally By: Steven J. Ralston + * (mailto:Steve.Ralston@lsil.com) + * + * $Id: mptscsih.h,v 1.7 2001/01/11 16:56:43 sralston Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + 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; version 2 of the License. + + 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. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SCSIHOST_H_INCLUDED +#define SCSIHOST_H_INCLUDED +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include "linux/version.h" + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SCSI Public stuff... + */ + +#ifdef __sparc__ +#define MPT_SCSI_CAN_QUEUE 63 +#define MPT_SCSI_CMD_PER_LUN 63 + /* FIXME! Still investigating qd=64 hang on sparc64... */ +#else +#define MPT_SCSI_CAN_QUEUE 64 +#define MPT_SCSI_CMD_PER_LUN 64 +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Various bits and pieces broke within the lk-2.4.0-testN series:-( + * So here are various HACKS to work around them. + */ + +/* + * Conditionalizing with "#ifdef MODULE/#endif" around: + * static Scsi_Host_Template driver_template = XX; + * #include <../../scsi/scsi_module.c> + * lines was REMOVED @ lk-2.4.0-test9 + * Issue discovered 20001213 by: sshirron + */ +#define MPT_SCSIHOST_NEED_ENTRY_EXIT_HOOKUPS 1 +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,0) +# if LINUX_VERSION_CODE == KERNEL_VERSION(2,4,0) + /* + * Super HACK! -by sralston:-( + * (good grief; heaven help me!) + */ +# include +# if !defined(CAP_LEASE) && !defined(MODULE) +# undef MPT_SCSIHOST_NEED_ENTRY_EXIT_HOOKUPS +# endif +# else +# ifndef MODULE +# undef MPT_SCSIHOST_NEED_ENTRY_EXIT_HOOKUPS +# endif +# endif +#endif + +/* + * tq_scheduler disappeared @ lk-2.4.0-test12 + * (right when newly defined TQ_ACTIVE) + */ +#define HAVE_TQ_SCHED 1 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +# include +# ifdef TQ_ACTIVE +# undef HAVE_TQ_SCHED +# endif +#endif +#ifdef HAVE_TQ_SCHED +#define SCHEDULE_TASK(x) \ + /*MOD_INC_USE_COUNT*/; \ + (x)->next = NULL; \ + queue_task(x, &tq_scheduler) +#else +#define SCHEDULE_TASK(x) \ + /*MOD_INC_USE_COUNT*/; \ + if (schedule_task(x) == 0) { \ + /*MOD_DEC_USE_COUNT*/; \ + } +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#define x_scsi_detect mptscsih_detect +#define x_scsi_release mptscsih_release +#define x_scsi_info mptscsih_info +#define x_scsi_queuecommand mptscsih_qcmd +#define x_scsi_abort mptscsih_abort +#define x_scsi_bus_reset mptscsih_bus_reset +#define x_scsi_dev_reset mptscsih_dev_reset +#define x_scsi_host_reset mptscsih_host_reset +#define x_scsi_bios_param mptscsih_bios_param + +#define x_scsi_taskmgmt_bh mptscsih_taskmgmt_bh +#define x_scsi_old_abort mptscsih_old_abort +#define x_scsi_old_reset mptscsih_old_reset + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * MPT SCSI Host / Initiator decls... + */ +extern int x_scsi_detect(Scsi_Host_Template *); +extern int x_scsi_release(struct Scsi_Host *host); +extern const char *x_scsi_info(struct Scsi_Host *); +/*extern int x_scsi_command(Scsi_Cmnd *);*/ +extern int x_scsi_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +#ifdef MPT_SCSI_USE_NEW_EH +extern int x_scsi_abort(Scsi_Cmnd *); +extern int x_scsi_bus_reset(Scsi_Cmnd *); +extern int x_scsi_dev_reset(Scsi_Cmnd *); +/*extern int x_scsi_host_reset(Scsi_Cmnd *);*/ +#else +extern int x_scsi_old_abort(Scsi_Cmnd *); +extern int x_scsi_old_reset(Scsi_Cmnd *, unsigned int); +#endif +extern int x_scsi_bios_param(Disk *, kdev_t, int *); +extern void x_scsi_taskmgmt_bh(void *); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define PROC_SCSI_DECL +#else +#define PROC_SCSI_DECL proc_name: "mptscsih", +#endif + +#ifdef MPT_SCSI_USE_NEW_EH + +#define MPT_SCSIHOST { \ + next: NULL, \ + PROC_SCSI_DECL \ + name: "MPT SCSI Host", \ + detect: x_scsi_detect, \ + release: x_scsi_release, \ + info: x_scsi_info, \ + command: NULL, \ + queuecommand: x_scsi_queuecommand, \ + eh_strategy_handler: NULL, \ + eh_abort_handler: x_scsi_abort, \ + eh_device_reset_handler: x_scsi_dev_reset, \ + eh_bus_reset_handler: x_scsi_bus_reset, \ + eh_host_reset_handler: NULL, \ + bios_param: x_scsi_bios_param, \ + can_queue: MPT_SCSI_CAN_QUEUE, \ + this_id: -1, \ + sg_tablesize: 25, \ + cmd_per_lun: MPT_SCSI_CMD_PER_LUN, \ + unchecked_isa_dma: 0, \ + use_clustering: ENABLE_CLUSTERING, \ + use_new_eh_code: 1 \ +} + +#else + +#define MPT_SCSIHOST { \ + next: NULL, \ + PROC_SCSI_DECL \ + name: "MPT SCSI Host", \ + detect: x_scsi_detect, \ + release: x_scsi_release, \ + info: x_scsi_info, \ + command: NULL, \ + queuecommand: x_scsi_queuecommand, \ + abort: x_scsi_old_abort, \ + reset: x_scsi_old_reset, \ + bios_param: x_scsi_bios_param, \ + can_queue: MPT_SCSI_CAN_QUEUE, \ + this_id: -1, \ + sg_tablesize: 25, \ + cmd_per_lun: MPT_SCSI_CMD_PER_LUN, \ + unchecked_isa_dma: 0, \ + use_clustering: ENABLE_CLUSTERING \ +} +#endif + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/* include/scsi/scsi.h may not be quite complete... */ +#ifndef RESERVE_10 +#define RESERVE_10 0x56 +#endif +#ifndef RELEASE_10 +#define RELEASE_10 0x57 +#endif +#ifndef PERSISTENT_RESERVE_IN +#define PERSISTENT_RESERVE_IN 0x5e +#endif +#ifndef PERSISTENT_RESERVE_OUT +#define PERSISTENT_RESERVE_OUT 0x5f +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/scsi3.h linux.ac/drivers/message/fusion/scsi3.h --- linux.vanilla/drivers/message/fusion/scsi3.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/scsi3.h Tue Apr 3 18:48:14 2001 @@ -0,0 +1,700 @@ +/* + * linux/drivers/message/fusion/scsi3.h + * SCSI-3 definitions and macros. + * (Ultimately) SCSI-3 definitions; for now, inheriting + * SCSI-2 definitions. + * + * Copyright (c) 1996-2001 Steven J. Ralston + * Written By: Steven J. Ralston (19960517) + * (mailto:Steve.Ralston@lsil.com) + * + * $Id: scsi3.h,v 1.4 2001/01/06 15:54:25 sralston Exp $ + */ + +#ifndef SCSI3_H_INCLUDED +#define SCSI3_H_INCLUDED +/***************************************************************************/ + +/**************************************************************************** + * + * Includes + */ +#ifdef __KERNEL__ +#include +#else + #ifndef U_STUFF_DEFINED + #define U_STUFF_DEFINED + typedef unsigned char u8; + typedef unsigned short u16; + typedef unsigned int u32; + #endif +#endif + +/**************************************************************************** + * + * Defines + */ + +/* + * SCSI Commands + */ +#define CMD_TestUnitReady 0x00 +#define CMD_RezeroUnit 0x01 /* direct-access devices */ +#define CMD_Rewind 0x01 /* sequential-access devices */ +#define CMD_RequestSense 0x03 +#define CMD_FormatUnit 0x04 +#define CMD_ReassignBlock 0x07 +#define CMD_Read6 0x08 +#define CMD_Write6 0x0A +#define CMD_WriteFilemark 0x10 +#define CMD_Space 0x11 +#define CMD_Inquiry 0x12 +#define CMD_ModeSelect6 0x15 +#define CMD_ModeSense6 0x1A +#define CMD_Reserve6 0x16 +#define CMD_Release6 0x17 +#define CMD_Erase 0x19 +#define CMD_StartStopUnit 0x1b /* direct-access devices */ +#define CMD_LoadUnload 0x1b /* sequential-access devices */ +#define CMD_ReceiveDiagnostic 0x1C +#define CMD_SendDiagnostic 0x1D +#define CMD_ReadCapacity 0x25 +#define CMD_Read10 0x28 +#define CMD_Write10 0x2A +#define CMD_WriteVerify 0x2E +#define CMD_Verify 0x2F +#define CMD_ReadDefectData 0x37 +#define CMD_LogSelect 0x4C +#define CMD_LogSense 0x4D +#define CMD_ModeSelect10 0x55 +#define CMD_Reserve10 0x56 +#define CMD_Release10 0x57 +#define CMD_ModeSense10 0x5A +#define CMD_ReportLuns 0xA0 + +/* + * Control byte field + */ +#define CONTROL_BYTE_NACA_BIT 0x04 +#define CONTROL_BYTE_Flag_BIT 0x02 +#define CONTROL_BYTE_Link_BIT 0x01 + +/* + * SCSI Messages + */ +#define MSG_COMPLETE 0x00 +#define MSG_EXTENDED 0x01 +#define MSG_SAVE_POINTERS 0x02 +#define MSG_RESTORE_POINTERS 0x03 +#define MSG_DISCONNECT 0x04 +#define MSG_IDERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT 0x07 +#define MSG_NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define MSG_LINKED_CMD_COMPLETE 0x0a +#define MSG_LCMD_COMPLETE_W_FLG 0x0b +#define MSG_BUS_DEVICE_RESET 0x0c +#define MSG_ABORT_TAG 0x0d +#define MSG_CLEAR_QUEUE 0x0e +#define MSG_INITIATE_RECOVERY 0x0f + +#define MSG_RELEASE_RECOVRY 0x10 +#define MSG_TERMINATE_IO 0x11 + +#define MSG_SIMPLE_QUEUE 0x20 +#define MSG_HEAD_OF_QUEUE 0x21 +#define MSG_ORDERED_QUEUE 0x22 +#define MSG_IGNORE_WIDE_RESIDUE 0x23 + +#define MSG_IDENTIFY 0x80 +#define MSG_IDENTIFY_W_DISC 0xc0 + +/* + * SCSI Phases + */ +#define PHS_DATA_OUT 0x00 +#define PHS_DATA_IN 0x01 +#define PHS_COMMAND 0x02 +#define PHS_STATUS 0x03 +#define PHS_MSG_OUT 0x06 +#define PHS_MSG_IN 0x07 + +/* + * Statuses + */ +#define STS_GOOD 0x00 +#define STS_CHECK_CONDITION 0x02 +#define STS_CONDITION_MET 0x04 +#define STS_BUSY 0x08 +#define STS_INTERMEDIATE 0x10 +#define STS_INTERMEDIATE_CONDITION_MET 0x14 +#define STS_RESERVATION_CONFLICT 0x18 +#define STS_COMMAND_TERMINATED 0x22 +#define STS_TASK_SET_FULL 0x28 +#define STS_QUEUE_FULL 0x28 +#define STS_ACA_ACTIVE 0x30 + +#define STS_VALID_MASK 0x3e + +#define SCSI_STATUS(x) ((x) & STS_VALID_MASK) + +/* + * SCSI QTag Types + */ +#define QTAG_SIMPLE 0x20 +#define QTAG_HEAD_OF_Q 0x21 +#define QTAG_ORDERED 0x22 + +/* + * SCSI Sense Key Definitons + */ +#define SK_NO_SENSE 0x00 +#define SK_RECOVERED_ERROR 0x01 +#define SK_NOT_READY 0x02 +#define SK_MEDIUM_ERROR 0x03 +#define SK_HARDWARE_ERROR 0x04 +#define SK_ILLEGAL_REQUEST 0x05 +#define SK_UNIT_ATTENTION 0x06 +#define SK_DATA_PROTECT 0x07 +#define SK_BLANK_CHECK 0x08 +#define SK_VENDOR_SPECIFIC 0x09 +#define SK_COPY_ABORTED 0x0a +#define SK_ABORTED_COMMAND 0x0b +#define SK_EQUAL 0x0c +#define SK_VOLUME_OVERFLOW 0x0d +#define SK_MISCOMPARE 0x0e +#define SK_RESERVED 0x0f + + + +#define SCSI_MAX_INQUIRY_BYTES 96 +#define SCSI_STD_INQUIRY_BYTES 36 + +#undef USE_SCSI_COMPLETE_INQDATA +/* + * Structure definition for SCSI Inquiry Data + * + * NOTE: The following structure is 96 bytes in size + * iff USE_SCSI_COMPLETE_INQDATA IS defined above (i.e. w/ "#define"). + * If USE_SCSI_COMPLETE_INQDATA is NOT defined above (i.e. w/ "#undef") + * then the following structure is only 36 bytes in size. + * THE CHOICE IS YOURS! + */ +typedef struct SCSI_Inquiry_Data +{ +#ifdef USE_SCSI_COMPLETE_INQDATA + u8 InqByte[SCSI_MAX_INQUIRY_BYTES]; +#else + u8 InqByte[SCSI_STD_INQUIRY_BYTES]; +#endif + +/* + * the following structure works only for little-endian (Intel, + * LSB first (1234) byte order) systems with 4-byte ints. + * + u32 Periph_Device_Type : 5, + Periph_Qualifier : 3, + Device_Type_Modifier : 7, + Removable_Media : 1, + ANSI_Version : 3, + ECMA_Version : 3, + ISO_Version : 2, + Response_Data_Format : 4, + reserved_0 : 3, + AERC : 1 ; + u32 Additional_Length : 8, + reserved_1 :16, + SftReset : 1, + CmdQue : 1, + reserved_2 : 1, + Linked : 1, + Sync : 1, + WBus16 : 1, + WBus32 : 1, + RelAdr : 1 ; + u8 Vendor_ID[8]; + u8 Product_ID[16]; + u8 Revision_Level [4]; +#ifdef USE_SCSI_COMPLETE_INQDATA + u8 Vendor_Specific[20]; + u8 reserved_3[40]; +#endif + * + */ + +} SCSI_Inquiry_Data_t; + +#define INQ_PERIPHINFO_BYTE 0 +#define INQ_Periph_Qualifier_MASK 0xe0 +#define INQ_Periph_Device_Type_MASK 0x1f + +#define INQ_Peripheral_Qualifier(inqp) \ + (int)((*((u8*)(inqp)+INQ_PERIPHINFO_BYTE) & INQ_Periph_Qualifier_MASK) >> 5) +#define INQ_Peripheral_Device_Type(inqp) \ + (int)(*((u8*)(inqp)+INQ_PERIPHINFO_BYTE) & INQ_Periph_Device_Type_MASK) + + +#define INQ_DEVTYPEMOD_BYTE 1 +#define INQ_RMB_BIT 0x80 +#define INQ_Device_Type_Modifier_MASK 0x7f + +#define INQ_Removable_Medium(inqp) \ + (int)(*((u8*)(inqp)+INQ_DEVTYPEMOD_BYTE) & INQ_RMB_BIT) +#define INQ_Device_Type_Modifier(inqp) \ + (int)(*((u8*)(inqp)+INQ_DEVTYPEMOD_BYTE) & INQ_Device_Type_Modifier_MASK) + + +#define INQ_VERSIONINFO_BYTE 2 +#define INQ_ISO_Version_MASK 0xc0 +#define INQ_ECMA_Version_MASK 0x38 +#define INQ_ANSI_Version_MASK 0x07 + +#define INQ_ISO_Version(inqp) \ + (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ISO_Version_MASK) +#define INQ_ECMA_Version(inqp) \ + (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ECMA_Version_MASK) +#define INQ_ANSI_Version(inqp) \ + (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ANSI_Version_MASK) + + +#define INQ_BYTE3 3 +#define INQ_AERC_BIT 0x80 +#define INQ_TrmTsk_BIT 0x40 +#define INQ_NormACA_BIT 0x20 +#define INQ_RDF_MASK 0x0F + +#define INQ_AER_Capable(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_AERC_BIT) +#define INQ_TrmTsk(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_TrmTsk_BIT) +#define INQ_NormACA(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_NormACA_BIT) +#define INQ_Response_Data_Format(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_RDF_MASK) + + +#define INQ_CAPABILITY_BYTE 7 +#define INQ_RelAdr_BIT 0x80 +#define INQ_WBus32_BIT 0x40 +#define INQ_WBus16_BIT 0x20 +#define INQ_Sync_BIT 0x10 +#define INQ_Linked_BIT 0x08 + /* INQ_Reserved BIT 0x40 */ +#define INQ_CmdQue_BIT 0x02 +#define INQ_SftRe_BIT 0x01 + +#define IS_RelAdr_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_RelAdr_BIT) +#define IS_WBus32_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_WBus32_BIT) +#define IS_WBus16_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_WBus16_BIT) +#define IS_Sync_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Sync_BIT) +#define IS_Linked_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Linked_BIT) +#define IS_CmdQue_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_CmdQue_BIT) +#define IS_SftRe_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_SftRe_BIT) + +#define INQ_Width_BITS \ + (INQ_WBus32_BIT | INQ_WBus16_BIT) +#define IS_Wide_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Width_BITS) + + +/* + * SCSI peripheral device types + */ +#define SCSI_TYPE_DAD 0x00 /* Direct Access Device */ +#define SCSI_TYPE_SAD 0x01 /* Sequential Access Device */ +#define SCSI_TYPE_TAPE SCSI_TYPE_SAD +#define SCSI_TYPE_PRT 0x02 /* Printer */ +#define SCSI_TYPE_PROC 0x03 /* Processor */ +#define SCSI_TYPE_WORM 0x04 +#define SCSI_TYPE_CDROM 0x05 +#define SCSI_TYPE_SCAN 0x06 /* Scanner */ +#define SCSI_TYPE_OPTICAL 0x07 /* Magneto/Optical */ +#define SCSI_TYPE_CHANGER 0x08 +#define SCSI_TYPE_COMM 0x09 /* Communications device */ +#define SCSI_TYPE_UNKNOWN 0x1f +#define SCSI_TYPE_UNCONFIGURED_LUN 0x7f + +#define SCSI_TYPE_MAX_KNOWN SCSI_TYPE_COMM + +/* + * Peripheral Qualifiers + */ +#define DEVICE_PRESENT 0x00 +#define LUN_NOT_PRESENT 0x01 +#define LUN_NOT_SUPPORTED 0x03 + +/* + * ANSI Versions + */ +#ifndef SCSI_1 +#define SCSI_1 0x01 +#endif +#ifndef SCSI_2 +#define SCSI_2 0x02 +#endif +#ifndef SCSI_3 +#define SCSI_3 0x03 +#endif + + +#define SCSI_MAX_SENSE_BYTES 255 +#define SCSI_STD_SENSE_BYTES 18 +#define SCSI_PAD_SENSE_BYTES (SCSI_MAX_SENSE_BYTES - SCSI_STD_SENSE_BYTES) + +#undef USE_SCSI_COMPLETE_SENSE +/* + * Structure definition for SCSI Sense Data + * + * NOTE: The following structure is 255 bytes in size + * iiff USE_SCSI_COMPLETE_SENSE IS defined above (i.e. w/ "#define"). + * If USE_SCSI_COMPLETE_SENSE is NOT defined above (i.e. w/ "#undef") + * then the following structure is only 19 bytes in size. + * THE CHOICE IS YOURS! + * + */ +typedef struct SCSI_Sense_Data +{ +#ifdef USE_SCSI_COMPLETE_SENSE + u8 SenseByte[SCSI_MAX_SENSE_BYTES]; +#else + u8 SenseByte[SCSI_STD_SENSE_BYTES]; +#endif + +/* + * the following structure works only for little-endian (Intel, + * LSB first (1234) byte order) systems with 4-byte ints. + * + u8 Error_Code :4, // 0x00 + Error_Class :3, + Valid :1 + ; + u8 Segment_Number // 0x01 + ; + u8 Sense_Key :4, // 0x02 + Reserved :1, + Incorrect_Length_Indicator:1, + End_Of_Media :1, + Filemark :1 + ; + u8 Information_MSB; // 0x03 + u8 Information_Byte2; // 0x04 + u8 Information_Byte1; // 0x05 + u8 Information_LSB; // 0x06 + u8 Additional_Length; // 0x07 + + u32 Command_Specific_Information; // 0x08 - 0x0b + + u8 Additional_Sense_Code; // 0x0c + u8 Additional_Sense_Code_Qualifier; // 0x0d + u8 Field_Replaceable_Unit_Code; // 0x0e + u8 Illegal_Req_Bit_Pointer :3, // 0x0f + Illegal_Req_Bit_Valid :1, + Illegal_Req_Reserved :2, + Illegal_Req_Cmd_Data :1, + Sense_Key_Specific_Valid :1 + ; + u16 Sense_Key_Specific_Data; // 0x10 - 0x11 + +#ifdef USE_SCSI_COMPLETE_SENSE + u8 Additional_Sense_Data[SCSI_PAD_SENSE_BYTES]; +#else + u8 Additional_Sense_Data[1]; +#endif + * + */ + +} SCSI_Sense_Data_t; + + +#define SD_ERRCODE_BYTE 0 +#define SD_Valid_BIT 0x80 +#define SD_Error_Code_MASK 0x7f +#define SD_Valid(sdp) \ + (int)(*((u8*)(sdp)+SD_ERRCODE_BYTE) & SD_Valid_BIT) +#define SD_Error_Code(sdp) \ + (int)(*((u8*)(sdp)+SD_ERRCODE_BYTE) & SD_Error_Code_MASK) + + +#define SD_SEGNUM_BYTE 1 +#define SD_Segment_Number(sdp) (int)(*((u8*)(sdp)+SD_SEGNUM_BYTE)) + + +#define SD_SENSEKEY_BYTE 2 +#define SD_Filemark_BIT 0x80 +#define SD_EOM_BIT 0x40 +#define SD_ILI_BIT 0x20 +#define SD_Sense_Key_MASK 0x0f +#define SD_Filemark(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_Filemark_BIT) +#define SD_EOM(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_EOM_BIT) +#define SD_ILI(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_ILI_BIT) +#define SD_Sense_Key(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_Sense_Key_MASK) + + +#define SD_INFO3_BYTE 3 +#define SD_INFO2_BYTE 4 +#define SD_INFO1_BYTE 5 +#define SD_INFO0_BYTE 6 +#define SD_Information3(sdp) (int)(*((u8*)(sdp)+SD_INFO3_BYTE)) +#define SD_Information2(sdp) (int)(*((u8*)(sdp)+SD_INFO2_BYTE)) +#define SD_Information1(sdp) (int)(*((u8*)(sdp)+SD_INFO1_BYTE)) +#define SD_Information0(sdp) (int)(*((u8*)(sdp)+SD_INFO0_BYTE)) + + +#define SD_ADDL_LEN_BYTE 7 +#define SD_Additional_Sense_Length(sdp) \ + (int)(*((u8*)(sdp)+SD_ADDL_LEN_BYTE)) +#define SD_Addl_Sense_Len SD_Additional_Sense_Length + + +#define SD_CMD_SPECIFIC3_BYTE 8 +#define SD_CMD_SPECIFIC2_BYTE 9 +#define SD_CMD_SPECIFIC1_BYTE 10 +#define SD_CMD_SPECIFIC0_BYTE 11 +#define SD_Cmd_Specific_Info3(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC3_BYTE)) +#define SD_Cmd_Specific_Info2(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC2_BYTE)) +#define SD_Cmd_Specific_Info1(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC1_BYTE)) +#define SD_Cmd_Specific_Info0(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC0_BYTE)) + + +#define SD_ADDL_SENSE_CODE_BYTE 12 +#define SD_Additional_Sense_Code(sdp) \ + (int)(*((u8*)(sdp)+SD_ADDL_SENSE_CODE_BYTE)) +#define SD_Addl_Sense_Code SD_Additional_Sense_Code +#define SD_ASC SD_Additional_Sense_Code + + +#define SD_ADDL_SENSE_CODE_QUAL_BYTE 13 +#define SD_Additional_Sense_Code_Qualifier(sdp) \ + (int)(*((u8*)(sdp)+SD_ADDL_SENSE_CODE_QUAL_BYTE)) +#define SD_Addl_Sense_Code_Qual SD_Additional_Sense_Code_Qualifier +#define SD_ASCQ SD_Additional_Sense_Code_Qualifier + + +#define SD_FIELD_REPL_UNIT_CODE_BYTE 14 +#define SD_Field_Replaceable_Unit_Code(sdp) \ + (int)(*((u8*)(sdp)+SD_FIELD_REPL_UNIT_CODE_BYTE)) +#define SD_Field_Repl_Unit_Code SD_Field_Replaceable_Unit_Code +#define SD_FRUC SD_Field_Replaceable_Unit_Code +#define SD_FRU SD_Field_Replaceable_Unit_Code + + +/* + * Sense-Key Specific offsets and macros. + */ +#define SD_SKS2_BYTE 15 +#define SD_SKS_Valid_BIT 0x80 +#define SD_SKS_Cmd_Data_BIT 0x40 +#define SD_SKS_Bit_Ptr_Valid_BIT 0x08 +#define SD_SKS_Bit_Ptr_MASK 0x07 +#define SD_SKS1_BYTE 16 +#define SD_SKS0_BYTE 17 +#define SD_Sense_Key_Specific_Valid(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Valid_BIT) +#define SD_SKS_Valid SD_Sense_Key_Specific_Valid +#define SD_SKS_CDB_Error(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Cmd_Data_BIT) +#define SD_Was_Illegal_Request SD_SKS_CDB_Error +#define SD_SKS_Bit_Pointer_Valid(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Bit_Ptr_Valid_BIT) +#define SD_SKS_Bit_Pointer(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Bit_Ptr_MASK) +#define SD_Field_Pointer(sdp) \ + (int)( ((u16)(*((u8*)(sdp)+SD_SKS1_BYTE)) << 8) \ + + *((u8*)(sdp)+SD_SKS0_BYTE) ) +#define SD_Bad_Byte SD_Field_Pointer +#define SD_Actual_Retry_Count SD_Field_Pointer +#define SD_Progress_Indication SD_Field_Pointer + +/* + * Mode Sense Write Protect Mask + */ +#define WRITE_PROTECT_MASK 0X80 + +/* + * Medium Type Codes + */ +#define OPTICAL_DEFAULT 0x00 +#define OPTICAL_READ_ONLY_MEDIUM 0x01 +#define OPTICAL_WRITE_ONCE_MEDIUM 0x02 +#define OPTICAL_READ_WRITABLE_MEDIUM 0x03 +#define OPTICAL_RO_OR_WO_MEDIUM 0x04 +#define OPTICAL_RO_OR_RW_MEDIUM 0x05 +#define OPTICAL_WO_OR_RW_MEDIUM 0x06 + + + +/* + * Structure definition for READ6, WRITE6 (6-byte CDB) + */ +typedef struct SCSI_RW6_CDB +{ + u32 OpCode :8, + LBA_HI :5, /* 5 MSBit's of the LBA */ + Lun :3, + LBA_MID :8, /* NOTE: total of 21 bits in LBA */ + LBA_LO :8 ; /* Max LBA = 0x001fffff */ + u8 BlockCount; + u8 Control; +} SCSI_RW6_t; + +#define MAX_RW6_LBA ((u32)0x001fffff) + +/* + * Structure definition for READ10, WRITE10 (10-byte CDB) + * + * NOTE: ParityCheck bit is applicable only for VERIFY and WRITE VERIFY for + * the ADP-92 DAC only. In the SCSI2 spec. this same bit is defined as a + * FUA (forced unit access) bit for READs and WRITEs. Since this driver + * does not use the FUA, this bit is defined as it is used by the ADP-92. + * Also, for READ CAPACITY, only the OpCode field is used. + */ +typedef struct SCSI_RW10_CDB +{ + u8 OpCode; + u8 Reserved1; + u32 LBA; + u8 Reserved2; + u16 BlockCount; + u8 Control; +} SCSI_RW10_t; + +#define PARITY_CHECK 0x08 /* parity check bit - byte[1], bit 3 */ + + /* + * Structure definition for data returned by READ CAPACITY cmd; + * READ CAPACITY data + */ + typedef struct READ_CAP_DATA + { + u32 MaxLBA; + u32 BlockBytes; + } SCSI_READ_CAP_DATA_t, *pSCSI_READ_CAP_DATA_t; + + +/* + * Structure definition for FORMAT UNIT CDB (6-byte CDB) + */ +typedef struct _SCSI_FORMAT_UNIT +{ + u8 OpCode; + u8 Reserved1; + u8 VendorSpecific; + u16 Interleave; + u8 Control; +} SCSI_FORMAT_UNIT_t; + +/* + * Structure definition for REQUEST SENSE (6-byte CDB) + */ +typedef struct _SCSI_REQUEST_SENSE +{ + u8 OpCode; + u8 Reserved1; + u8 Reserved2; + u8 Reserved3; + u8 AllocLength; + u8 Control; +} SCSI_REQ_SENSE_t; + +/* + * Structure definition for REPORT LUNS (12-byte CDB) + */ +typedef struct _SCSI_REPORT_LUNS +{ + u8 OpCode; + u8 Reserved1[5]; + u32 AllocationLength; + u8 Reserved2; + u8 Control; +} SCSI_REPORT_LUNS_t, *pSCSI_REPORT_LUNS_t; + + /* + * (per-level) LUN information bytes + */ +/* + * Following doesn't work on ARMCC compiler + * [apparently] because it pads every struct + * to be multiple of 4 bytes! + * So SCSI_LUN_LEVELS_t winds up being 16 + * bytes instead of 8! + * + typedef struct LUN_INFO + { + u8 AddrMethod_plus_LunOrBusNumber; + u8 LunOrTarget; + } SCSI_LUN_INFO_t, *pSCSI_LUN_INFO_t; + + typedef struct LUN_LEVELS + { + SCSI_LUN_INFO_t LUN_0; + SCSI_LUN_INFO_t LUN_1; + SCSI_LUN_INFO_t LUN_2; + SCSI_LUN_INFO_t LUN_3; + } SCSI_LUN_LEVELS_t, *pSCSI_LUN_LEVELS_t; +*/ + /* + * All 4 levels (8 bytes) of LUN information + */ + typedef struct LUN_LEVELS + { + u8 LVL1_AddrMethod_plus_LunOrBusNumber; + u8 LVL1_LunOrTarget; + u8 LVL2_AddrMethod_plus_LunOrBusNumber; + u8 LVL2_LunOrTarget; + u8 LVL3_AddrMethod_plus_LunOrBusNumber; + u8 LVL3_LunOrTarget; + u8 LVL4_AddrMethod_plus_LunOrBusNumber; + u8 LVL4_LunOrTarget; + } SCSI_LUN_LEVELS_t, *pSCSI_LUN_LEVELS_t; + + /* + * Structure definition for data returned by REPORT LUNS cmd; + * LUN reporting parameter list format + */ + typedef struct LUN_REPORT + { + u32 LunListLength; + u32 Reserved; + SCSI_LUN_LEVELS_t LunInfo[1]; + } SCSI_LUN_REPORT_t, *pSCSI_LUN_REPORT_t; + +/**************************************************************************** + * + * Externals + */ + +/**************************************************************************** + * + * Public Typedefs & Related Defines + */ + +/**************************************************************************** + * + * Macros (embedded, above) + */ + +/**************************************************************************** + * + * Public Variables + */ + +/**************************************************************************** + * + * Public Prototypes (module entry points) + */ + + +/***************************************************************************/ +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/scsiops.c linux.ac/drivers/message/fusion/scsiops.c --- linux.vanilla/drivers/message/fusion/scsiops.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/scsiops.c Tue Apr 3 17:54:47 2001 @@ -0,0 +1,309 @@ + +static const char *ScsiOpcodeString[256] = { + "TEST UNIT READY\0\01", /* 00h */ + "REWIND\0\002" + "\001REZERO UNIT", /* 01h */ + "\0\0", /* 02h */ + "REQUEST SENSE\0\01", /* 03h */ + "FORMAT UNIT\0\03" + "\001FORMAT MEDIUM\0" + "\002FORMAT", /* 04h */ + "READ BLOCK LIMITS\0\1", /* 05h */ + "\0\0", /* 06h */ + "REASSIGN BLOCKS\0\02" + "\010INITIALIZE ELEMENT STATUS", /* 07h */ + "READ(06)\0\04" + "\001READ\0" + "\003RECEIVE\0" + "\011GET MESSAGE(06)", /* 08h */ + "\0\0", /* 09h */ + "WRITE(06)\0\05" + "\001WRITE\0" + "\002PRINT\0" + "\003SEND(6)\0" + "\011SEND MESSAGE(06)", /* 0Ah */ + "SEEK(06)\0\02" + "\003SLEW AND PRINT", /* 0Bh */ + "\0\0", /* 0Ch */ + "\0\0", /* 0Dh */ + "\0\0", /* 0Eh */ + "READ REVERSE\0\01", /* 0Fh */ + "WRITE FILEMARKS\0\02" + "\003SYNCRONIZE BUFFER", /* 10h */ + "SPACE(6)\0\01", /* 11h */ + "INQUIRY\0\01", /* 12h */ + "VERIFY\0\01", /* 13h */ + "RECOVER BUFFERED DATA\0\01", /* 14h */ + "MODE SELECT(06)\0\01", /* 15h */ + "RESERVE(06)\0\02" + "\010RESERVE ELEMENT(06)", /* 16h */ + "RELEASE(06)\0\02" + "\010RELEASE ELEMENT(06)", /* 17h */ + "COPY\0\01", /* 18h */ + "ERASE\0\01", /* 19h */ + "MODE SENSE(06)\0\01", /* 1Ah */ + "STOP START UNIT\0\04" + "\001LOAD UNLOAD\0" + "\002STOP PRINT\0" + "\006SCAN\0\002", /* 1Bh */ + "RECEIVE DIAGNOSTIC RESULTS\0\01", /* 1Ch */ + "SEND DIAGNOSTIC\0\01", /* 1Dh */ + "PREVENT ALLOW MEDIUM REMOVAL\0\01", /* 1Eh */ + "\0\0", /* 1Fh */ + "\0\0", /* 20h */ + "\0\0", /* 21h */ + "\0\0", /* 22h */ + "READ FORMAT CAPACITIES\0\01", /* 23h */ + "SET WINDOW\0\01", /* 24h */ + "READ CAPACITY\0\03" + "\006GET WINDOW\0" + "\037FREAD CARD CAPACITY", /* 25h */ + "\0\0", /* 26h */ + "\0\0", /* 27h */ + "READ(10)\0\02" + "\011GET MESSAGE(10)", /* 28h */ + "READ GENERATION\0\01", /* 29h */ + "WRITE(10)\0\03" + "\011SEND(10)\0" + "\011SEND MESSAGE(10)", /* 2Ah */ + "SEEK(10)\0\03" + "LOCATE(10)\0" + "POSITION TO ELEMENT", /* 2Bh */ + "ERASE(10)\0\01", /* 2Ch */ + "READ UPDATED BLOCK\0\01", /* 2Dh */ + "WRITE AND VERIFY(10)\0\01", /* 2Eh */ + "VERIFY(10)\0\01", /* 2Fh */ + "SEARCH DATA HIGH(10)\0\01", /* 30h */ + "SEARCH DATA EQUAL(10)\0\02" + "OBJECT POSITION", /* 31h */ + "SEARCH DATA LOW(10)\0\01", /* 32h */ + "SET LIMITS(10)\0\01", /* 33h */ + "PRE-FETCH(10)\0\03" + "READ POSITION\0" + "GET DATA BUFFER STATUS", /* 34h */ + "SYNCHRONIZE CACHE(10)\0\01", /* 35h */ + "LOCK UNLOCK CACHE(10)\0\01", /* 36h */ + "READ DEFECT DATA(10)\0\01", /* 37h */ + "MEDIUM SCAN\0\01", /* 38h */ + "COMPARE\0\01", /* 39h */ + "COPY AND VERIFY\0\01", /* 3Ah */ + "WRITE BUFFER\0\01", /* 3Bh */ + "READ BUFFER\0\01", /* 3Ch */ + "UPDATE BLOCK\0\01", /* 3Dh */ + "READ LONG\0\01", /* 3Eh */ + "WRITE LONG\0\01", /* 3Fh */ + "CHANGE DEFINITION\0\01", /* 40h */ + "WRITE SAME(10)\0\01", /* 41h */ + "READ SUB-CHANNEL\0\01", /* 42h */ + "READ TOC/PMA/ATIP\0\01", /* 43h */ + "REPORT DENSITY SUPPORT\0\01", /* 44h */ + "READ HEADER\0\01", /* 44h */ + "PLAY AUDIO(10)\0\01", /* 45h */ + "GET CONFIGURATION\0\01", /* 46h */ + "PLAY AUDIO MSF\0\01", /* 47h */ + "PLAY AUDIO TRACK INDEX\0\01", /* 48h */ + "PLAY TRACK RELATIVE(10)\0\01", /* 49h */ + "GET EVENT STATUS NOTIFICATION\0\01", /* 4Ah */ + "PAUSE/RESUME\0\01", /* 4Bh */ + "LOG SELECT\0\01", /* 4Ch */ + "LOG SENSE\0\01", /* 4Dh */ + "STOP PLAY/SCAN\0\01", /* 4Eh */ + "\0\0", /* 4Fh */ + "XDWRITE(10)\0\01", /* 50h */ + "XPWRITE(10)\0\02" + "READ DISC INFORMATION", /* 51h */ + "XDREAD(10)\0\01" + "READ TRACK INFORMATION", /* 52h */ + "RESERVE TRACK\0\01", /* 53h */ + "SEND OPC INFORMATION\0\01", /* 54h */ + "MODE SELECT(10)\0\01", /* 55h */ + "RESERVE(10)\0\02" + "RESERVE ELEMENT(10)", /* 56h */ + "RELEASE(10)\0\02" + "RELEASE ELEMENT(10)", /* 57h */ + "REPAIR TRACK\0\01", /* 58h */ + "READ MASTER CUE\0\01", /* 59h */ + "MODE SENSE(10)\0\01", /* 5Ah */ + "CLOSE TRACK/SESSION\0\01", /* 5Bh */ + "READ BUFFER CAPACITY\0\01", /* 5Ch */ + "SEND CUE SHEET\0\01", /* 5Dh */ + "PERSISTENT RESERVE IN\0\01", /* 5Eh */ + "PERSISTENT RESERVE OUT\0\01", /* 5Fh */ + "\0\0", /* 60h */ + "\0\0", /* 61h */ + "\0\0", /* 62h */ + "\0\0", /* 63h */ + "\0\0", /* 64h */ + "\0\0", /* 65h */ + "\0\0", /* 66h */ + "\0\0", /* 67h */ + "\0\0", /* 68h */ + "\0\0", /* 69h */ + "\0\0", /* 6Ah */ + "\0\0", /* 6Bh */ + "\0\0", /* 6Ch */ + "\0\0", /* 6Dh */ + "\0\0", /* 6Eh */ + "\0\0", /* 6Fh */ + "\0\0", /* 70h */ + "\0\0", /* 71h */ + "\0\0", /* 72h */ + "\0\0", /* 73h */ + "\0\0", /* 74h */ + "\0\0", /* 75h */ + "\0\0", /* 76h */ + "\0\0", /* 77h */ + "\0\0", /* 78h */ + "\0\0", /* 79h */ + "\0\0", /* 7Ah */ + "\0\0", /* 7Bh */ + "\0\0", /* 7Ch */ + "\0\0", /* 7Eh */ + "\0\0", /* 7Eh */ + "\0\0", /* 7Fh */ + "XDWRITE EXTENDED(16)\0\01", /* 80h */ + "REBUILD(16)\0\01", /* 81h */ + "REGENERATE(16)\0\01", /* 82h */ + "EXTENDED COPY\0\01", /* 83h */ + "RECEIVE COPY RESULTS\0\01", /* 84h */ + "ACCESS CONTROL IN [proposed]\0\01", /* 86h */ + "ACCESS CONTROL OUT [proposed]\0\01", /* 87h */ + "READ(16)\0\01", /* 88h */ + "DEVICE LOCKS [proposed]\0\01", /* 89h */ + "WRITE(16)\0\01", /* 8Ah */ + "\0\0", /* 8Bh */ + "READ ATTRIBUTES [proposed]\0\01", /* 8Ch */ + "WRITE ATTRIBUTES [proposed]\0\01", /* 8Dh */ + "WRITE AND VERIFY(16)\0\01", /* 8Eh */ + "VERIFY(16)\0\01", /* 8Fh */ + "PRE-FETCH(16)\0\01", /* 90h */ + "SYNCHRONIZE CACHE(16)\0\02" + "SPACE(16) [1]", /* 91h */ + "LOCK UNLOCK CACHE(16)\0\02" + "LOCATE(16) [1]", /* 92h */ + "WRITE SAME(16)\0\01", /* 93h */ + "[usage proposed by SCSI Socket Services project]\0\01", /* 94h */ + "[usage proposed by SCSI Socket Services project]\0\01", /* 95h */ + "[usage proposed by SCSI Socket Services project]\0\01", /* 96h */ + "[usage proposed by SCSI Socket Services project]\0\01", /* 97h */ + "MARGIN CONTROL [proposed]\0\01", /* 98h */ + "\0\0", /* 99h */ + "\0\0", /* 9Ah */ + "\0\0", /* 9Bh */ + "\0\0", /* 9Ch */ + "\0\0", /* 9Dh */ + "SERVICE ACTION IN [proposed]\0\01", /* 9Eh */ + "SERVICE ACTION OUT [proposed]\0\01", /* 9Fh */ + "REPORT LUNS\0\01", /* A0h */ + "BLANK\0\01", /* A1h */ + "SEND EVENT\0\01", /* A2h */ + "MAINTENANCE (IN)\0\02" + "SEND KEY", /* A3h */ + "MAINTENANCE (OUT)\0\02" + "REPORT KEY", /* A4h */ + "MOVE MEDIUM\0\02" + "PLAY AUDIO(12)", /* A5h */ + "EXCHANGE MEDIUM\0\02" + "LOAD/UNLOAD C/DVD", /* A6h */ + "MOVE MEDIUM ATTACHED\0\02" + "SET READ AHEAD\0\01", /* A7h */ + "READ(12)\0\02" + "GET MESSAGE(12)", /* A8h */ + "PLAY TRACK RELATIVE(12)\0\01", /* A9h */ + "WRITE(12)\0\02" + "SEND MESSAGE(12)", /* AAh */ + "\0\0", /* ABh */ + "ERASE(12)\0\02" + "GET PERFORMANCE", /* ACh */ + "READ DVD STRUCTURE\0\01", /* ADh */ + "WRITE AND VERIFY(12)\0\01", /* AEh */ + "VERIFY(12)\0\01", /* AFh */ + "SEARCH DATA HIGH(12)\0\01", /* B0h */ + "SEARCH DATA EQUAL(12)\0\01", /* B1h */ + "SEARCH DATA LOW(12)\0\01", /* B2h */ + "SET LIMITS(12)\0\01", /* B3h */ + "READ ELEMENT STATUS ATTACHED\0\01", /* B4h */ + "REQUEST VOLUME ELEMENT ADDRESS\0\01", /* B5h */ + "SEND VOLUME TAG\0\02" + "SET STREAMING", /* B6h */ + "READ DEFECT DATA(12)\0\01", /* B7h */ + "READ ELEMENT STATUS\0\01", /* B8h */ + "READ CD MSF\0\01", /* B9h */ + "REDUNDANCY GROUP (IN)\0\02" + "SCAN", /* BAh */ + "REDUNDANCY GROUP (OUT)\0\02" + "SET CD-ROM SPEED", /* BBh */ + "SPARE (IN)\0\02" + "PLAY CD", /* BCh */ + "SPARE (OUT)\0\02" + "MECHANISM STATUS", /* BDh */ + "VOLUME SET (IN)\0\02" + "READ CD", /* BEh */ + "VOLUME SET (OUT)\0\0\02" + "SEND DVD STRUCTURE", /* BFh */ + "\0\0", /* C0h */ + "\0\0", /* C1h */ + "\0\0", /* C2h */ + "\0\0", /* C3h */ + "\0\0", /* C4h */ + "\0\0", /* C5h */ + "\0\0", /* C6h */ + "\0\0", /* C7h */ + "\0\0", /* C8h */ + "\0\0", /* C9h */ + "\0\0", /* CAh */ + "\0\0", /* CBh */ + "\0\0", /* CCh */ + "\0\0", /* CDh */ + "\0\0", /* CEh */ + "\0\0", /* CFh */ + "\0\0", /* D0h */ + "\0\0", /* D1h */ + "\0\0", /* D2h */ + "\0\0", /* D3h */ + "\0\0", /* D4h */ + "\0\0", /* D5h */ + "\0\0", /* D6h */ + "\0\0", /* D7h */ + "\0\0", /* D8h */ + "\0\0", /* D9h */ + "\0\0", /* DAh */ + "\0\0", /* DBh */ + "\0\0", /* DCh */ + "\0\0", /* DEh */ + "\0\0", /* DEh */ + "\0\0", /* DFh */ + "\0\0", /* E0h */ + "\0\0", /* E1h */ + "\0\0", /* E2h */ + "\0\0", /* E3h */ + "\0\0", /* E4h */ + "\0\0", /* E5h */ + "\0\0", /* E6h */ + "\0\0", /* E7h */ + "\0\0", /* E8h */ + "\0\0", /* E9h */ + "\0\0", /* EAh */ + "\0\0", /* EBh */ + "\0\0", /* ECh */ + "\0\0", /* EDh */ + "\0\0", /* EEh */ + "\0\0", /* EFh */ + "\0\0", /* F0h */ + "\0\0", /* F1h */ + "\0\0", /* F2h */ + "\0\0", /* F3h */ + "\0\0", /* F4h */ + "\0\0", /* F5h */ + "\0\0", /* F6h */ + "\0\0", /* F7h */ + "\0\0", /* F8h */ + "\0\0", /* F9h */ + "\0\0", /* FAh */ + "\0\0", /* FBh */ + "\0\0", /* FEh */ + "\0\0", /* FEh */ + "\0\0", /* FEh */ + "\0\0" /* FFh */ +}; + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/t10.org/asc-num.txt linux.ac/drivers/message/fusion/t10.org/asc-num.txt --- linux.vanilla/drivers/message/fusion/t10.org/asc-num.txt Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/t10.org/asc-num.txt Tue Apr 3 17:54:47 2001 @@ -0,0 +1,505 @@ +File: ASC-NUM.TXT + +SCSI ASC/ASCQ Assignments +Numeric Sorted Listing +as of 5/18/00 + + D - DIRECT ACCESS DEVICE (SBC-2) device column key + .T - SEQUENTIAL ACCESS DEVICE (SSC) ------------------- + . L - PRINTER DEVICE (SSC) blank = reserved + . P - PROCESSOR DEVICE (SPC) not blank = allowed + . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC-2) + . . R - CD DEVICE (MMC) + . . S - SCANNER DEVICE (SCSI-2) + . . .O - OPTICAL MEMORY DEVICE (SBC-2) + . . . M - MEDIA CHANGER DEVICE (SMC) + . . . C - COMMUNICATION DEVICE (SCSI-2) + . . . .A - STORAGE ARRAY DEVICE (SCC) + . . . . E - ENCLOSURE SERVICES DEVICE (SES) + . . . . B - SIMPLIFIED DIRECT-ACCESS DEVICE (RBC) + . . . . .K - OPTICAL CARD READER/WRITER DEVICE (OCRW) +ASC/ASCQ DTLPWRSOMCAEBK Description +------- -------------- ---------------------------------------------------- +00h/00h DTLPWRSOMCAEBK NO ADDITIONAL SENSE INFORMATION +00h/01h T FILEMARK DETECTED +00h/02h T S END-OF-PARTITION/MEDIUM DETECTED +00h/03h T SETMARK DETECTED +00h/04h T S BEGINNING-OF-PARTITION/MEDIUM DETECTED +00h/05h TL S END-OF-DATA DETECTED +00h/06h DTLPWRSOMCAEBK I/O PROCESS TERMINATED +00h/11h R AUDIO PLAY OPERATION IN PROGRESS +00h/12h R AUDIO PLAY OPERATION PAUSED +00h/13h R AUDIO PLAY OPERATION SUCCESSFULLY COMPLETED +00h/14h R AUDIO PLAY OPERATION STOPPED DUE TO ERROR +00h/15h R NO CURRENT AUDIO STATUS TO RETURN +00h/16h DTLPWRSOMCAEBK OPERATION IN PROGRESS +00h/17h DTL WRSOM AEBK CLEANING REQUESTED +01h/00h D W O BK NO INDEX/SECTOR SIGNAL +02h/00h D WR OM BK NO SEEK COMPLETE +03h/00h DTL W SO BK PERIPHERAL DEVICE WRITE FAULT +03h/01h T NO WRITE CURRENT +03h/02h T EXCESSIVE WRITE ERRORS +04h/00h DTLPWRSOMCAEBK LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE +04h/01h DTLPWRSOMCAEBK LOGICAL UNIT IS IN PROCESS OF BECOMING READY +04h/02h DTLPWRSOMCAEBK LOGICAL UNIT NOT READY, INITIALIZING CMD. REQUIRED +04h/03h DTLPWRSOMCAEBK LOGICAL UNIT NOT READY, MANUAL INTERVENTION REQUIRED +04h/04h DTL R O B LOGICAL UNIT NOT READY, FORMAT IN PROGRESS +04h/05h DT W OMCA BK LOGICAL UNIT NOT READY, REBUILD IN PROGRESS +04h/06h DT W OMCA BK LOGICAL UNIT NOT READY, RECALCULATION IN PROGRESS +04h/07h DTLPWRSOMCAEBK LOGICAL UNIT NOT READY, OPERATION IN PROGRESS +04h/08h R LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS +04h/09h DTLPWRSOMCAEBK LOGICAL UNIT NOT READY, SELF-TEST IN PROGRESS +04h/10h auxiliary memory code 2 (99-148) [proposed] +05h/00h DTL WRSOMCAEBK LOGICAL UNIT DOES NOT RESPOND TO SELECTION +06h/00h D WR OM BK NO REFERENCE POSITION FOUND +07h/00h DTL WRSOM BK MULTIPLE PERIPHERAL DEVICES SELECTED +08h/00h DTL WRSOMCAEBK LOGICAL UNIT COMMUNICATION FAILURE +08h/01h DTL WRSOMCAEBK LOGICAL UNIT COMMUNICATION TIME-OUT +08h/02h DTL WRSOMCAEBK LOGICAL UNIT COMMUNICATION PARITY ERROR +08h/03h DT R OM BK LOGICAL UNIT COMMUNICATION CRC ERROR (ULTRA-DMA/32) +08h/04h DTLPWRSO C K UNREACHABLE COPY TARGET +09h/00h DT WR O B TRACK FOLLOWING ERROR +09h/01h WR O K TRACKING SERVO FAILURE +09h/02h WR O K FOCUS SERVO FAILURE +09h/03h WR O SPINDLE SERVO FAILURE +09h/04h DT WR O B HEAD SELECT FAULT +0Ah/00h DTLPWRSOMCAEBK ERROR LOG OVERFLOW +0Bh/00h DTLPWRSOMCAEBK WARNING +0Bh/01h DTLPWRSOMCAEBK WARNING - SPECIFIED TEMPERATURE EXCEEDED +0Bh/02h DTLPWRSOMCAEBK WARNING - ENCLOSURE DEGRADED +0Ch/00h T RS WRITE ERROR +0Ch/01h K WRITE ERROR - RECOVERED WITH AUTO REALLOCATION +0Ch/02h D W O BK WRITE ERROR - AUTO REALLOCATION FAILED +0Ch/03h D W O BK WRITE ERROR - RECOMMEND REASSIGNMENT +0Ch/04h DT W O B COMPRESSION CHECK MISCOMPARE ERROR +0Ch/05h DT W O B DATA EXPANSION OCCURRED DURING COMPRESSION +0Ch/06h DT W O B BLOCK NOT COMPRESSIBLE +0Ch/07h R WRITE ERROR - RECOVERY NEEDED +0Ch/08h R WRITE ERROR - RECOVERY FAILED +0Ch/09h R WRITE ERROR - LOSS OF STREAMING +0Ch/0Ah R WRITE ERROR - PADDING BLOCKS ADDED +0Ch/0Bh auxiliary memory code 4 (99-148) [proposed] +0Dh/00h +0Eh/00h +0Fh/00h +10h/00h D W O BK ID CRC OR ECC ERROR +11h/00h DT WRSO BK UNRECOVERED READ ERROR +11h/01h DT WRSO BK READ RETRIES EXHAUSTED +11h/02h DT WRSO BK ERROR TOO LONG TO CORRECT +11h/03h DT W SO BK MULTIPLE READ ERRORS +11h/04h D W O BK UNRECOVERED READ ERROR - AUTO REALLOCATE FAILED +11h/05h WR O B L-EC UNCORRECTABLE ERROR +11h/06h WR O B CIRC UNRECOVERED ERROR +11h/07h W O B DATA RE-SYNCHRONIZATION ERROR +11h/08h T INCOMPLETE BLOCK READ +11h/09h T NO GAP FOUND +11h/0Ah DT O BK MISCORRECTED ERROR +11h/0Bh D W O BK UNRECOVERED READ ERROR - RECOMMEND REASSIGNMENT +11h/0Ch D W O BK UNRECOVERED READ ERROR - RECOMMEND REWRITE THE DATA +11h/0Dh DT WR O B DE-COMPRESSION CRC ERROR +11h/0Eh DT WR O B CANNOT DECOMPRESS USING DECLARED ALGORITHM +11h/0Fh R ERROR READING UPC/EAN NUMBER +11h/10h R ERROR READING ISRC NUMBER +11h/11h R READ ERROR - LOSS OF STREAMING +11h/12h auxiliary memory code 3 (99-148) [proposed] +12h/00h D W O BK ADDRESS MARK NOT FOUND FOR ID FIELD +13h/00h D W O BK ADDRESS MARK NOT FOUND FOR DATA FIELD +14h/00h DTL WRSO BK RECORDED ENTITY NOT FOUND +14h/01h DT WR O BK RECORD NOT FOUND +14h/02h T FILEMARK OR SETMARK NOT FOUND +14h/03h T END-OF-DATA NOT FOUND +14h/04h T BLOCK SEQUENCE ERROR +14h/05h DT W O BK RECORD NOT FOUND - RECOMMEND REASSIGNMENT +14h/06h DT W O BK RECORD NOT FOUND - DATA AUTO-REALLOCATED +15h/00h DTL WRSOM BK RANDOM POSITIONING ERROR +15h/01h DTL WRSOM BK MECHANICAL POSITIONING ERROR +15h/02h DT WR O BK POSITIONING ERROR DETECTED BY READ OF MEDIUM +16h/00h D W O BK DATA SYNCHRONIZATION MARK ERROR +16h/01h D W O BK DATA SYNC ERROR - DATA REWRITTEN +16h/02h D W O BK DATA SYNC ERROR - RECOMMEND REWRITE +16h/03h D W O BK DATA SYNC ERROR - DATA AUTO-REALLOCATED +16h/04h D W O BK DATA SYNC ERROR - RECOMMEND REASSIGNMENT +17h/00h DT WRSO BK RECOVERED DATA WITH NO ERROR CORRECTION APPLIED +17h/01h DT WRSO BK RECOVERED DATA WITH RETRIES +17h/02h DT WR O BK RECOVERED DATA WITH POSITIVE HEAD OFFSET +17h/03h DT WR O BK RECOVERED DATA WITH NEGATIVE HEAD OFFSET +17h/04h WR O B RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED +17h/05h D WR O BK RECOVERED DATA USING PREVIOUS SECTOR ID +17h/06h D W O BK RECOVERED DATA WITHOUT ECC - DATA AUTO-REALLOCATED +17h/07h D WR O BK RECOVERED DATA WITHOUT ECC - RECOMMEND REASSIGNMENT +17h/08h D WR O BK RECOVERED DATA WITHOUT ECC - RECOMMEND REWRITE +17h/09h D WR O BK RECOVERED DATA WITHOUT ECC - DATA REWRITTEN +18h/00h DT WR O BK RECOVERED DATA WITH ERROR CORRECTION APPLIED +18h/01h D WR O BK RECOVERED DATA WITH ERROR CORR. & RETRIES APPLIED +18h/02h D WR O BK RECOVERED DATA - DATA AUTO-REALLOCATED +18h/03h R RECOVERED DATA WITH CIRC +18h/04h R RECOVERED DATA WITH L-EC +18h/05h D WR O BK RECOVERED DATA - RECOMMEND REASSIGNMENT +18h/06h D WR O BK RECOVERED DATA - RECOMMEND REWRITE +18h/07h D W O BK RECOVERED DATA WITH ECC - DATA REWRITTEN +19h/00h D O K DEFECT LIST ERROR +19h/01h D O K DEFECT LIST NOT AVAILABLE +19h/02h D O K DEFECT LIST ERROR IN PRIMARY LIST +19h/03h D O K DEFECT LIST ERROR IN GROWN LIST +1Ah/00h DTLPWRSOMCAEBK PARAMETER LIST LENGTH ERROR +1Bh/00h DTLPWRSOMCAEBK SYNCHRONOUS DATA TRANSFER ERROR +1Ch/00h D O BK DEFECT LIST NOT FOUND +1Ch/01h D O BK PRIMARY DEFECT LIST NOT FOUND +1Ch/02h D O BK GROWN DEFECT LIST NOT FOUND +1Dh/00h DT WR O BK MISCOMPARE DURING VERIFY OPERATION +1Eh/00h D W O BK RECOVERED ID WITH ECC CORRECTION +1Fh/00h D O K PARTIAL DEFECT LIST TRANSFER +20h/00h DTLPWRSOMCAEBK INVALID COMMAND OPERATION CODE +20h/01h access controls code 1 (99-314) [proposed] +20h/02h access controls code 2 (99-314) [proposed] +20h/03h access controls code 3 (99-314) [proposed] +21h/00h DT WR OM BK LOGICAL BLOCK ADDRESS OUT OF RANGE +21h/01h DT WR OM BK INVALID ELEMENT ADDRESS +22h/00h D ILLEGAL FUNCTION (USE 20 00, 24 00, OR 26 00) +23h/00h +24h/00h DTLPWRSOMCAEBK INVALID FIELD IN CDB +24h/01h DTLPWRSOMCAEBK CDB DECRYPTION ERROR +25h/00h DTLPWRSOMCAEBK LOGICAL UNIT NOT SUPPORTED +26h/00h DTLPWRSOMCAEBK INVALID FIELD IN PARAMETER LIST +26h/01h DTLPWRSOMCAEBK PARAMETER NOT SUPPORTED +26h/02h DTLPWRSOMCAEBK PARAMETER VALUE INVALID +26h/03h DTLPWRSOMCAE K THRESHOLD PARAMETERS NOT SUPPORTED +26h/04h DTLPWRSOMCAEBK INVALID RELEASE OF PERSISTENT RESERVATION +26h/05h DTLPWRSOMCA BK DATA DECRYPTION ERROR +26h/06h DTLPWRSO C K TOO MANY TARGET DESCRIPTORS +26h/07h DTLPWRSO C K UNSUPPORTED TARGET DESCRIPTOR TYPE CODE +26h/08h DTLPWRSO C K TOO MANY SEGMENT DESCRIPTORS +26h/09h DTLPWRSO C K UNSUPPORTED SEGMENT DESCRIPTOR TYPE CODE +26h/0Ah DTLPWRSO C K UNEXPECTED INEXACT SEGMENT +26h/0Bh DTLPWRSO C K INLINE DATA LENGTH EXCEEDED +26h/0Ch DTLPWRSO C K INVALID OPERATION FOR COPY SOURCE OR DESTINATION +26h/0Dh DTLPWRSO C K COPY SEGMENT GRANULARITY VIOLATION +27h/00h DT WR O BK WRITE PROTECTED +27h/01h DT WR O BK HARDWARE WRITE PROTECTED +27h/02h DT WR O BK LOGICAL UNIT SOFTWARE WRITE PROTECTED +27h/03h T R ASSOCIATED WRITE PROTECT +27h/04h T R PERSISTENT WRITE PROTECT +27h/05h T R PERMANENT WRITE PROTECT +28h/00h DTLPWRSOMCAEBK NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED +28h/01h DT WR OM B IMPORT OR EXPORT ELEMENT ACCESSED +29h/00h DTLPWRSOMCAEBK POWER ON, RESET, OR BUS DEVICE RESET OCCURRED +29h/01h DTLPWRSOMCAEBK POWER ON OCCURRED +29h/02h DTLPWRSOMCAEBK SCSI BUS RESET OCCURRED +29h/03h DTLPWRSOMCAEBK BUS DEVICE RESET FUNCTION OCCURRED +29h/04h DTLPWRSOMCAEBK DEVICE INTERNAL RESET +29h/05h DTLPWRSOMCAEBK TRANSCEIVER MODE CHANGED TO SINGLE-ENDED +29h/06h DTLPWRSOMCAEBK TRANSCEIVER MODE CHANGED TO LVD +2Ah/00h DTL WRSOMCAEBK PARAMETERS CHANGED +2Ah/01h DTL WRSOMCAEBK MODE PARAMETERS CHANGED +2Ah/02h DTL WRSOMCAE K LOG PARAMETERS CHANGED +2Ah/03h DTLPWRSOMCAE K RESERVATIONS PREEMPTED +2Ah/04h DTLPWRSOMCAE RESERVATIONS RELEASED +2Ah/05h DTLPWRSOMCAE REGISTRATIONS PREEMPTED +2Bh/00h DTLPWRSO C K COPY CANNOT EXECUTE SINCE HOST CANNOT DISCONNECT +2Ch/00h DTLPWRSOMCAEBK COMMAND SEQUENCE ERROR +2Ch/01h S TOO MANY WINDOWS SPECIFIED +2Ch/02h S INVALID COMBINATION OF WINDOWS SPECIFIED +2Ch/03h R CURRENT PROGRAM AREA IS NOT EMPTY +2Ch/04h R CURRENT PROGRAM AREA IS EMPTY +2Ch/05h B ILLEGAL POWER CONDITION REQUEST +2Dh/00h T OVERWRITE ERROR ON UPDATE IN PLACE +2Eh/00h DTLPWRSO CA K ERROR DETECTED BY THIRD PARTY TEMPORARY INITIATOR +2Eh/01h DTLPWRSO CA K THIRD PARTY DEVICE FAILURE +2Eh/02h DTLPWRSO CA K COPY TARGET DEVICE NOT REACHABLE +2Eh/03h DTLPWRSO CA K INCORRECT COPY TARGET DEVICE TYPE +2Eh/04h DTLPWRSO CA K COPY TARGET DEVICE DATA UNDERRUN +2Eh/05h DTLPWRSO CA K COPY TARGET DEVICE DATA OVERRUN +2Fh/00h DTLPWRSOMCAEBK COMMANDS CLEARED BY ANOTHER INITIATOR +30h/00h DT WR OM BK INCOMPATIBLE MEDIUM INSTALLED +30h/01h DT WR O BK CANNOT READ MEDIUM - UNKNOWN FORMAT +30h/02h DT WR O BK CANNOT READ MEDIUM - INCOMPATIBLE FORMAT +30h/03h DT R K CLEANING CARTRIDGE INSTALLED +30h/04h DT WR O BK CANNOT WRITE MEDIUM - UNKNOWN FORMAT +30h/05h DT WR O BK CANNOT WRITE MEDIUM - INCOMPATIBLE FORMAT +30h/06h DT WR O B CANNOT FORMAT MEDIUM - INCOMPATIBLE MEDIUM +30h/07h DTL WRSOM AEBK CLEANING FAILURE +30h/08h R CANNOT WRITE - APPLICATION CODE MISMATCH +30h/09h R CURRENT SESSION NOT FIXATED FOR APPEND +31h/00h DT WR O BK MEDIUM FORMAT CORRUPTED +31h/01h D L R O B FORMAT COMMAND FAILED +32h/00h D W O BK NO DEFECT SPARE LOCATION AVAILABLE +32h/01h D W O BK DEFECT LIST UPDATE FAILURE +33h/00h T TAPE LENGTH ERROR +34h/00h DTLPWRSOMCAEBK ENCLOSURE FAILURE +35h/00h DTLPWRSOMCAEBK ENCLOSURE SERVICES FAILURE +35h/01h DTLPWRSOMCAEBK UNSUPPORTED ENCLOSURE FUNCTION +35h/02h DTLPWRSOMCAEBK ENCLOSURE SERVICES UNAVAILABLE +35h/03h DTLPWRSOMCAEBK ENCLOSURE SERVICES TRANSFER FAILURE +35h/04h DTLPWRSOMCAEBK ENCLOSURE SERVICES TRANSFER REFUSED +36h/00h L RIBBON, INK, OR TONER FAILURE +37h/00h DTL WRSOMCAEBK ROUNDED PARAMETER +38h/00h B EVENT STATUS NOTIFICATION +38h/02h B ESN - POWER MANAGEMENT CLASS EVENT +38h/04h B ESN - MEDIA CLASS EVENT +38h/06h B ESN - DEVICE BUSY CLASS EVENT +39h/00h DTL WRSOMCAE K SAVING PARAMETERS NOT SUPPORTED +3Ah/00h DTL WRSOM BK MEDIUM NOT PRESENT +3Ah/01h DT WR OM BK MEDIUM NOT PRESENT - TRAY CLOSED +3Ah/02h DT WR OM BK MEDIUM NOT PRESENT - TRAY OPEN +3Ah/03h DT WR OM B MEDIUM NOT PRESENT - LOADABLE +3Ah/04h DT WR OM B MEDIUM NOT PRESENT - MEDIUM AUXILIARY MEMORY ACCESSIBLE +3Bh/00h TL SEQUENTIAL POSITIONING ERROR +3Bh/01h T TAPE POSITION ERROR AT BEGINNING-OF-MEDIUM +3Bh/02h T TAPE POSITION ERROR AT END-OF-MEDIUM +3Bh/03h L TAPE OR ELECTRONIC VERTICAL FORMS UNIT NOT READY +3Bh/04h L SLEW FAILURE +3Bh/05h L PAPER JAM +3Bh/06h L FAILED TO SENSE TOP-OF-FORM +3Bh/07h L FAILED TO SENSE BOTTOM-OF-FORM +3Bh/08h T REPOSITION ERROR +3Bh/09h S READ PAST END OF MEDIUM +3Bh/0Ah S READ PAST BEGINNING OF MEDIUM +3Bh/0Bh S POSITION PAST END OF MEDIUM +3Bh/0Ch T S POSITION PAST BEGINNING OF MEDIUM +3Bh/0Dh DT WR OM BK MEDIUM DESTINATION ELEMENT FULL +3Bh/0Eh DT WR OM BK MEDIUM SOURCE ELEMENT EMPTY +3Bh/0Fh R END OF MEDIUM REACHED +3Bh/11h DT WR OM BK MEDIUM MAGAZINE NOT ACCESSIBLE +3Bh/12h DT WR OM BK MEDIUM MAGAZINE REMOVED +3Bh/13h DT WR OM BK MEDIUM MAGAZINE INSERTED +3Bh/14h DT WR OM BK MEDIUM MAGAZINE LOCKED +3Bh/15h DT WR OM BK MEDIUM MAGAZINE UNLOCKED +3Bh/16h R MECHANICAL POSITIONING OR CHANGER ERROR +3Ch/00h +3Dh/00h DTLPWRSOMCAE K INVALID BITS IN IDENTIFY MESSAGE +3Eh/00h DTLPWRSOMCAEBK LOGICAL UNIT HAS NOT SELF-CONFIGURED YET +3Eh/01h DTLPWRSOMCAEBK LOGICAL UNIT FAILURE +3Eh/02h DTLPWRSOMCAEBK TIMEOUT ON LOGICAL UNIT +3Eh/03h DTLPWRSOMCAEBK LOGICAL UNIT FAILED SELF-TEST +3Eh/04h DTLPWRSOMCAEBK LOGICAL UNIT UNABLE TO UPDATE SELF-TEST LOG +3Fh/00h DTLPWRSOMCAEBK TARGET OPERATING CONDITIONS HAVE CHANGED +3Fh/01h DTLPWRSOMCAEBK MICROCODE HAS BEEN CHANGED +3Fh/02h DTLPWRSOMC BK CHANGED OPERATING DEFINITION +3Fh/03h DTLPWRSOMCAEBK INQUIRY DATA HAS CHANGED +3Fh/04h DT WR OMCAEBK COMPONENT DEVICE ATTACHED +3Fh/05h DT WR OMCAEBK DEVICE IDENTIFIER CHANGED +3Fh/06h DT WR OMCAEB REDUNDANCY GROUP CREATED OR MODIFIED +3Fh/07h DT WR OMCAEB REDUNDANCY GROUP DELETED +3Fh/08h DT WR OMCAEB SPARE CREATED OR MODIFIED +3Fh/09h DT WR OMCAEB SPARE DELETED +3Fh/0Ah DT WR OMCAEBK VOLUME SET CREATED OR MODIFIED +3Fh/0Bh DT WR OMCAEBK VOLUME SET DELETED +3Fh/0Ch DT WR OMCAEBK VOLUME SET DEASSIGNED +3Fh/0Dh DT WR OMCAEBK VOLUME SET REASSIGNED +3Fh/0Eh DTLPWRSOMCAE REPORTED LUNS DATA HAS CHANGED +3Fh/0Fh DTLPWRSOMCAEBK ECHO BUFFER OVERWRITTEN +3Fh/10h DT WR OM B MEDIUM LOADABLE +3Fh/11h DT WR OM B MEDIUM AUXILIARY MEMORY ACCESSIBLE +40h/00h D RAM FAILURE (SHOULD USE 40 NN) +40h/NNh DTLPWRSOMCAEBK DIAGNOSTIC FAILURE ON COMPONENT NN (80H-FFH) +41h/00h D DATA PATH FAILURE (SHOULD USE 40 NN) +42h/00h D POWER-ON OR SELF-TEST FAILURE (SHOULD USE 40 NN) +43h/00h DTLPWRSOMCAEBK MESSAGE ERROR +44h/00h DTLPWRSOMCAEBK INTERNAL TARGET FAILURE +45h/00h DTLPWRSOMCAEBK SELECT OR RESELECT FAILURE +46h/00h DTLPWRSOMC BK UNSUCCESSFUL SOFT RESET +47h/00h DTLPWRSOMCAEBK SCSI PARITY ERROR +47h/01h DTLPWRSOMCAEBK DATA PHASE CRC ERROR DETECTED +47h/02h DTLPWRSOMCAEBK SCSI PARITY ERROR DETECTED DURING ST DATA PHASE +47h/03h DTLPWRSOMCAEBK INFORMATION UNIT CRC ERROR DETECTED +47h/04h DTLPWRSOMCAEBK ASYNCHRONOUS INFORMATION PROTECTION ERROR DETECTED +48h/00h DTLPWRSOMCAEBK INITIATOR DETECTED ERROR MESSAGE RECEIVED +49h/00h DTLPWRSOMCAEBK INVALID MESSAGE ERROR +4Ah/00h DTLPWRSOMCAEBK COMMAND PHASE ERROR +4Bh/00h DTLPWRSOMCAEBK DATA PHASE ERROR +4Ch/00h DTLPWRSOMCAEBK LOGICAL UNIT FAILED SELF-CONFIGURATION +4Dh/NNh DTLPWRSOMCAEBK TAGGED OVERLAPPED COMMANDS (NN = QUEUE TAG) +4Eh/00h DTLPWRSOMCAEBK OVERLAPPED COMMANDS ATTEMPTED +4Fh/00h +50h/00h T WRITE APPEND ERROR +50h/01h T WRITE APPEND POSITION ERROR +50h/02h T POSITION ERROR RELATED TO TIMING +51h/00h T R O ERASE FAILURE +52h/00h T CARTRIDGE FAULT +53h/00h DTL WRSOM BK MEDIA LOAD OR EJECT FAILED +53h/01h T UNLOAD TAPE FAILURE +53h/02h DT WR OM BK MEDIUM REMOVAL PREVENTED +54h/00h P SCSI TO HOST SYSTEM INTERFACE FAILURE +55h/00h P SYSTEM RESOURCE FAILURE +55h/01h D O BK SYSTEM BUFFER FULL +55h/02h DTLPWRSOM AE K INSUFFICIENT RESERVATION RESOURCES +55h/03h DTLPWRSOMCAE INSUFFICIENT RESOURCES +55h/04h DTLPWRSOM AE INSUFFICIENT REGISTRATION RESOURCES +55h/05h access controls code 4 (99-314) [proposed] +55h/06h auxiliary memory code 1 (99-148) [proposed] +56h/00h +57h/00h R UNABLE TO RECOVER TABLE-OF-CONTENTS +58h/00h O GENERATION DOES NOT EXIST +59h/00h O UPDATED BLOCK READ +5Ah/00h DTLPWRSOM BK OPERATOR REQUEST OR STATE CHANGE INPUT +5Ah/01h DT WR OM BK OPERATOR MEDIUM REMOVAL REQUEST +5Ah/02h DT WR O A BK OPERATOR SELECTED WRITE PROTECT +5Ah/03h DT WR O A BK OPERATOR SELECTED WRITE PERMIT +5Bh/00h DTLPWRSOM K LOG EXCEPTION +5Bh/01h DTLPWRSOM K THRESHOLD CONDITION MET +5Bh/02h DTLPWRSOM K LOG COUNTER AT MAXIMUM +5Bh/03h DTLPWRSOM K LOG LIST CODES EXHAUSTED +5Ch/00h D O RPL STATUS CHANGE +5Ch/01h D O SPINDLES SYNCHRONIZED +5Ch/02h D O SPINDLES NOT SYNCHRONIZED +5Dh/00h DTLPWRSOMCAEBK FAILURE PREDICTION THRESHOLD EXCEEDED +5Dh/01h R B MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED +5Dh/02h R LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED +5Dh/10h D B HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE +5Dh/11h D B HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH +5Dh/12h D B HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH +5Dh/13h D B HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH +5Dh/14h D B HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS +5Dh/15h D B HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH +5Dh/16h D B HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH +5Dh/17h D B HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS +5Dh/18h D B HARDWARE IMPENDING FAILURE CONTROLLER DETECTED +5Dh/19h D B HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE +5Dh/1Ah D B HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE +5Dh/1Bh D B HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT +5Dh/1Ch D B HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT +5Dh/20h D B CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE +5Dh/21h D B CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH +5Dh/22h D B CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH +5Dh/23h D B CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH +5Dh/24h D B CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS +5Dh/25h D B CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH +5Dh/26h D B CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH +5Dh/27h D B CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS +5Dh/28h D B CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED +5Dh/29h D B CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE +5Dh/2Ah D B CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE +5Dh/2Bh D B CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT +5Dh/2Ch D B CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT +5Dh/30h D B DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE +5Dh/31h D B DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH +5Dh/32h D B DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH +5Dh/33h D B DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH +5Dh/34h D B DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS +5Dh/35h D B DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH +5Dh/36h D B DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH +5Dh/37h D B DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS +5Dh/38h D B DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED +5Dh/39h D B DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE +5Dh/3Ah D B DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE +5Dh/3Bh D B DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT +5Dh/3Ch D B DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT +5Dh/40h D B SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE +5Dh/41h D B SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH +5Dh/42h D B SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH +5Dh/43h D B SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH +5Dh/44h D B SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS +5Dh/45h D B SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH +5Dh/46h D B SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH +5Dh/47h D B SERVO IMPENDING FAILURE CHANNEL PARAMETRICS +5Dh/48h D B SERVO IMPENDING FAILURE CONTROLLER DETECTED +5Dh/49h D B SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE +5Dh/4Ah D B SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE +5Dh/4Bh D B SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT +5Dh/4Ch D B SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT +5Dh/50h D B SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE +5Dh/51h D B SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH +5Dh/52h D B SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH +5Dh/53h D B SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH +5Dh/54h D B SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS +5Dh/55h D B SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH +5Dh/56h D B SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH +5Dh/57h D B SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS +5Dh/58h D B SPINDLE IMPENDING FAILURE CONTROLLER DETECTED +5Dh/59h D B SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE +5Dh/5Ah D B SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE +5Dh/5Bh D B SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT +5Dh/5Ch D B SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT +5Dh/60h D B FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE +5Dh/61h D B FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH +5Dh/62h D B FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH +5Dh/63h D B FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH +5Dh/64h D B FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS +5Dh/65h D B FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH +5Dh/66h D B FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH +5Dh/67h D B FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS +5Dh/68h D B FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED +5Dh/69h D B FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE +5Dh/6Ah D B FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE +5Dh/6Bh D B FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT +5Dh/6Ch D B FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT +5Dh/FFh DTLPWRSOMCAEBK FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE) +5Eh/00h DTLPWRSO CA K LOW POWER CONDITION ON +5Eh/01h DTLPWRSO CA K IDLE CONDITION ACTIVATED BY TIMER +5Eh/02h DTLPWRSO CA K STANDBY CONDITION ACTIVATED BY TIMER +5Eh/03h DTLPWRSO CA K IDLE CONDITION ACTIVATED BY COMMAND +5Eh/04h DTLPWRSO CA K STANDBY CONDITION ACTIVATED BY COMMAND +5Eh/41h B POWER STATE CHANGE TO ACTIVE +5Eh/42h B POWER STATE CHANGE TO IDLE +5Eh/43h B POWER STATE CHANGE TO STANDBY +5Eh/45h B POWER STATE CHANGE TO SLEEP +5Eh/47h BK POWER STATE CHANGE TO DEVICE CONTROL +5Fh/00h +60h/00h S LAMP FAILURE +61h/00h S VIDEO ACQUISITION ERROR +61h/01h S UNABLE TO ACQUIRE VIDEO +61h/02h S OUT OF FOCUS +62h/00h S SCAN HEAD POSITIONING ERROR +63h/00h R END OF USER AREA ENCOUNTERED ON THIS TRACK +63h/01h R PACKET DOES NOT FIT IN AVAILABLE SPACE +64h/00h R ILLEGAL MODE FOR THIS TRACK +64h/01h R INVALID PACKET SIZE +65h/00h DTLPWRSOMCAEBK VOLTAGE FAULT +66h/00h S AUTOMATIC DOCUMENT FEEDER COVER UP +66h/01h S AUTOMATIC DOCUMENT FEEDER LIFT UP +66h/02h S DOCUMENT JAM IN AUTOMATIC DOCUMENT FEEDER +66h/03h S DOCUMENT MISS FEED AUTOMATIC IN DOCUMENT FEEDER +67h/00h A CONFIGURATION FAILURE +67h/01h A CONFIGURATION OF INCAPABLE LOGICAL UNITS FAILED +67h/02h A ADD LOGICAL UNIT FAILED +67h/03h A MODIFICATION OF LOGICAL UNIT FAILED +67h/04h A EXCHANGE OF LOGICAL UNIT FAILED +67h/05h A REMOVE OF LOGICAL UNIT FAILED +67h/06h A ATTACHMENT OF LOGICAL UNIT FAILED +67h/07h A CREATION OF LOGICAL UNIT FAILED +67h/08h A ASSIGN FAILURE OCCURRED +67h/09h A MULTIPLY ASSIGNED LOGICAL UNIT +68h/00h A LOGICAL UNIT NOT CONFIGURED +69h/00h A DATA LOSS ON LOGICAL UNIT +69h/01h A MULTIPLE LOGICAL UNIT FAILURES +69h/02h A PARITY/DATA MISMATCH +6Ah/00h A INFORMATIONAL, REFER TO LOG +6Bh/00h A STATE CHANGE HAS OCCURRED +6Bh/01h A REDUNDANCY LEVEL GOT BETTER +6Bh/02h A REDUNDANCY LEVEL GOT WORSE +6Ch/00h A REBUILD FAILURE OCCURRED +6Dh/00h A RECALCULATE FAILURE OCCURRED +6Eh/00h A COMMAND TO LOGICAL UNIT FAILED +6Fh/00h R COPY PROTECTION KEY EXCHANGE FAILURE - AUTHENTICATION FAILURE +6Fh/01h R COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT PRESENT +6Fh/02h R COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT ESTABLISHED +6Fh/03h R READ OF SCRAMBLED SECTOR WITHOUT AUTHENTICATION +6Fh/04h R MEDIA REGION CODE IS MISMATCHED TO LOGICAL UNIT REGION +6Fh/05h R DRIVE REGION MUST BE PERMANENT/REGION RESET COUNT ERROR +70h/NNh T DECOMPRESSION EXCEPTION SHORT ALGORITHM ID OF NN +71h/00h T DECOMPRESSION EXCEPTION LONG ALGORITHM ID +72h/00h R SESSION FIXATION ERROR +72h/01h R SESSION FIXATION ERROR WRITING LEAD-IN +72h/02h R SESSION FIXATION ERROR WRITING LEAD-OUT +72h/03h R SESSION FIXATION ERROR - INCOMPLETE TRACK IN SESSION +72h/04h R EMPTY OR PARTIALLY WRITTEN RESERVED TRACK +72h/05h R NO MORE TRACK RESERVATIONS ALLOWED +73h/00h R CD CONTROL ERROR +73h/01h R POWER CALIBRATION AREA ALMOST FULL +73h/02h R POWER CALIBRATION AREA IS FULL +73h/03h R POWER CALIBRATION AREA ERROR +73h/04h R PROGRAM MEMORY AREA UPDATE FAILURE +73h/05h R PROGRAM MEMORY AREA IS FULL +73h/06h R RMA/PMA IS FULL +74h/00h +75h/00h +76h/00h +77h/00h +78h/00h +79h/00h +7Ah/00h +7Bh/00h +7Ch/00h +7Dh/00h +7Eh/00h +7Fh/00h diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/t10.org/op-num.txt linux.ac/drivers/message/fusion/t10.org/op-num.txt --- linux.vanilla/drivers/message/fusion/t10.org/op-num.txt Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/t10.org/op-num.txt Tue Apr 3 17:54:47 2001 @@ -0,0 +1,234 @@ +File: OP-NUM.TXT + +SCSI Operation Codes +Numeric Sorted Listing +as of 3/21/00 + + D - DIRECT ACCESS DEVICE (SBC) device column key + .T - SEQUENTIAL ACCESS DEVICE (SSC) ------------------- + . L - PRINTER DEVICE (SSC) M = Mandatory + . P - PROCESSOR DEVICE (SPC) O = Optional + . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC) V = Vendor specific + . . R - CD DEVICE (MMC) Z = Obsolete + . . S - SCANNER DEVICE (SCSI-2) + . . .O - OPTICAL MEMORY DEVICE (SBC) + . . . M - MEDIA CHANGER DEVICE (SMC) + . . . C - COMMUNICATION DEVICE (SCSI-2) + . . . .A - STORAGE ARRAY DEVICE (SCC) + . . . . E - ENCLOSURE SERVICES DEVICE (SES) + . . . . B - SIMPLIFIED DIRECT-ACCESS DEVICE (RBC) + . . . . .K - OPTICAL CARD READER/WRITER DEVICE (OCRW) +OP DTLPWRSOMCAEBK Description +-- -------------- ---------------------------------------------------- +00 MMMMMMMMMMMMMM TEST UNIT READY +01 M REWIND +01 Z V ZZ ZO REZERO UNIT +02 VVVVVV V +03 MMMMMMMMMMMM M REQUEST SENSE +04 M O O FORMAT UNIT +04 O FORMAT MEDIUM +04 O FORMAT +05 VMVVVV V READ BLOCK LIMITS +06 VVVVVV V +07 OVV O OV REASSIGN BLOCKS +07 O INITIALIZE ELEMENT STATUS +08 O V OO OV READ(06) +08 M READ +08 O RECEIVE +08 M GET MESSAGE(06) +09 VVVVVV V +0A O O OV WRITE(06) +0A M WRITE +0A M SEND(06) +0A M SEND MESSAGE(06) +0A M PRINT +0B Z ZO ZV SEEK(06) +0B O SLEW AND PRINT +0C VVVVVV V +0D VVVVVV V +0E VVVVVV V +0F VOVVVV V READ REVERSE +10 VM VVV WRITE FILEMARKS +10 O O SYNCHRONIZE BUFFER +11 VMVVVV SPACE(6) +12 MMMMMMMMMMMMMM INQUIRY +13 V VVVV +13 O VERIFY +14 VOOVVV RECOVER BUFFERED DATA +15 OMO OOOOOOOO O MODE SELECT(06) +16 MMMOMOMM OO O RESERVE(06) +16 M RESERVE ELEMENT(06) +17 MMMOMOMM OO O RELEASE(06) +17 M RELEASE ELEMENT(06) +18 OOOOOOOO O COPY +19 VMVVVV ERASE +1A OMO OOOOOOOO O MODE SENSE(06) +1B O OM O O MO STOP START UNIT +1B O LOAD UNLOAD +1B O SCAN +1B O STOP PRINT +1C OOOOOOOOOOOM O RECEIVE DIAGNOSTIC RESULTS +1D MMMMMMMMMMOM M SEND DIAGNOSTIC +1E OO OM OO O PREVENT ALLOW MEDIUM REMOVAL +1F +20 V VV V V +21 V VV V V +22 V VV V V +23 V V V V +23 O READ FORMAT CAPACITIES +24 V VVM SET WINDOW +25 M MM M READ CAPACITY +25 M READ CARD CAPACITY +25 O GET WINDOW +26 V VV +27 V VV +28 M MMMM MM READ(10) +28 O GET MESSAGE(10) +29 V VV O READ GENERATION +2A M MM M MO WRITE(10) +2A O SEND(10) +2A O SEND MESSAGE(10) +2B O OM O O SEEK(10) +2B O LOCATE(10) +2B O POSITION TO ELEMENT +2C V O O ERASE(10) +2D V O O READ UPDATED BLOCK +2E O OO O MO WRITE AND VERIFY(10) +2F O OO O VERIFY(10) +30 Z ZZ Z SEARCH DATA HIGH(10) +31 Z ZZ Z SEARCH DATA EQUAL(10) +31 O OBJECT POSITION +32 Z ZZ Z SEARCH DATA LOW(10) +33 O OO O SET LIMITS(10) +34 O OO O O PRE-FETCH(10) +34 M READ POSITION +34 O GET DATA BUFFER STATUS +35 O OM O MO SYNCHRONIZE CACHE(10) +36 O OO O O LOCK UNLOCK CACHE(10) +37 O O READ DEFECT DATA(10) +38 O O O MEDIUM SCAN +39 OOOOOOOO O COMPARE +3A OOOOOOOO O COPY AND VERIFY +3B OOOOOOOOOOOOMO WRITE BUFFER +3C OOOOOOOOOOO O READ BUFFER +3D O O UPDATE BLOCK +3E O OO O READ LONG +3F O O O WRITE LONG +40 ZZZZZZZZZZ CHANGE DEFINITION +41 O WRITE SAME(10) +42 M READ SUB-CHANNEL +43 M READ TOC/PMA/ATIP +44 M REPORT DENSITY SUPPORT +44 M READ HEADER +45 O PLAY AUDIO(10) +46 O GET CONFIGURATION +47 O PLAY AUDIO MSF +48 Z PLAY AUDIO TRACK INDEX +49 Z PLAY TRACK RELATIVE(10) +4A O GET EVENT STATUS NOTIFICATION +4B O PAUSE/RESUME +4C OOOOOOOOOOO O LOG SELECT +4D OOOOOOOOOOO O LOG SENSE +4E O STOP PLAY/SCAN +4F +50 O XDWRITE(10) +51 O XPWRITE(10) +51 M READ DISC INFORMATION +52 O XDREAD(10) +52 M READ TRACK INFORMATION +53 M RESERVE TRACK +54 O SEND OPC INFORMATION +55 OOO OOOOOOOOMO MODE SELECT(10) +56 MMMOMMMM OO RESERVE(10) +56 M RESERVE ELEMENT(10) +57 MMMOMMMM OO RELEASE(10) +57 M RELEASE ELEMENT(10) +58 O REPAIR TRACK +59 O READ MASTER CUE +5A OOO OOOOOOOOMO MODE SENSE(10) +5B M CLOSE TRACK/SESSION +5C O READ BUFFER CAPACITY +5D O SEND CUE SHEET +5E OOOOOOOOO OO PERSISTENT RESERVE IN +5F OOOOOOOOO OO PERSISTENT RESERVE OUT +80 O XDWRITE EXTENDED(16) +81 O REBUILD(16) +82 O REGENERATE(16) +83 OOOOOOOO O EXTENDED COPY +84 OOOOOOOO O RECEIVE COPY RESULTS +85 +86 ACCESS CONTROL IN [proposed] +87 ACCESS CONTROL OUT [proposed] +88 O OO O O READ(16) +89 DEVICE LOCKS [proposed] +8A O O O O WRITE(16) +8B +8C READ ATTRIBUTES [proposed] +8D WRITE ATTRIBUTES [proposed] +8E O O O O WRITE AND VERIFY(16) +8F O OO O O VERIFY(16) +90 O OO O O PRE-FETCH(16) +91 O OO O O SYNCHRONIZE CACHE(16) +91 O SPACE(16) [1] +92 O OO O LOCK UNLOCK CACHE(16) +92 O LOCATE(16) [1] +93 O WRITE SAME(16) +94 [usage proposed by SCSI Socket Services project] +95 [usage proposed by SCSI Socket Services project] +96 [usage proposed by SCSI Socket Services project] +97 [usage proposed by SCSI Socket Services project] +98 MARGIN CONTROL [proposed] +99 +9A +9B +9C +9D +9E SERVICE ACTION IN [proposed] +9F SERVICE ACTION OUT [proposed] +A0 OOOOOOOOOOMO O REPORT LUNS +A1 O BLANK +A2 O SEND EVENT +A3 OOO O OOOMO MAINTENANCE (IN) +A3 O SEND KEY +A4 OOO O OOOOO MAINTENANCE (OUT) +A4 O REPORT KEY +A5 O M MOVE MEDIUM +A5 O PLAY AUDIO(12) +A6 O EXCHANGE MEDIUM +A6 O LOAD/UNLOAD C/DVD +A7 OO O OO MOVE MEDIUM ATTACHED +A7 O SET READ AHEAD +A8 OM O READ(12) +A8 O GET MESSAGE(12) +A9 Z PLAY TRACK RELATIVE(12) +AA O O WRITE(12) +AA O SEND MESSAGE(12) +AB +AC O ERASE(12) +AC O GET PERFORMANCE +AD O READ DVD STRUCTURE +AE O O WRITE AND VERIFY(12) +AF OZ O VERIFY(12) +B0 ZZ Z SEARCH DATA HIGH(12) +B1 ZZ Z SEARCH DATA EQUAL(12) +B2 ZZ Z SEARCH DATA LOW(12) +B3 OO O SET LIMITS(12) +B4 OO OZ OO READ ELEMENT STATUS ATTACHED +B5 O REQUEST VOLUME ELEMENT ADDRESS +B6 O SEND VOLUME TAG +B6 O SET STREAMING +B7 O READ DEFECT DATA(12) +B8 O Z M READ ELEMENT STATUS +B9 M READ CD MSF +BA O O OO MO REDUNDANCY GROUP (IN) +BA O SCAN +BB O O OO OO REDUNDANCY GROUP (OUT) +BB O SET CD-ROM SPEED +BC O O OO MO SPARE (IN) +BC O PLAY CD +BD O O OO OO SPARE (OUT) +BD M MECHANISM STATUS +BE O O OO MO VOLUME SET (IN) +BE O READ CD +BF O O OO OO VOLUME SET (OUT) +BF O SEND DVD STRUCTURE diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/t10.org/stds.txt linux.ac/drivers/message/fusion/t10.org/stds.txt --- linux.vanilla/drivers/message/fusion/t10.org/stds.txt Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/t10.org/stds.txt Tue Apr 3 17:54:47 2001 @@ -0,0 +1,120 @@ +File: STDS.TXT + +SCSI Standards Version Descriptor Value Assignments +as of 3/29/00 + +Code Standard +----- ---------------------------------------------------------------- +14BDh ANSI IEEE 1394:1995 +0B20h EPI (no version claimed) +0B3Ch EPI ANSI NCITS TR-23:1999 +0B3Bh EPI T10/1134 revision 16 +0D40h FC-AL (no version claimed) +0D5Ch FC-AL ANSI X3.272:1996 +0D60h FC-AL-2 (no version claimed) +0D61h FC-AL-2 T11/1133 revision 7.0 +1320h FC-FLA (no version claimed) +133Ch FC-FLA ANSI NCITS TR-20:1998 +133Bh FC-FLA T11/1235 revision 7 +0DA0h FC-FS (no version claimed) +0D20h FC-PH (no version claimed) +0D3Bh FC-PH ANSI X3.230:1994 +0D3Ch FC-PH ANSI X3.230:1994 with Amnd 1 ANSI X3.230/AM1:1996 +0D80h FC-PH-3 (no version claimed) +0D9Ch FC-PH-3 ANSI X3.303-1998 +1340h FC-PLDA (no version claimed) +135Ch FC-PLDA ANSI NCITS TR-19:1998 +135Bh FC-PLDA T11/1162 revision 2.1 +1300h FC-Tape (no version claimed) +1301h FC-Tape T11/1315 revision 1.16 +08C0h FCP (no version claimed) +08DCh FCP ANSI X3.269:1996 +08DBh FCP T10/0993 revision 12 +0900h FCP-2 (no version claimed) +0901h FCP-2 T10/1144 revision 4 +0AC0h Fast-20 (no version claimed) +0ADCh Fast-20 ANSI X3.277:1996 +0ADBh Fast-20 T10/1071 revision 06 +14A0h IEEE 1394 (no version claimed) +14C0h IEEE 1394a (no version claimed) +14E0h IEEE 1394b (no version claimed) +0140h MMC (no version claimed) +015Ch MMC ANSI X3.304:1997 +015Bh MMC T10/1048 revision 10a +0240h MMC-2 (no version claimed) +0255h MMC-2 T10/1228 revision 11 +02A0h MMC-3 (no version claimed) +0280h OCRW (no version claimed) +029Eh OCRW ISO/IEC 14776-381 +0220h RBC (no version claimed) +0238h RBC T10/1240 revision 10a +02C0h RMC (no version claimed) +0020h SAM (no version claimed) +003Ch SAM ANSI X3.270:1996 +003Bh SAM T10/0994 revision 18 +0040h SAM-2 (no version claimed) +0180h SBC (no version claimed) +019Ch SBC ANSI NCITS.306:1998 +019Bh SBC T10/0996 revision 08c +0320h SBC-2 (no version claimed) +08E0h SBP-2 (no version claimed) +08FCh SBP-2 ANSI NCITS.325:1999 +08FBh SBP-2 T10/1155 revision 04 +0160h SCC (no version claimed) +017Ch SCC ANSI X3.276:1997 +017Bh SCC T10/1047 revision 06c +01E0h SCC-2 (no version claimed) +01FCh SCC-2 ANSI NCITS.318:1998 +01FBh SCC-2 T10/1125 revision 04 +01C0h SES (no version claimed) +01DCh SES ANSI NCITS.305:1998 +01DBh SES T10/1212 revision 08b +08A0h SIP (no version claimed) +08BCh SIP ANSI X3.292:1997 +08BBh SIP T10/0856 revision 10 +01A0h SMC (no version claimed) +01BCh SMC ANSI NCITS.314:1998 +01BBh SMC T10/0999 revision 10a +02E0h SMC-2 (no version claimed) +0120h SPC (no version claimed) +013Ch SPC ANSI X3.301:1997 +013Bh SPC T10/0995 revision 11a +0260h SPC-2 (no version claimed) +0267h SPC-2 T10/1236 revision 12 +0300h SPC-3 (no version claimed) +0AA0h SPI (no version claimed) +0ABAh SPI ANSI X3.253:1995 +0ABCh SPI ANSI X3.253:1995 with SPI Amnd ANSI X3.253/AM1:1998 +0AB9h SPI T10/0855 revision 15a +0ABBh SPI T10/0855 revision 15a with SPI Amnd revision 3a +0AE0h SPI-2 (no version claimed) +0AFCh SPI-2 ANSI X3.302:1999 +0AFBh SPI-2 T10/1142 revision 20b +0B00h SPI-3 (no version claimed) +0B18h SPI-3 T10/1302-D revision 10 +0B19h SPI-3 T10/1302-D revision 13a +0B40h SPI-4 (no version claimed) +1360h SSA-PH2 (no version claimed) +137Ch SSA-PH2 ANSI X3.293:1996 +137Bh SSA-PH2 T10.1/1145 revision 09c +1380h SSA-PH3 (no version claimed) +139Ch SSA-PH3 ANSI NCITS.307:1998 +139Bh SSA-PH3 T10.1/1146 revision 05b +0880h SSA-S2P (no version claimed) +089Ch SSA-S2P ANSI X3.294:1996 +089Bh SSA-S2P T10.1/1121 revision 07b +0860h SSA-S3P (no version claimed) +087Ch SSA-S3P ANSI NCITS.309:1998 +087Bh SSA-S3P T10.1/1051 revision 05b +0840h SSA-TL1 (no version claimed) +085Ch SSA-TL1 ANSI X3.295:1996 +085Bh SSA-TL1 T10.1/0989 revision 10b +0820h SSA-TL2 (no version claimed) +083Ch SSA-TL2 ANSI NCITS.308:1998 +083Bh SSA-TL2 T10.1/1147 revision 05b +0200h SSC (no version claimed) +0201h SSC T10/0997 revision 17 +0207h SSC T10/0997 revision 22 +0920h SST (no version claimed) +0940h SVP (no version claimed) +0000h Version Descriptor Not Supported or No Standard Identified diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/fusion/t10.org/vendorid.txt linux.ac/drivers/message/fusion/t10.org/vendorid.txt --- linux.vanilla/drivers/message/fusion/t10.org/vendorid.txt Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/fusion/t10.org/vendorid.txt Tue Apr 3 17:54:47 2001 @@ -0,0 +1,340 @@ +This file contains the list of T10 Technical Committee vendor identifications +as of 2000/06/16 at 16:03:08. The purpose of this list is to help avoid redundant +usage of vendor identifications. T10, a subcommittee of the National +Committee on Information Technology Standards (NCITS), maintains an +informal list of vendor identifications currently in use. Please contact +the chairman of T10 prior to using a new vendor identification to avoid +conflicts. + + John Lohmeyer, Chair T10 Technical Committee + LSI Logic Corp. + 4420 ArrowsWest Dr. + Colorado Springs, CO 80907 + Tel: 719-533-7560 + Fax: 719-533-7183 + Email: lohmeyer@t10.org + +When requesting a new vendor ID, please specify your preferred vendor ID +code and your organization name EXACTLY as you would like it to appear in +this list. There are a few simple rules to follow: + + 1. The Vendor ID code shall be 8 or fewer ASCII graphic characters + (codes 21h through 7Eh). Spaces (20h) are added to the right to + make exactly 8 characters. + + 2. The Organization Name shall be 68 or fewer ASCII graphic characters + (codes 20h through 7Eh). + + 3. Please avoid requesting previously assigned vendor IDs. While the + committee list includes both upper and lowercase characters, + vendor IDs that are the same as an existing ID except for case + will not be accepted. + + Vendor ID Organization + --------- --------------------------------------------------------------- + 3M 3M Company + ACL Automated Cartridge Librarys, Inc. + AcuLab AcuLab, Inc. (Tulsa, OK) + ADAPTEC Adaptec + ADIC Advanced Digital Information Corporation + ADSI Adaptive Data Systems, Inc. (a Western Digital subsidiary) + ADTX ADTX Co., Ltd. + AERONICS Aeronics, Inc. + AGFA AGFA + AMCODYNE Amcodyne + ANAMATIC Anamartic Limited (England) + Ancor Ancor Communications, Inc. + ANCOT ANCOT Corp. + ANDATACO Andataco (now nStor) + ANRITSU Anritsu Corporation + APPLE Apple Computer, Inc. + ARCHIVE Archive + ARK ARK Research Corporation + ARTECON Artecon Inc. (Obs. - now Dot Hill) + ASACA ASACA Corp. + ASC Advanced Storage Concepts, Inc. + ASPEN Aspen Peripherals + AST AST Research + ASTK Alcatel STK A/S + AT&T AT&T + ATARI Atari Corporation + ATG CYG ATG Cygnet Inc. + ATTO ATTO Technology Inc. + ATX Alphatronix + AVR Advanced Vision Research + BALLARD Ballard Synergy Corp. + BERGSWD Berg Software Design + BEZIER Bezier Systems, Inc. + BHTi Breece Hill Technologies + BiT BiT Microsystems (obsolete, new ID: BITMICRO) + BITMICRO BiT Microsystems, Inc. + BNCHMARK Benchmark Tape Systems Corporation + BoxHill Box Hill Systems Corporation (Obs. - now Dot Hill) + BREA BREA Technologies, Inc. + BULL Bull Peripherals Corp. + BUSLOGIC BusLogic Inc. + CalComp CalComp, A Lockheed Company + CALIPER Caliper (California Peripheral Corp.) + CAST Advanced Storage Tech + CDC Control Data or MPI + CDP Columbia Data Products + CenData Central Data Corporation + Cereva Cereva Networks Inc. + CHEROKEE Cherokee Data Systems + CHINON Chinon + CIE&YED YE Data, C.Itoh Electric Corp. + CIPHER Cipher Data Products + Ciprico Ciprico, Inc. + CIRRUSL Cirrus Logic Inc. + CMD CMD Technology Inc. + CNGR SFW Congruent Software, Inc. + CNSi Chaparral Network Storage, Inc. + COGITO Cogito + COMPAQ Compaq Computer Corporation + COMPORT Comport Corp. + COMPSIG Computer Signal Corporation + COMPTEX Comptex Pty Limited + CONNER Conner Peripherals + CORE Core International, Inc. + CPL Cross Products Ltd + CPU TECH CPU Technology, Inc. + CREO Creo Products Inc. + CROSFLD Crosfield Electronics (now FujiFilm Electonic Imaging Ltd) + CROSSRDS Crossroads Systems, Inc. + CSM, INC Computer SM, Inc. + Data Com Data Com Information Systems Pty. Ltd. + DATABOOK Databook, Inc. + DATACOPY Datacopy Corp. + DataCore DataCore Software Corporation + DATAPT Datapoint Corp. + DEC Digital Equipment (Obsolete: New products use 'COMPAQ') + DEI Digital Engineering, Inc. + DELL Dell Computer Corporation + DELPHI Delphi Data Div. of Sparks Industries, Inc. + DENON Denon/Nippon Columbia + DenOptix DenOptix, Inc. + DEST DEST Corp. + DGC Data General Corp. + DIGIDATA Digi-Data Corporation + DigiIntl Digi International + Digital Digital Equipment Corporation (Obs: New products use 'COMPAQ') + DILOG Distributed Logic Corp. + DISC Document Imaging Systems Corp. + DotHill Dot Hill Systems Corp. + DPT Distributed Processing Technology + DSI Data Spectrum, Inc. + DSM Deterner Steuerungs- und Maschinenbau GmbH & Co. + DTC QUME Data Technology Qume + DXIMAGIN DX Imaging + ECCS ECCS, Inc. + ECMA European Computer Manufacturers Association + Elms Elms Systems Corporation + EMASS EMASS, Inc. + EMC EMC Corp. + EMTEC EMTEC Magnetics + EMULEX Emulex + EPSON Epson + Eris/RSI RSI Systems, Inc. + EuroLogc Eurologic Systems Limited + EXABYTE Exabyte Corp. + FFEILTD FujiFilm Electonic Imaging Ltd + FILENET FileNet Corp. + FRAMDRV FRAMEDRIVE Corp. + FUJI Fuji Electric Co., Ltd. (Japan) + FUJIFILM Fuji Photo Film, Co., Ltd. + FUJITSU Fujitsu + FUNAI Funai Electric Co., Ltd. + FUTURED Future Domain Corp. + G&D Giesecke & Devrient GmbH + GENSIG General Signal Networks + Gen_Dyn General Dynamics + GIGATAPE GIGATAPE GmbH + GIGATRND GigaTrend Incorporated + Global Global Memory Test Consortium + Goidelic Goidelic Precision, Inc. + GoldStar LG Electronics Inc. + GOULD Gould + HAGIWARA Hagiwara Sys-Com Co., Ltd. + HITACHI Hitachi America Ltd or Nissei Sangyo America Ltd + HONEYWEL Honeywell Inc. + HP Hewlett Packard + i-cubed i-cubed ltd. + IBM International Business Machines + ICL ICL + ICP ICP vortex Computersysteme GmbH + IDE International Data Engineering, Inc. + IGR Intergraph Corp. + IMATION Imation + IMPLTD Integrated Micro Products Ltd. + IMPRIMIS Imprimis Technology Inc. + Indigita Indigita Corporation + INITIO Initio Corporation + INSITE Insite Peripherals + INTEL Intel Corporation + IOC I/O Concepts, Inc. + IOMEGA Iomega + ISi Information Storage inc. + ISO International Standards Organization + ITC International Tapetronics Corporation + JPC Inc. JPC Inc. + JVC JVC Information Products Co. + KENNEDY Kennedy Company + KENWOOD KENWOOD Corporation + KODAK Eastman Kodak + KONAN Konan + KONICA Konica Japan + Kyocera Kyocera Corporation + LAPINE Lapine Technology + LASERDRV LaserDrive Limited + LASERGR Lasergraphics, Inc. + LG LG Electronics Inc. + LGE LG Electronics Inc. + LION Lion Optics Corporation + LMS Laser Magnetic Storage International Company + LSI LSI Logic Corp. + LSILOGIC LSI Logic Storage Systems, Inc. + LTO-CVE Linear Tape - Open, Compliance Verification Entity + MATSHITA Matsushita + MAXELL Hitachi Maxell, Ltd. + MaxOptix Maxoptix Corp. + MAXSTRAT Maximum Strategy, Inc. + MAXTOR Maxtor Corp. + McDATA McDATA Corporation + MDI Micro Design International, Inc. + MEADE Meade Instruments Corporation + MEII Mountain Engineering II, Inc. + MELA Mitsubishi Electronics America + MELCO Mitsubishi Electric (Japan) + MEMREL Memrel Corporation + MEMTECH MemTech Technology + MERIDATA Oy Meridata Finland Ltd + METRUM Metrum, Inc. + MICROBTX Microbotics Inc. + MICROP Micropolis + MICROTEK Microtek Storage Corp + Minitech Minitech (UK) Limited + Minolta Minolta Corporation + MINSCRIB Miniscribe + MITSUMI Mitsumi Electric Co., Ltd. + MOSAID Mosaid Technologies Inc. + MOTOROLA Motorola + MPM Mitsubishi Paper Mills, Ltd. + MST Morning Star Technologies, Inc. + MTNGATE MountainGate Data Systems + NAI North Atlantic Industries + NAKAMICH Nakamichi Corporation + NatInst National Instruments + NatSemi National Semiconductor Corp. + NCITS National Committee for Information Technology Standards + NCL NCL America + NCR NCR Corporation + NEC NEC + NEXSAN Nexsan Technologies, Ltd. + NISCA NISCA Inc. + NKK NKK Corp. + NRC Nakamichi Research Corporation + NSD Nippon Systems Development Co.,Ltd. + NSM NSM Jukebox GmbH + nStor nStor Technologies, Inc. + NT Northern Telecom + OAI Optical Access International + OCE Oce Graphics + OKI OKI Electric Industry Co.,Ltd (Japan) + OMI Optical Media International + OMNIS OMNIS Company (FRANCE) + OPTIMEM Cipher/Optimem + OPTOTECH Optotech + ORANGE Orange Micro, Inc. + ORCA Orca Technology + OSI Optical Storage International + OTL OTL Engineering + PASCOsci Pasco Scientific + PATHLGHT Pathlight Technology, Inc. + PERTEC Pertec Peripherals Corporation + PFTI Performance Technology Inc. + PFU PFU Limited + PICO Packard Instrument Company + PIONEER Pioneer Electronic Corp. + PLASMON Plasmon Data + PRAIRIE PrairieTek + PREPRESS PrePRESS Solutions + PRESOFT PreSoft Architects + PRESTON Preston Scientific + PRIAM Priam + PRIMAGFX Primagraphics Ltd + PROCOM Procom Technology + PTI Peripheral Technology Inc. + QIC Quarter-Inch Cartridge Drive Standards, Inc. + QUALSTAR Qualstar + QUANTEL Quantel Ltd. + QUANTUM Quantum Corp. + R-BYTE R-Byte, Inc. + RACALREC Racal Recorders + RADSTONE Radstone Technology + RGI Raster Graphics, Inc. + RHS Racal-Heim Systems GmbH + RICOH Ricoh + RODIME Rodime + RTI Reference Technology + SAMSUNG Samsung Electronics Co., Ltd. + SAN Storage Area Networks, Ltd. + SANKYO Sankyo Seiki + SANYO SANYO Electric Co., Ltd. + SCInc. Storage Concepts, Inc. + SCREEN Dainippon Screen Mfg. Co., Ltd. + SDI Storage Dimensions, Inc. + SDS Solid Data Systems + SEAGATE Seagate + SEQUOIA Sequoia Advanced Technologies, Inc. + Shinko Shinko Electric Co., Ltd. + SIEMENS Siemens + SII Seiko Instruments Inc. + SMS Scientific Micro Systems/OMTI + SNYSIDE Sunnyside Computing Inc. + SONIC Sonic Solutions + SONY Sony Corporation Japan + SPD Storage Products Distribution, Inc. + SPECIAL Special Computing Co. + SPECTRA Spectra Logic, a Division of Western Automation Labs, Inc. + SPERRY Sperry (now Unisys Corp.) + Sterling Sterling Diagnostic Imaging, Inc. + STK Storage Technology Corporation + STORAPP StorageApps, Inc. + STORM Storm Technology, Inc. + StrmLgc StreamLogic Corp. + SUMITOMO Sumitomo Electric Industries, Ltd. + SUN Sun Microsystems, Inc. + SYMBIOS Symbios Logic Inc. + SyQuest SyQuest Technology, Inc. + SYSGEN Sysgen + T-MITTON Transmitton England + TALARIS Talaris Systems, Inc. + TALLGRAS Tallgrass Technologies + TANDBERG Tandberg Data A/S + TANDON Tandon + TDK TDK Corporation + TEAC TEAC Japan + TECOLOTE Tecolote Designs + TEGRA Tegra Varityper + Tek Tektronix + TENTIME Laura Technologies, Inc. + TI-DSG Texas Instruments + TMS Texas Memory Systems, Inc. + TOSHIBA Toshiba Japan + TRIPACE Tripace + ULTRA UltraStor Corporation + UNISYS Unisys + USCORE Underscore, Inc. + USDC US Design Corp. + VDS Victor Data Systems Co., Ltd. + VERBATIM Verbatim Corporation + VEXCEL VEXCEL IMAGING GmbH + VICOMSL1 Vicom Systems, Inc. + VRC Vermont Research Corp. + WangDAT WangDAT + WANGTEK Wangtek + WDIGTL Western Digital + WEARNES Wearnes Technology Corporation + WSC0001 Wisecom, Inc. + X3 National Committee for Information Technology Standards (NCITS) + XEBEC Xebec Corporation diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/Config.in linux.ac/drivers/message/i2o/Config.in --- linux.vanilla/drivers/message/i2o/Config.in Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/Config.in Tue Apr 3 17:54:47 2001 @@ -0,0 +1,16 @@ +mainmenu_option next_comment +comment 'I2O device support' + +tristate 'I2O support' CONFIG_I2O + +if [ "$CONFIG_PCI" = "y" ]; then + dep_tristate ' I2O PCI support' CONFIG_I2O_PCI $CONFIG_I2O +fi +dep_tristate ' I2O Block OSM' CONFIG_I2O_BLOCK $CONFIG_I2O +if [ "$CONFIG_NET" = "y" ]; then + dep_tristate ' I2O LAN OSM' CONFIG_I2O_LAN $CONFIG_I2O +fi +dep_tristate ' I2O SCSI OSM' CONFIG_I2O_SCSI $CONFIG_I2O $CONFIG_SCSI +dep_tristate ' I2O /proc support' CONFIG_I2O_PROC $CONFIG_I2O + +endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/Makefile linux.ac/drivers/message/i2o/Makefile --- linux.vanilla/drivers/message/i2o/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/Makefile Tue Apr 3 17:54:47 2001 @@ -0,0 +1,20 @@ +# +# Makefile for the kernel I2O OSM. +# +# Note : at this point, these files are compiled on all systems. +# In the future, some of these should be built conditionally. +# + +O_TARGET := i2o.o + +export-objs := i2o_pci.o i2o_core.o i2o_config.o i2o_block.o i2o_lan.o i2o_scsi.o i2o_proc.o + +obj-$(CONFIG_I2O_PCI) += i2o_pci.o +obj-$(CONFIG_I2O) += i2o_core.o i2o_config.o +obj-$(CONFIG_I2O_BLOCK) += i2o_block.o +obj-$(CONFIG_I2O_LAN) += i2o_lan.o +obj-$(CONFIG_I2O_SCSI) += i2o_scsi.o +obj-$(CONFIG_I2O_PROC) += i2o_proc.o + +include $(TOPDIR)/Rules.make + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/README linux.ac/drivers/message/i2o/README --- linux.vanilla/drivers/message/i2o/README Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/README Tue Apr 3 17:54:47 2001 @@ -0,0 +1,98 @@ + + Linux I2O Support (c) Copyright 1999 Red Hat Software + and others. + + 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. + +AUTHORS (so far) + +Alan Cox, Building Number Three Ltd. + Core code, SCSI and Block OSMs + +Steve Ralston, LSI Logic Corp. + Debugging SCSI and Block OSM + +Deepak Saxena, Intel Corp. + Various core/block extensions + /proc interface, bug fixes + Ioctl interfaces for control + Debugging LAN OSM + +Philip Rumpf + Fixed assorted dumb SMP locking bugs + +Juha Sievanen, University of Helsinki Finland + LAN OSM code + /proc interface to LAN class + Bug fixes + Core code extensions + +Auvo Häkkinen, University of Helsinki Finland + LAN OSM code + /Proc interface to LAN class + Bug fixes + Core code extensions + +Taneli Vähäkangas, University of Helsinki Finland + Fixes to i2o_config + +CREDITS + + This work was made possible by + +Red Hat Software + Funding for the Building #3 part of the project + +Symbios Logic (Now LSI) + Host adapters, hints, known to work platforms when I hit + compatibility problems + +BoxHill Corporation + Loan of initial FibreChannel disk array used for development work. + +European Comission + Funding the work done by the University of Helsinki + +SysKonnect + Loan of FDDI and Gigabit Ethernet cards + +ASUSTeK + Loan of I2O motherboard + +STATUS: + +o The core setup works within limits. +o The scsi layer seems to almost work. + I'm still chasing down the hang bug. +o The block OSM is mostly functional +o LAN OSM works with FDDI and Ethernet cards. + +TO DO: + +General: +o Provide hidden address space if asked +o Long term message flow control +o PCI IOP's without interrupts are not supported yet +o Push FAIL handling into the core +o DDM control interfaces for module load etc +o Add I2O 2.0 support (Deffered to 2.5 kernel) + +Block: +o Multiple major numbers +o Read ahead and cache handling stuff. Talk to Ingo and people +o Power management +o Finish Media changers + +SCSI: +o Find the right way to associate drives/luns/busses + +Lan: +o Performance tuning +o Test Fibre Channel code + +Tape: +o Anyone seen anything implementing this ? + (D.S: Will attempt to do so if spare cycles permit) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/README.ioctl linux.ac/drivers/message/i2o/README.ioctl --- linux.vanilla/drivers/message/i2o/README.ioctl Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/README.ioctl Tue Apr 3 17:54:47 2001 @@ -0,0 +1,394 @@ + +Linux I2O User Space Interface +rev 0.3 - 04/20/99 + +============================================================================= +Originally written by Deepak Saxena(deepak@plexity.net) +Currently maintained by Deepak Saxena(deepak@plexity.net) +============================================================================= + +I. Introduction + +The Linux I2O subsystem provides a set of ioctl() commands that can be +utilized by user space applications to communicate with IOPs and devices +on individual IOPs. This document defines the specific ioctl() commands +that are available to the user and provides examples of their uses. + +This document assumes the reader is familiar with or has access to the +I2O specification as no I2O message parameters are outlined. For information +on the specification, see http://www.i2osig.org + +This document and the I2O user space interface are currently maintained +by Deepak Saxena. Please send all comments, errata, and bug fixes to +deepak@csociety.purdue.edu + +II. IOP Access + +Access to the I2O subsystem is provided through the device file named +/dev/i2o/ctl. This file is a character file with major number 10 and minor +number 166. It can be created through the following command: + + mknod /dev/i2o/ctl c 10 166 + +III. Determining the IOP Count + + SYNOPSIS + + ioctl(fd, I2OGETIOPS, int *count); + + u8 count[MAX_I2O_CONTROLLERS]; + + DESCRIPTION + + This function returns the system's active IOP table. count should + point to a buffer containing MAX_I2O_CONTROLLERS entries. Upon + returning, each entry will contain a non-zero value if the given + IOP unit is active, and NULL if it is inactive or non-existent. + + RETURN VALUE. + + Returns 0 if no errors occur, and -1 otherwise. If an error occurs, + errno is set appropriately: + + EFAULT Invalid user space pointer was passed + +IV. Getting Hardware Resource Table + + SYNOPSIS + + ioctl(fd, I2OHRTGET, struct i2o_cmd_hrt *hrt); + + struct i2o_cmd_hrtlct + { + u32 iop; /* IOP unit number */ + void *resbuf; /* Buffer for result */ + u32 *reslen; /* Buffer length in bytes */ + }; + + DESCRIPTION + + This function returns the Hardware Resource Table of the IOP specified + by hrt->iop in the buffer pointed to by hrt->resbuf. The actual size of + the data is written into *(hrt->reslen). + + RETURNS + + This function returns 0 if no errors occur. If an error occurs, -1 + is returned and errno is set appropriately: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ENOBUFS Buffer not large enough. If this occurs, the required + buffer length is written into *(hrt->reslen) + +V. Getting Logical Configuration Table + + SYNOPSIS + + ioctl(fd, I2OLCTGET, struct i2o_cmd_lct *lct); + + struct i2o_cmd_hrtlct + { + u32 iop; /* IOP unit number */ + void *resbuf; /* Buffer for result */ + u32 *reslen; /* Buffer length in bytes */ + }; + + DESCRIPTION + + This function returns the Logical Configuration Table of the IOP specified + by lct->iop in the buffer pointed to by lct->resbuf. The actual size of + the data is written into *(lct->reslen). + + RETURNS + + This function returns 0 if no errors occur. If an error occurs, -1 + is returned and errno is set appropriately: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ENOBUFS Buffer not large enough. If this occurs, the required + buffer length is written into *(lct->reslen) + +VI. Settting Parameters + + SYNOPSIS + + ioctl(fd, I2OPARMSET, struct i2o_parm_setget *ops); + + struct i2o_cmd_psetget + { + u32 iop; /* IOP unit number */ + u32 tid; /* Target device TID */ + void *opbuf; /* Operation List buffer */ + u32 oplen; /* Operation List buffer length in bytes */ + void *resbuf; /* Result List buffer */ + u32 *reslen; /* Result List buffer length in bytes */ + }; + + DESCRIPTION + + This function posts a UtilParamsSet message to the device identified + by ops->iop and ops->tid. The operation list for the message is + sent through the ops->opbuf buffer, and the result list is written + into the buffer pointed to by ops->resbuf. The number of bytes + written is placed into *(ops->reslen). + + RETURNS + + The return value is the size in bytes of the data written into + ops->resbuf if no errors occur. If an error occurs, -1 is returned + and errno is set appropriatly: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ENOBUFS Buffer not large enough. If this occurs, the required + buffer length is written into *(ops->reslen) + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + + A return value of 0 does not mean that the value was actually + changed properly on the IOP. The user should check the result + list to determine the specific status of the transaction. + +VII. Getting Parameters + + SYNOPSIS + + ioctl(fd, I2OPARMGET, struct i2o_parm_setget *ops); + + struct i2o_parm_setget + { + u32 iop; /* IOP unit number */ + u32 tid; /* Target device TID */ + void *opbuf; /* Operation List buffer */ + u32 oplen; /* Operation List buffer length in bytes */ + void *resbuf; /* Result List buffer */ + u32 *reslen; /* Result List buffer length in bytes */ + }; + + DESCRIPTION + + This function posts a UtilParamsGet message to the device identified + by ops->iop and ops->tid. The operation list for the message is + sent through the ops->opbuf buffer, and the result list is written + into the buffer pointed to by ops->resbuf. The actual size of data + written is placed into *(ops->reslen). + + RETURNS + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ENOBUFS Buffer not large enough. If this occurs, the required + buffer length is written into *(ops->reslen) + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + + A return value of 0 does not mean that the value was actually + properly retreived. The user should check the result list + to determine the specific status of the transaction. + +VIII. Downloading Software + + SYNOPSIS + + ioctl(fd, I2OSWDL, struct i2o_sw_xfer *sw); + + struct i2o_sw_xfer + { + u32 iop; /* IOP unit number */ + u8 flags; /* DownloadFlags field */ + u8 sw_type; /* Software type */ + u32 sw_id; /* Software ID */ + void *buf; /* Pointer to software buffer */ + u32 *swlen; /* Length of software buffer */ + u32 *maxfrag; /* Number of fragments */ + u32 *curfrag; /* Current fragment number */ + }; + + DESCRIPTION + + This function downloads a software fragment pointed by sw->buf + to the iop identified by sw->iop. The DownloadFlags, SwID, SwType + and SwSize fields of the ExecSwDownload message are filled in with + the values of sw->flags, sw->sw_id, sw->sw_type and *(sw->swlen). + + The fragments _must_ be sent in order and be 8K in size. The last + fragment _may_ be shorter, however. The kernel will compute its + size based on information in the sw->swlen field. + + Please note that SW transfers can take a long time. + + RETURNS + + This function returns 0 no errors occur. If an error occurs, -1 + is returned and errno is set appropriatly: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + +IX. Uploading Software + + SYNOPSIS + + ioctl(fd, I2OSWUL, struct i2o_sw_xfer *sw); + + struct i2o_sw_xfer + { + u32 iop; /* IOP unit number */ + u8 flags; /* UploadFlags */ + u8 sw_type; /* Software type */ + u32 sw_id; /* Software ID */ + void *buf; /* Pointer to software buffer */ + u32 *swlen; /* Length of software buffer */ + u32 *maxfrag; /* Number of fragments */ + u32 *curfrag; /* Current fragment number */ + }; + + DESCRIPTION + + This function uploads a software fragment from the IOP identified + by sw->iop, sw->sw_type, sw->sw_id and optionally sw->swlen fields. + The UploadFlags, SwID, SwType and SwSize fields of the ExecSwUpload + message are filled in with the values of sw->flags, sw->sw_id, + sw->sw_type and *(sw->swlen). + + The fragments _must_ be requested in order and be 8K in size. The + user is responsible for allocating memory pointed by sw->buf. The + last fragment _may_ be shorter. + + Please note that SW transfers can take a long time. + + RETURNS + + This function returns 0 if no errors occur. If an error occurs, -1 + is returned and errno is set appropriatly: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + +X. Removing Software + + SYNOPSIS + + ioctl(fd, I2OSWDEL, struct i2o_sw_xfer *sw); + + struct i2o_sw_xfer + { + u32 iop; /* IOP unit number */ + u8 flags; /* RemoveFlags */ + u8 sw_type; /* Software type */ + u32 sw_id; /* Software ID */ + void *buf; /* Unused */ + u32 *swlen; /* Length of the software data */ + u32 *maxfrag; /* Unused */ + u32 *curfrag; /* Unused */ + }; + + DESCRIPTION + + This function removes software from the IOP identified by sw->iop. + The RemoveFlags, SwID, SwType and SwSize fields of the ExecSwRemove message + are filled in with the values of sw->flags, sw->sw_id, sw->sw_type and + *(sw->swlen). Give zero in *(sw->len) if the value is unknown. IOP uses + *(sw->swlen) value to verify correct identication of the module to remove. + The actual size of the module is written into *(sw->swlen). + + RETURNS + + This function returns 0 if no errors occur. If an error occurs, -1 + is returned and errno is set appropriatly: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + +X. Validating Configuration + + SYNOPSIS + + ioctl(fd, I2OVALIDATE, int *iop); + u32 iop; + + DESCRIPTION + + This function posts an ExecConfigValidate message to the controller + identified by iop. This message indicates that the the current + configuration is accepted. The iop changes the status of suspect drivers + to valid and may delete old drivers from its store. + + RETURNS + + This function returns 0 if no erro occur. If an error occurs, -1 is + returned and errno is set appropriatly: + + ETIMEDOUT Timeout waiting for reply message + ENXIO Invalid IOP number + +XI. Configuration Dialog + + SYNOPSIS + + ioctl(fd, I2OHTML, struct i2o_html *htquery); + struct i2o_html + { + u32 iop; /* IOP unit number */ + u32 tid; /* Target device ID */ + u32 page; /* HTML page */ + void *resbuf; /* Buffer for reply HTML page */ + u32 *reslen; /* Length in bytes of reply buffer */ + void *qbuf; /* Pointer to HTTP query string */ + u32 qlen; /* Length in bytes of query string buffer */ + }; + + DESCRIPTION + + This function posts an UtilConfigDialog message to the device identified + by htquery->iop and htquery->tid. The requested HTML page number is + provided by the htquery->page field, and the resultant data is stored + in the buffer pointed to by htquery->resbuf. If there is an HTTP query + string that is to be sent to the device, it should be sent in the buffer + pointed to by htquery->qbuf. If there is no query string, this field + should be set to NULL. The actual size of the reply received is written + into *(htquery->reslen). + + RETURNS + + This function returns 0 if no error occur. If an error occurs, -1 + is returned and errno is set appropriatly: + + EFAULT Invalid user space pointer was passed + ENXIO Invalid IOP number + ENOBUFS Buffer not large enough. If this occurs, the required + buffer length is written into *(ops->reslen) + ETIMEDOUT Timeout waiting for reply message + ENOMEM Kernel memory allocation error + +XII. Events + + In the process of determining this. Current idea is to have use + the select() interface to allow user apps to periodically poll + the /dev/i2o/ctl device for events. When select() notifies the user + that an event is available, the user would call read() to retrieve + a list of all the events that are pending for the specific device. + +============================================================================= +Revision History +============================================================================= + +Rev 0.1 - 04/01/99 +- Initial revision + +Rev 0.2 - 04/06/99 +- Changed return values to match UNIX ioctl() standard. Only return values + are 0 and -1. All errors are reported through errno. +- Added summary of proposed possible event interfaces + +Rev 0.3 - 04/20/99 +- Changed all ioctls() to use pointers to user data instead of actual data +- Updated error values to match the code diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_block.c linux.ac/drivers/message/i2o/i2o_block.c --- linux.vanilla/drivers/message/i2o/i2o_block.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_block.c Sun Apr 22 00:56:37 2001 @@ -0,0 +1,2074 @@ +/* + * I2O Random Block Storage Class OSM + * + * (C) Copyright 1999 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This is a beta test release. Most of the good code was taken + * from the nbd driver by Pavel Machek, who in turn took some of it + * from loop.c. Isn't free software great for reusability 8) + * + * Fixes/additions: + * Steve Ralston: + * Multiple device handling error fixes, + * Added a queue depth. + * Alan Cox: + * FC920 has an rmw bug. Dont or in the end marker. + * Removed queue walk, fixed for 64bitness. + * Deepak Saxena: + * Independent queues per IOP + * Support for dynamic device creation/deletion + * Code cleanup + * Support for larger I/Os through merge* functions + * (taken from DAC960 driver) + * Boji T Kannanthanam: + * Set the I2O Block devices to be detected in increasing + * order of TIDs during boot. + * Search and set the I2O block device that we boot off from as + * the first device to be claimed (as /dev/i2o/hda) + * Properly attach/detach I2O gendisk structure from the system + * gendisk list. The I2O block devices now appear in + * /proc/partitions. + * + * To do: + * Serial number scanning to find duplicates for FC multipathing + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR I2O_MAJOR + +#include + +#define MAX_I2OB 16 + +#define MAX_I2OB_DEPTH 128 +#define MAX_I2OB_RETRIES 4 + +//#define DRIVERDEBUG +#ifdef DRIVERDEBUG +#define DEBUG( s ) +#else +#define DEBUG( s ) printk( s ) +#endif + +/* + * Events that this OSM is interested in + */ +#define I2OB_EVENT_MASK (I2O_EVT_IND_BSA_VOLUME_LOAD | \ + I2O_EVT_IND_BSA_VOLUME_UNLOAD | \ + I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ | \ + I2O_EVT_IND_BSA_CAPACITY_CHANGE | \ + I2O_EVT_IND_BSA_SCSI_SMART ) + + +/* + * I2O Block Error Codes - should be in a header file really... + */ +#define I2O_BSA_DSC_SUCCESS 0x0000 +#define I2O_BSA_DSC_MEDIA_ERROR 0x0001 +#define I2O_BSA_DSC_ACCESS_ERROR 0x0002 +#define I2O_BSA_DSC_DEVICE_FAILURE 0x0003 +#define I2O_BSA_DSC_DEVICE_NOT_READY 0x0004 +#define I2O_BSA_DSC_MEDIA_NOT_PRESENT 0x0005 +#define I2O_BSA_DSC_MEDIA_LOCKED 0x0006 +#define I2O_BSA_DSC_MEDIA_FAILURE 0x0007 +#define I2O_BSA_DSC_PROTOCOL_FAILURE 0x0008 +#define I2O_BSA_DSC_BUS_FAILURE 0x0009 +#define I2O_BSA_DSC_ACCESS_VIOLATION 0x000A +#define I2O_BSA_DSC_WRITE_PROTECTED 0x000B +#define I2O_BSA_DSC_DEVICE_RESET 0x000C +#define I2O_BSA_DSC_VOLUME_CHANGED 0x000D +#define I2O_BSA_DSC_TIMEOUT 0x000E + +/* + * Some of these can be made smaller later + */ + +static int i2ob_blksizes[MAX_I2OB<<4]; +static int i2ob_hardsizes[MAX_I2OB<<4]; +static int i2ob_sizes[MAX_I2OB<<4]; +static int i2ob_media_change_flag[MAX_I2OB]; +static u32 i2ob_max_sectors[MAX_I2OB<<4]; + +static int i2ob_context; + +/* + * I2O Block device descriptor + */ +struct i2ob_device +{ + struct i2o_controller *controller; + struct i2o_device *i2odev; + int unit; + int tid; + int flags; + int refcnt; + struct request *head, *tail; + request_queue_t *req_queue; + int max_segments; + int done_flag; + int constipated; + int depth; +}; + +/* + * FIXME: + * We should cache align these to avoid ping-ponging lines on SMP + * boxes under heavy I/O load... + */ +struct i2ob_request +{ + struct i2ob_request *next; + struct request *req; + int num; +}; + +/* + * Per IOP requst queue information + * + * We have a separate requeust_queue_t per IOP so that a heavilly + * loaded I2O block device on an IOP does not starve block devices + * across all I2O controllers. + * + */ +struct i2ob_iop_queue +{ + atomic_t queue_depth; + struct i2ob_request request_queue[MAX_I2OB_DEPTH]; + struct i2ob_request *i2ob_qhead; + request_queue_t req_queue; +}; +static struct i2ob_iop_queue *i2ob_queues[MAX_I2O_CONTROLLERS]; +static struct i2ob_request *i2ob_backlog[MAX_I2O_CONTROLLERS]; +static struct i2ob_request *i2ob_backlog_tail[MAX_I2O_CONTROLLERS]; + +/* + * Each I2O disk is one of these. + */ + +static struct i2ob_device i2ob_dev[MAX_I2OB<<4]; +static int i2ob_dev_count = 0; +static struct hd_struct i2ob[MAX_I2OB<<4]; +static struct gendisk i2ob_gendisk; /* Declared later */ + +/* + * Mutex and spin lock for event handling synchronization + * evt_msg contains the last event. + */ +static DECLARE_MUTEX_LOCKED(i2ob_evt_sem); +static DECLARE_MUTEX_LOCKED(i2ob_thread_dead); +static spinlock_t i2ob_evt_lock = SPIN_LOCK_UNLOCKED; +static u32 evt_msg[MSG_FRAME_SIZE>>2]; + +static struct timer_list i2ob_timer; +static int i2ob_timer_started = 0; + +static void i2o_block_reply(struct i2o_handler *, struct i2o_controller *, + struct i2o_message *); +static void i2ob_new_device(struct i2o_controller *, struct i2o_device *); +static void i2ob_del_device(struct i2o_controller *, struct i2o_device *); +static void i2ob_reboot_event(void); +static int i2ob_install_device(struct i2o_controller *, struct i2o_device *, int); +static void i2ob_end_request(struct request *); +static void i2ob_request(request_queue_t *); +static int i2ob_backlog_request(struct i2o_controller *, struct i2ob_device *); +static int i2ob_init_iop(unsigned int); +static request_queue_t* i2ob_get_queue(kdev_t); +static int i2ob_query_device(struct i2ob_device *, int, int, void*, int); +static int do_i2ob_revalidate(kdev_t, int); +static int i2ob_evt(void *); + +static int evt_pid = 0; +static int evt_running = 0; +static int scan_unit = 0; + +/* + * I2O OSM registration structure...keeps getting bigger and bigger :) + */ +static struct i2o_handler i2o_block_handler = +{ + i2o_block_reply, + i2ob_new_device, + i2ob_del_device, + i2ob_reboot_event, + "I2O Block OSM", + 0, + I2O_CLASS_RANDOM_BLOCK_STORAGE +}; + +/* + * Get a message + */ + +static u32 i2ob_get(struct i2ob_device *dev) +{ + struct i2o_controller *c=dev->controller; + return I2O_POST_READ32(c); +} + +/* + * Turn a Linux block request into an I2O block read/write. + */ + +static int i2ob_send(u32 m, struct i2ob_device *dev, struct i2ob_request *ireq, u32 base, int unit) +{ + struct i2o_controller *c = dev->controller; + int tid = dev->tid; + unsigned long msg; + unsigned long mptr; + u64 offset; + struct request *req = ireq->req; + struct buffer_head *bh = req->bh; + int count = req->nr_sectors<<9; + char *last = NULL; + unsigned short size = 0; + + // printk(KERN_INFO "i2ob_send called\n"); + /* Map the message to a virtual address */ + msg = c->mem_offset + m; + + /* + * Build the message based on the request. + */ + __raw_writel(i2ob_context|(unit<<8), msg+8); + __raw_writel(ireq->num, msg+12); + __raw_writel(req->nr_sectors << 9, msg+20); + + /* + * Mask out partitions from now on + */ + unit &= 0xF0; + + /* This can be optimised later - just want to be sure its right for + starters */ + offset = ((u64)(req->sector+base)) << 9; + __raw_writel( offset & 0xFFFFFFFF, msg+24); + __raw_writel(offset>>32, msg+28); + mptr=msg+32; + + if(req->cmd == READ) + { + __raw_writel(I2O_CMD_BLOCK_READ<<24|HOST_TID<<12|tid, msg+4); + while(bh!=NULL) + { + if(bh->b_data == last) { + size += bh->b_size; + last += bh->b_size; + if(bh->b_reqnext) + __raw_writel(0x14000000|(size), mptr-8); + else + __raw_writel(0xD4000000|(size), mptr-8); + } + else + { + if(bh->b_reqnext) + __raw_writel(0x10000000|(bh->b_size), mptr); + else + __raw_writel(0xD0000000|(bh->b_size), mptr); + __raw_writel(virt_to_bus(bh->b_data), mptr+4); + mptr += 8; + size = bh->b_size; + last = bh->b_data + size; + } + + count -= bh->b_size; + bh = bh->b_reqnext; + } + /* + * Heuristic for now since the block layer doesnt give + * us enough info. If its a big write assume sequential + * readahead on controller. If its small then don't read + * ahead but do use the controller cache. + */ + if(size >= 8192) + __raw_writel((8<<24)|(1<<16)|8, msg+16); + else + __raw_writel((8<<24)|(1<<16)|4, msg+16); + } + else if(req->cmd == WRITE) + { + __raw_writel(I2O_CMD_BLOCK_WRITE<<24|HOST_TID<<12|tid, msg+4); + while(bh!=NULL) + { + if(bh->b_data == last) { + size += bh->b_size; + last += bh->b_size; + if(bh->b_reqnext) + __raw_writel(0x14000000|(size), mptr-8); + else + __raw_writel(0xD4000000|(size), mptr-8); + } + else + { + if(bh->b_reqnext) + __raw_writel(0x14000000|(bh->b_size), mptr); + else + __raw_writel(0xD4000000|(bh->b_size), mptr); + __raw_writel(virt_to_bus(bh->b_data), mptr+4); + mptr += 8; + size = bh->b_size; + last = bh->b_data + size; + } + + count -= bh->b_size; + bh = bh->b_reqnext; + } + + if(c->battery) + { + + if(size>16384) + __raw_writel(4, msg+16); + else + /* + * Allow replies to come back once data is cached in the controller + * This allows us to handle writes quickly thus giving more of the + * queue to reads. + */ + __raw_writel(16, msg+16); + } + else + { + /* Large write, don't cache */ + if(size>8192) + __raw_writel(4, msg+16); + else + /* write through */ + __raw_writel(8, msg+16); + } + } + __raw_writel(I2O_MESSAGE_SIZE(mptr-msg)>>2 | SGL_OFFSET_8, msg); + + if(count != 0) + { + printk(KERN_ERR "Request count botched by %d.\n", count); + } + + i2o_post_message(c,m); + atomic_inc(&i2ob_queues[c->unit]->queue_depth); + + return 0; +} + +/* + * Remove a request from the _locked_ request list. We update both the + * list chain and if this is the last item the tail pointer. Caller + * must hold the lock. + */ + +static inline void i2ob_unhook_request(struct i2ob_request *ireq, + unsigned int iop) +{ + ireq->next = i2ob_queues[iop]->i2ob_qhead; + i2ob_queues[iop]->i2ob_qhead = ireq; +} + +/* + * Request completion handler + */ + +static inline void i2ob_end_request(struct request *req) +{ + /* + * Loop until all of the buffers that are linked + * to this request have been marked updated and + * unlocked. + */ + + while (end_that_request_first( req, !req->errors, "i2o block" )); + + /* + * It is now ok to complete the request. + */ + end_that_request_last( req ); +} + +/* + * Request merging functions + */ +static inline int i2ob_new_segment(request_queue_t *q, struct request *req, + int __max_segments) +{ + int max_segments = i2ob_dev[MINOR(req->rq_dev)].max_segments; + + if (__max_segments < max_segments) + max_segments = __max_segments; + + if (req->nr_segments < max_segments) { + req->nr_segments++; + return 1; + } + return 0; +} + +static int i2ob_back_merge(request_queue_t *q, struct request *req, + struct buffer_head *bh, int __max_segments) +{ + if (req->bhtail->b_data + req->bhtail->b_size == bh->b_data) + return 1; + return i2ob_new_segment(q, req, __max_segments); +} + +static int i2ob_front_merge(request_queue_t *q, struct request *req, + struct buffer_head *bh, int __max_segments) +{ + if (bh->b_data + bh->b_size == req->bh->b_data) + return 1; + return i2ob_new_segment(q, req, __max_segments); +} + +static int i2ob_merge_requests(request_queue_t *q, + struct request *req, + struct request *next, + int __max_segments) +{ + int max_segments = i2ob_dev[MINOR(req->rq_dev)].max_segments; + int total_segments = req->nr_segments + next->nr_segments; + + if (__max_segments < max_segments) + max_segments = __max_segments; + + if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) + total_segments--; + + if (total_segments > max_segments) + return 0; + + req->nr_segments = total_segments; + return 1; +} + +static int i2ob_flush(struct i2o_controller *c, struct i2ob_device *d, int unit) +{ + unsigned long msg; + u32 m = i2ob_get(d); + + if(m == 0xFFFFFFFF) + return -1; + + msg = c->mem_offset + m; + + /* + * Ask the controller to write the cache back. This sorts out + * the supertrak firmware flaw and also does roughly the right + * thing for other cases too. + */ + + __raw_writel(FIVE_WORD_MSG_SIZE|SGL_OFFSET_0, msg); + __raw_writel(I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|d->tid, msg+4); + __raw_writel(i2ob_context|(unit<<8), msg+8); + __raw_writel(0, msg+12); + __raw_writel(60<<16, msg+16); + + i2o_post_message(c,m); + return 0; +} + +/* + * OSM reply handler. This gets all the message replies + */ + +static void i2o_block_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg) +{ + unsigned long flags; + struct i2ob_request *ireq = NULL; + u8 st; + u32 *m = (u32 *)msg; + u8 unit = (m[2]>>8)&0xF0; /* low 4 bits are partition */ + struct i2ob_device *dev = &i2ob_dev[(unit&0xF0)]; + + /* + * FAILed message + */ + if(m[0] & (1<<13)) + { + /* + * FAILed message from controller + * We increment the error count and abort it + * + * In theory this will never happen. The I2O block class + * speficiation states that block devices never return + * FAILs but instead use the REQ status field...but + * better be on the safe side since no one really follows + * the spec to the book :) + */ + ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; + ireq->req->errors++; + + spin_lock_irqsave(&io_request_lock, flags); + i2ob_unhook_request(ireq, c->unit); + i2ob_end_request(ireq->req); + spin_unlock_irqrestore(&io_request_lock, flags); + + /* Now flush the message by making it a NOP */ + m[0]&=0x00FFFFFF; + m[0]|=(I2O_CMD_UTIL_NOP)<<24; + i2o_post_message(c,virt_to_bus(m)); + + return; + } + + if(msg->function == I2O_CMD_UTIL_EVT_REGISTER) + { + spin_lock(&i2ob_evt_lock); + memcpy(evt_msg, msg, (m[0]>>16)<<2); + spin_unlock(&i2ob_evt_lock); + up(&i2ob_evt_sem); + return; + } + + if(msg->function == I2O_CMD_BLOCK_CFLUSH) + { + spin_lock_irqsave(&io_request_lock, flags); + dev->constipated=0; + DEBUG(("unconstipated\n")); + if(i2ob_backlog_request(c, dev)==0) + i2ob_request(dev->req_queue); + spin_unlock_irqrestore(&io_request_lock, flags); + return; + } + + if(!dev->i2odev) + { + /* + * This is HACK, but Intel Integrated RAID allows user + * to delete a volume that is claimed, locked, and in use + * by the OS. We have to check for a reply from a + * non-existent device and flag it as an error or the system + * goes kaput... + */ + ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; + ireq->req->errors++; + printk(KERN_WARNING "I2O Block: Data transfer to deleted device!\n"); + spin_lock_irqsave(&io_request_lock, flags); + i2ob_unhook_request(ireq, c->unit); + i2ob_end_request(ireq->req); + spin_unlock_irqrestore(&io_request_lock, flags); + return; + } + + /* + * Lets see what is cooking. We stuffed the + * request in the context. + */ + + ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; + st=m[4]>>24; + + if(st!=0) + { + int err; + char *bsa_errors[] = + { + "Success", + "Media Error", + "Failure communicating to device", + "Device Failure", + "Device is not ready", + "Media not present", + "Media is locked by another user", + "Media has failed", + "Failure communicating to device", + "Device bus failure", + "Device is locked by another user", + "Device is write protected", + "Device has reset", + "Volume has changed, waiting for acknowledgement" + }; + + err = m[4]&0xFFFF; + + /* + * Device not ready means two things. One is that the + * the thing went offline (but not a removal media) + * + * The second is that you have a SuperTrak 100 and the + * firmware got constipated. Unlike standard i2o card + * setups the supertrak returns an error rather than + * blocking for the timeout in these cases. + */ + + + spin_lock_irqsave(&io_request_lock, flags); + if(err==4) + { + /* + * Time to uncork stuff + */ + + if(!dev->constipated) + { + dev->constipated = 1; + DEBUG(("constipated\n")); + /* Now pull the chain */ + if(i2ob_flush(c, dev, unit)<0) + { + DEBUG(("i2ob: Unable to queue flush. Retrying I/O immediately.\n")); + dev->constipated=0; + } + DEBUG(("flushing\n")); + } + + /* + * Recycle the request + */ + +// i2ob_unhook_request(ireq, c->unit); + + /* + * Place it on the recycle queue + */ + + ireq->next = NULL; + if(i2ob_backlog_tail[c->unit]!=NULL) + i2ob_backlog_tail[c->unit]->next = ireq; + else + i2ob_backlog[c->unit] = ireq; + i2ob_backlog_tail[c->unit] = ireq; + + atomic_dec(&i2ob_queues[c->unit]->queue_depth); + + /* + * If the constipator flush failed we want to + * poke the queue again. + */ + + i2ob_request(dev->req_queue); + spin_unlock_irqrestore(&io_request_lock, flags); + + /* + * and out + */ + + return; + } + spin_unlock_irqrestore(&io_request_lock, flags); + printk(KERN_ERR "\n/dev/%s error: %s", dev->i2odev->dev_name, + bsa_errors[m[4]&0XFFFF]); + if(m[4]&0x00FF0000) + printk(" - DDM attempted %d retries", (m[4]>>16)&0x00FF ); + printk(".\n"); + ireq->req->errors++; + } + else + ireq->req->errors = 0; + + /* + * Dequeue the request. We use irqsave locks as one day we + * may be running polled controllers from a BH... + */ + + spin_lock_irqsave(&io_request_lock, flags); + i2ob_unhook_request(ireq, c->unit); + i2ob_end_request(ireq->req); + atomic_dec(&i2ob_queues[c->unit]->queue_depth); + + /* + * We may be able to do more I/O + */ + + if(i2ob_backlog_request(c, dev)==0) + i2ob_request(dev->req_queue); + + spin_unlock_irqrestore(&io_request_lock, flags); +} + +/* + * Event handler. Needs to be a separate thread b/c we may have + * to do things like scan a partition table, or query parameters + * which cannot be done from an interrupt or from a bottom half. + */ +static int i2ob_evt(void *dummy) +{ + unsigned int evt; + unsigned int flags; + int unit; + int i; + //The only event that has data is the SCSI_SMART event. + struct i2o_reply { + u32 header[4]; + u32 evt_indicator; + u8 ASC; + u8 ASCQ; + u8 data[16]; + } *evt_local; + + lock_kernel(); + daemonize(); + unlock_kernel(); + + strcpy(current->comm, "i2oblock"); + evt_running = 1; + + while(1) + { + if(down_interruptible(&i2ob_evt_sem)) + { + evt_running = 0; + printk("exiting..."); + break; + } + + /* + * Keep another CPU/interrupt from overwriting the + * message while we're reading it + * + * We stuffed the unit in the TxContext and grab the event mask + * None of the BSA we care about events have EventData + */ + spin_lock_irqsave(&i2ob_evt_lock, flags); + evt_local = (struct i2o_reply *)evt_msg; + spin_unlock_irqrestore(&i2ob_evt_lock, flags); + + unit = evt_local->header[3]; + evt = evt_local->evt_indicator; + + switch(evt) + { + /* + * New volume loaded on same TID, so we just re-install. + * The TID/controller don't change as it is the same + * I2O device. It's just new media that we have to + * rescan. + */ + case I2O_EVT_IND_BSA_VOLUME_LOAD: + { + i2ob_install_device(i2ob_dev[unit].i2odev->controller, + i2ob_dev[unit].i2odev, unit); + break; + } + + /* + * No media, so set all parameters to 0 and set the media + * change flag. The I2O device is still valid, just doesn't + * have media, so we don't want to clear the controller or + * device pointer. + */ + case I2O_EVT_IND_BSA_VOLUME_UNLOAD: + { + for(i = unit; i <= unit+15; i++) + { + i2ob_sizes[i] = 0; + i2ob_hardsizes[i] = 0; + i2ob_max_sectors[i] = 0; + i2ob[i].nr_sects = 0; + i2ob_gendisk.part[i].nr_sects = 0; + } + i2ob_media_change_flag[unit] = 1; + break; + } + + case I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ: + printk(KERN_WARNING "%s: Attempt to eject locked media\n", + i2ob_dev[unit].i2odev->dev_name); + break; + + /* + * The capacity has changed and we are going to be + * updating the max_sectors and other information + * about this disk. We try a revalidate first. If + * the block device is in use, we don't want to + * do that as there may be I/Os bound for the disk + * at the moment. In that case we read the size + * from the device and update the information ourselves + * and the user can later force a partition table + * update through an ioctl. + */ + case I2O_EVT_IND_BSA_CAPACITY_CHANGE: + { + u64 size; + + if(do_i2ob_revalidate(MKDEV(MAJOR_NR, unit),0) != -EBUSY) + continue; + + if(i2ob_query_device(&i2ob_dev[unit], 0x0004, 0, &size, 8) !=0 ) + i2ob_query_device(&i2ob_dev[unit], 0x0000, 4, &size, 8); + + spin_lock_irqsave(&io_request_lock, flags); + i2ob_sizes[unit] = (int)(size>>10); + i2ob_gendisk.part[unit].nr_sects = size>>9; + i2ob[unit].nr_sects = (int)(size>>9); + spin_unlock_irqrestore(&io_request_lock, flags); + break; + } + + /* + * We got a SCSI SMART event, we just log the relevant + * information and let the user decide what they want + * to do with the information. + */ + case I2O_EVT_IND_BSA_SCSI_SMART: + { + char buf[16]; + printk(KERN_INFO "I2O Block: %s received a SCSI SMART Event\n",i2ob_dev[unit].i2odev->dev_name); + evt_local->data[16]='\0'; + sprintf(buf,"%s",&evt_local->data[0]); + printk(KERN_INFO " Disk Serial#:%s\n",buf); + printk(KERN_INFO " ASC 0x%02x \n",evt_local->ASC); + printk(KERN_INFO " ASCQ 0x%02x \n",evt_local->ASCQ); + break; + } + + /* + * Non event + */ + + case 0: + break; + + /* + * An event we didn't ask for. Call the card manufacturer + * and tell them to fix their firmware :) + */ + default: + printk(KERN_INFO "%s: Received event %d we didn't register for\n" + KERN_INFO " Blame the I2O card manufacturer 8)\n", + i2ob_dev[unit].i2odev->dev_name, evt); + break; + } + }; + + up_and_exit(&i2ob_thread_dead,0); + return 0; +} + +/* + * The timer handler will attempt to restart requests + * that are queued to the driver. This handler + * currently only gets called if the controller + * had no more room in its inbound fifo. + */ + +static void i2ob_timer_handler(unsigned long q) +{ + unsigned long flags; + + /* + * We cannot touch the request queue or the timer + * flag without holding the io_request_lock. + */ + spin_lock_irqsave(&io_request_lock,flags); + + /* + * Clear the timer started flag so that + * the timer can be queued again. + */ + i2ob_timer_started = 0; + + /* + * Restart any requests. + */ + i2ob_request((request_queue_t*)q); + + /* + * Free the lock. + */ + spin_unlock_irqrestore(&io_request_lock,flags); +} + +static int i2ob_backlog_request(struct i2o_controller *c, struct i2ob_device *dev) +{ + u32 m; + struct i2ob_request *ireq; + + while((ireq=i2ob_backlog[c->unit])!=NULL) + { + int unit; + + if(atomic_read(&i2ob_queues[c->unit]->queue_depth) > dev->depth/4) + break; + + m = i2ob_get(dev); + if(m == 0xFFFFFFFF) + break; + + i2ob_backlog[c->unit] = ireq->next; + if(i2ob_backlog[c->unit] == NULL) + i2ob_backlog_tail[c->unit] = NULL; + + unit = MINOR(ireq->req->rq_dev); + i2ob_send(m, dev, ireq, i2ob[unit].start_sect, unit); + } + if(i2ob_backlog[c->unit]) + return 1; + return 0; +} + +/* + * The I2O block driver is listed as one of those that pulls the + * front entry off the queue before processing it. This is important + * to remember here. If we drop the io lock then CURRENT will change + * on us. We must unlink CURRENT in this routine before we return, if + * we use it. + */ + +static void i2ob_request(request_queue_t *q) +{ + struct request *req; + struct i2ob_request *ireq; + int unit; + struct i2ob_device *dev; + u32 m; + + + while (!list_empty(&q->queue_head)) { + /* + * On an IRQ completion if there is an inactive + * request on the queue head it means it isnt yet + * ready to dispatch. + */ + req = blkdev_entry_next_request(&q->queue_head); + + if(req->rq_status == RQ_INACTIVE) + return; + + unit = MINOR(req->rq_dev); + dev = &i2ob_dev[(unit&0xF0)]; + + /* + * Queue depths probably belong with some kind of + * generic IOP commit control. Certainly its not right + * its global! + */ + if(atomic_read(&i2ob_queues[dev->unit]->queue_depth) >= dev->depth) + break; + + /* + * Is the channel constipated ? + */ + + if(i2ob_backlog[dev->unit]!=NULL) + break; + + /* Get a message */ + m = i2ob_get(dev); + + if(m==0xFFFFFFFF) + { + /* + * See if the timer has already been queued. + */ + if (!i2ob_timer_started) + { + DEBUG((KERN_ERR "i2ob: starting timer\n")); + + /* + * Set the timer_started flag to insure + * that the timer is only queued once. + * Queing it more than once will corrupt + * the timer queue. + */ + i2ob_timer_started = 1; + + /* + * Set up the timer to expire in + * 500ms. + */ + i2ob_timer.expires = jiffies + (HZ >> 1); + i2ob_timer.data = (unsigned int)q; + + /* + * Start it. + */ + + add_timer(&i2ob_timer); + return; + } + } + + /* + * Everything ok, so pull from kernel queue onto our queue + */ + req->errors = 0; + blkdev_dequeue_request(req); + req->sem = NULL; + + ireq = i2ob_queues[dev->unit]->i2ob_qhead; + i2ob_queues[dev->unit]->i2ob_qhead = ireq->next; + ireq->req = req; + + i2ob_send(m, dev, ireq, i2ob[unit].start_sect, (unit&0xF0)); + } +} + + +/* + * SCSI-CAM for ioctl geometry mapping + * Duplicated with SCSI - this should be moved into somewhere common + * perhaps genhd ? + * + * LBA -> CHS mapping table taken from: + * + * "Incorporating the I2O Architecture into BIOS for Intel Architecture + * Platforms" + * + * This is an I2O document that is only available to I2O members, + * not developers. + * + * From my understanding, this is how all the I2O cards do this + * + * Disk Size | Sectors | Heads | Cylinders + * ---------------+---------+-------+------------------- + * 1 < X <= 528M | 63 | 16 | X/(63 * 16 * 512) + * 528M < X <= 1G | 63 | 32 | X/(63 * 32 * 512) + * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512) + * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512) + * + */ +#define BLOCK_SIZE_528M 1081344 +#define BLOCK_SIZE_1G 2097152 +#define BLOCK_SIZE_21G 4403200 +#define BLOCK_SIZE_42G 8806400 +#define BLOCK_SIZE_84G 17612800 + +static void i2o_block_biosparam( + unsigned long capacity, + unsigned short *cyls, + unsigned char *hds, + unsigned char *secs) +{ + unsigned long heads, sectors, cylinders; + + sectors = 63L; /* Maximize sectors per track */ + if(capacity <= BLOCK_SIZE_528M) + heads = 16; + else if(capacity <= BLOCK_SIZE_1G) + heads = 32; + else if(capacity <= BLOCK_SIZE_21G) + heads = 64; + else if(capacity <= BLOCK_SIZE_42G) + heads = 128; + else + heads = 255; + + cylinders = capacity / (heads * sectors); + + *cyls = (unsigned short) cylinders; /* Stuff return values */ + *secs = (unsigned char) sectors; + *hds = (unsigned char) heads; +} + + +/* + * Rescan the partition tables + */ + +static int do_i2ob_revalidate(kdev_t dev, int maxu) +{ + int minor=MINOR(dev); + int i; + + minor&=0xF0; + + i2ob_dev[minor].refcnt++; + if(i2ob_dev[minor].refcnt>maxu+1) + { + i2ob_dev[minor].refcnt--; + return -EBUSY; + } + + for( i = 15; i>=0 ; i--) + { + int m = minor+i; + kdev_t d = MKDEV(MAJOR_NR, m); + struct super_block *sb = get_super(d); + + sync_dev(d); + if(sb) + invalidate_inodes(sb); + invalidate_buffers(d); + i2ob_gendisk.part[m].start_sect = 0; + i2ob_gendisk.part[m].nr_sects = 0; + } + + /* + * Do a physical check and then reconfigure + */ + + i2ob_install_device(i2ob_dev[minor].controller, i2ob_dev[minor].i2odev, + minor); + i2ob_dev[minor].refcnt--; + return 0; +} + +/* + * Issue device specific ioctl calls. + */ + +static int i2ob_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct i2ob_device *dev; + int minor; + + /* Anyone capable of this syscall can do *real bad* things */ + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (!inode) + return -EINVAL; + minor = MINOR(inode->i_rdev); + if (minor >= (MAX_I2OB<<4)) + return -ENODEV; + + dev = &i2ob_dev[minor]; + switch (cmd) { + case BLKGETSIZE: + return put_user(i2ob[minor].nr_sects, (long *) arg); + + case HDIO_GETGEO: + { + struct hd_geometry g; + int u=minor&0xF0; + i2o_block_biosparam(i2ob_sizes[u]<<1, + &g.cylinders, &g.heads, &g.sectors); + g.start = i2ob[minor].start_sect; + return copy_to_user((void *)arg,&g, sizeof(g))?-EFAULT:0; + } + + case BLKRRPART: + if(!capable(CAP_SYS_ADMIN)) + return -EACCES; + return do_i2ob_revalidate(inode->i_rdev,1); + + case BLKFLSBUF: + case BLKROSET: + case BLKROGET: + case BLKRASET: + case BLKRAGET: + case BLKPG: + return blk_ioctl(inode->i_rdev, cmd, arg); + + default: + return -EINVAL; + } +} + +/* + * Close the block device down + */ + +static int i2ob_release(struct inode *inode, struct file *file) +{ + struct i2ob_device *dev; + int minor; + + minor = MINOR(inode->i_rdev); + if (minor >= (MAX_I2OB<<4)) + return -ENODEV; + dev = &i2ob_dev[(minor&0xF0)]; + + /* + * This is to deail with the case of an application + * opening a device and then the device dissapears while + * it's in use, and then the application tries to release + * it. ex: Unmounting a deleted RAID volume at reboot. + * If we send messages, it will just cause FAILs since + * the TID no longer exists. + */ + if(!dev->i2odev) + return 0; + + /* Sync the device so we don't get errors */ + fsync_dev(inode->i_rdev); + + if (dev->refcnt <= 0) + printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt); + dev->refcnt--; + if(dev->refcnt==0) + { + /* + * Flush the onboard cache on unmount + */ + u32 msg[5]; + int *query_done = &dev->done_flag; + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid; + msg[2] = i2ob_context|0x40000000; + msg[3] = (u32)query_done; + msg[4] = 60<<16; + DEBUG("Flushing..."); + i2o_post_wait(dev->controller, msg, 20, 60); + + /* + * Unlock the media + */ + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid; + msg[2] = i2ob_context|0x40000000; + msg[3] = (u32)query_done; + msg[4] = -1; + DEBUG("Unlocking..."); + i2o_post_wait(dev->controller, msg, 20, 2); + DEBUG("Unlocked.\n"); + + /* + * Now unclaim the device. + */ + + if (i2o_release_device(dev->i2odev, &i2o_block_handler)) + printk(KERN_ERR "i2ob_release: controller rejected unclaim.\n"); + + DEBUG("Unclaim\n"); + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Open the block device. + */ + +static int i2ob_open(struct inode *inode, struct file *file) +{ + int minor; + struct i2ob_device *dev; + + if (!inode) + return -EINVAL; + minor = MINOR(inode->i_rdev); + if (minor >= MAX_I2OB<<4) + return -ENODEV; + dev=&i2ob_dev[(minor&0xF0)]; + + if(!dev->i2odev) + return -ENODEV; + + if(dev->refcnt++==0) + { + u32 msg[6]; + + DEBUG("Claim "); + if(i2o_claim_device(dev->i2odev, &i2o_block_handler)) + { + dev->refcnt--; + printk(KERN_INFO "I2O Block: Could not open device\n"); + return -EBUSY; + } + DEBUG("Claimed "); + + /* + * Mount the media if needed. Note that we don't use + * the lock bit. Since we have to issue a lock if it + * refuses a mount (quite possible) then we might as + * well just send two messages out. + */ + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_MMOUNT<<24|HOST_TID<<12|dev->tid; + msg[4] = -1; + msg[5] = 0; + DEBUG("Mount "); + i2o_post_wait(dev->controller, msg, 24, 2); + + /* + * Lock the media + */ + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_MLOCK<<24|HOST_TID<<12|dev->tid; + msg[4] = -1; + DEBUG("Lock "); + i2o_post_wait(dev->controller, msg, 20, 2); + DEBUG("Ready.\n"); + } + MOD_INC_USE_COUNT; + return 0; +} + +/* + * Issue a device query + */ + +static int i2ob_query_device(struct i2ob_device *dev, int table, + int field, void *buf, int buflen) +{ + return i2o_query_scalar(dev->controller, dev->tid, + table, field, buf, buflen); +} + + +/* + * Install the I2O block device we found. + */ + +static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, int unit) +{ + u64 size; + u32 blocksize; + u32 limit; + u8 type; + u32 flags, status; + struct i2ob_device *dev=&i2ob_dev[unit]; + int i; + + /* + * For logging purposes... + */ + printk(KERN_INFO "i2ob: Installing tid %d device at unit %d\n", + d->lct_data.tid, unit); + + /* + * Ask for the current media data. If that isn't supported + * then we ask for the device capacity data + */ + if(i2ob_query_device(dev, 0x0004, 1, &blocksize, 4) != 0 + || i2ob_query_device(dev, 0x0004, 0, &size, 8) !=0 ) + { + i2ob_query_device(dev, 0x0000, 3, &blocksize, 4); + i2ob_query_device(dev, 0x0000, 4, &size, 8); + } + + i2ob_query_device(dev, 0x0000, 5, &flags, 4); + i2ob_query_device(dev, 0x0000, 6, &status, 4); + i2ob_sizes[unit] = (int)(size>>10); + for(i=unit; i <= unit+15 ; i++) + i2ob_hardsizes[i] = blocksize; + i2ob_gendisk.part[unit].nr_sects = size>>9; + i2ob[unit].nr_sects = (int)(size>>9); + + /* Set limit based on inbound frame size */ + limit = (d->controller->status_block->inbound_frame_size - 8)/2; + limit = limit<<9; + + /* + * Max number of Scatter-Gather Elements + */ + + for(i=unit;i<=unit+15;i++) + { + if(d->controller->type == I2O_TYPE_PCI && d->controller->bus.pci.queue_buggy) + { + i2ob_max_sectors[i] = 32; + i2ob_dev[i].max_segments = 8; + i2ob_dev[i].depth = 4; + } + else if(d->controller->type == I2O_TYPE_PCI && d->controller->bus.pci.short_req) + { + i2ob_max_sectors[i] = 8; + i2ob_dev[i].max_segments = 8; + } + else + { + /* MAX_SECTORS was used but 255 is a dumb number for + striped RAID */ + i2ob_max_sectors[i]=256; + i2ob_dev[i].max_segments = (d->controller->status_block->inbound_frame_size - 8)/2; + } + } + + printk(KERN_INFO "Max segments set to %d\n", + i2ob_dev[unit].max_segments); + printk(KERN_INFO "Byte limit is %d.\n", limit); + + i2ob_query_device(dev, 0x0000, 0, &type, 1); + + sprintf(d->dev_name, "%s%c", i2ob_gendisk.major_name, 'a' + (unit>>4)); + + printk(KERN_INFO "%s: ", d->dev_name); + switch(type) + { + case 0: printk("Disk Storage");break; + case 4: printk("WORM");break; + case 5: printk("CD-ROM");break; + case 7: printk("Optical device");break; + default: + printk("Type %d", type); + } + if(status&(1<<10)) + printk("(RAID)"); + if(((flags & (1<<3)) && !(status & (1<<3))) || + ((flags & (1<<4)) && !(status & (1<<4)))) + { + printk(KERN_INFO " Not loaded.\n"); + return 1; + } + printk("- %dMb, %d byte sectors", + (int)(size>>20), blocksize); + if(status&(1<<0)) + { + u32 cachesize; + i2ob_query_device(dev, 0x0003, 0, &cachesize, 4); + cachesize>>=10; + if(cachesize>4095) + printk(", %dMb cache", cachesize>>10); + else + printk(", %dKb cache", cachesize); + + } + printk(".\n"); + printk(KERN_INFO "%s: Maximum sectors/read set to %d.\n", + d->dev_name, i2ob_max_sectors[unit]); + + /* + * If this is the first I2O block device found on this IOP, + * we need to initialize all the queue data structures + * before any I/O can be performed. If it fails, this + * device is useless. + */ + if(!i2ob_queues[c->unit]) { + if(i2ob_init_iop(c->unit)) + return 1; + } + + /* + * This will save one level of lookup/indirection in critical + * code so that we can directly get the queue ptr from the + * device instead of having to go the IOP data structure. + */ + dev->req_queue = &i2ob_queues[c->unit]->req_queue; + + grok_partitions(&i2ob_gendisk, unit>>4, 1<<4, (long)(size>>9)); + + /* + * Register for the events we're interested in and that the + * device actually supports. + */ + i2o_event_register(c, d->lct_data.tid, i2ob_context, unit, + (I2OB_EVENT_MASK & d->lct_data.event_capabilities)); + + return 0; +} + +/* + * Initialize IOP specific queue structures. This is called + * once for each IOP that has a block device sitting behind it. + */ +static int i2ob_init_iop(unsigned int unit) +{ + int i; + + i2ob_queues[unit] = (struct i2ob_iop_queue*) + kmalloc(sizeof(struct i2ob_iop_queue), GFP_ATOMIC); + if(!i2ob_queues[unit]) + { + printk(KERN_WARNING + "Could not allocate request queue for I2O block device!\n"); + return -1; + } + + for(i = 0; i< MAX_I2OB_DEPTH; i++) + { + i2ob_queues[unit]->request_queue[i].next = + &i2ob_queues[unit]->request_queue[i+1]; + i2ob_queues[unit]->request_queue[i].num = i; + } + + /* Queue is MAX_I2OB + 1... */ + i2ob_queues[unit]->request_queue[i].next = NULL; + i2ob_queues[unit]->i2ob_qhead = &i2ob_queues[unit]->request_queue[0]; + atomic_set(&i2ob_queues[unit]->queue_depth, 0); + + blk_init_queue(&i2ob_queues[unit]->req_queue, i2ob_request); + blk_queue_headactive(&i2ob_queues[unit]->req_queue, 0); + i2ob_queues[unit]->req_queue.back_merge_fn = i2ob_back_merge; + i2ob_queues[unit]->req_queue.front_merge_fn = i2ob_front_merge; + i2ob_queues[unit]->req_queue.merge_requests_fn = i2ob_merge_requests; + i2ob_queues[unit]->req_queue.queuedata = &i2ob_queues[unit]; + + return 0; +} + +/* + * Get the request queue for the given device. + */ +static request_queue_t* i2ob_get_queue(kdev_t dev) +{ + int unit = MINOR(dev)&0xF0; + + return i2ob_dev[unit].req_queue; +} + +/* + * Probe the I2O subsytem for block class devices + */ +static void i2ob_scan(int bios) +{ + int i; + int warned = 0; + + struct i2o_device *d, *b=NULL; + struct i2o_controller *c; + struct i2ob_device *dev; + + for(i=0; i< MAX_I2O_CONTROLLERS; i++) + { + c=i2o_find_controller(i); + + if(c==NULL) + continue; + + /* + * The device list connected to the I2O Controller is doubly linked + * Here we traverse the end of the list , and start claiming devices + * from that end. This assures that within an I2O controller atleast + * the newly created volumes get claimed after the older ones, thus + * mapping to same major/minor (and hence device file name) after + * every reboot. + * The exception being: + * 1. If there was a TID reuse. + * 2. There was more than one I2O controller. + */ + + if(!bios) + { + for (d=c->devices;d!=NULL;d=d->next) + if(d->next == NULL) + b = d; + } + else + b = c->devices; + + while(b != NULL) + { + d=b; + if(bios) + b = b->next; + else + b = b->prev; + + if(d->lct_data.class_id!=I2O_CLASS_RANDOM_BLOCK_STORAGE) + continue; + + if(d->lct_data.user_tid != 0xFFF) + continue; + + if(bios) + { + if(d->lct_data.bios_info != 0x80) + continue; + printk(KERN_INFO "Claiming as Boot device: Controller %d, TID %d\n", c->unit, d->lct_data.tid); + } + else + { + if(d->lct_data.bios_info == 0x80) + continue; /*Already claimed on pass 1 */ + } + + if(i2o_claim_device(d, &i2o_block_handler)) + { + printk(KERN_WARNING "i2o_block: Controller %d, TID %d\n", c->unit, + d->lct_data.tid); + printk(KERN_WARNING "\t%sevice refused claim! Skipping installation\n", bios?"Boot d":"D"); + continue; + } + + if(scan_uniti2odev = d; + dev->controller = c; + dev->unit = c->unit; + dev->tid = d->lct_data.tid; + + if(i2ob_install_device(c,d,scan_unit)) + printk(KERN_WARNING "Could not install I2O block device\n"); + else + { + scan_unit+=16; + i2ob_dev_count++; + + /* We want to know when device goes away */ + i2o_device_notify_on(d, &i2o_block_handler); + } + } + else + { + if(!warned++) + printk(KERN_WARNING "i2o_block: too many device, registering only %d.\n", scan_unit>>4); + } + i2o_release_device(d, &i2o_block_handler); + } + i2o_unlock_controller(c); + } +} + +static void i2ob_probe(void) +{ + /* + * Some overhead/redundancy involved here, while trying to + * claim the first boot volume encountered as /dev/i2o/hda + * everytime. All the i2o_controllers are searched and the + * first i2o block device marked as bootable is claimed + * If an I2O block device was booted off , the bios sets + * its bios_info field to 0x80, this what we search for. + * Assuming that the bootable volume is /dev/i2o/hda + * everytime will prevent any kernel panic while mounting + * root partition + */ + + printk(KERN_INFO "i2o_block: Checking for Boot device...\n"); + i2ob_scan(1); + + /* + * Now the remainder. + */ + printk(KERN_INFO "i2o_block: Checking for I2O Block devices...\n"); + i2ob_scan(0); +} + + +/* + * New device notification handler. Called whenever a new + * I2O block storage device is added to the system. + * + * Should we spin lock around this to keep multiple devs from + * getting updated at the same time? + * + */ +void i2ob_new_device(struct i2o_controller *c, struct i2o_device *d) +{ + struct i2ob_device *dev; + int unit = 0; + + printk(KERN_INFO "i2o_block: New device detected\n"); + printk(KERN_INFO " Controller %d Tid %d\n",c->unit, d->lct_data.tid); + + /* Check for available space */ + if(i2ob_dev_count>=MAX_I2OB<<4) + { + printk(KERN_ERR "i2o_block: No more devices allowed!\n"); + return; + } + for(unit = 0; unit < (MAX_I2OB<<4); unit += 16) + { + if(!i2ob_dev[unit].i2odev) + break; + } + + if(i2o_claim_device(d, &i2o_block_handler)) + { + printk(KERN_INFO + "i2o_block: Unable to claim device. Installation aborted\n"); + return; + } + + dev = &i2ob_dev[unit]; + dev->i2odev = d; + dev->controller = c; + dev->tid = d->lct_data.tid; + + if(i2ob_install_device(c,d,unit)) + printk(KERN_ERR "i2o_block: Could not install new device\n"); + else + { + i2ob_dev_count++; + i2o_device_notify_on(d, &i2o_block_handler); + } + + i2o_release_device(d, &i2o_block_handler); + + return; +} + +/* + * Deleted device notification handler. Called when a device we + * are talking to has been deleted by the user or some other + * mysterious fource outside the kernel. + */ +void i2ob_del_device(struct i2o_controller *c, struct i2o_device *d) +{ + int unit = 0; + int i = 0; + int flags; + + spin_lock_irqsave(&io_request_lock, flags); + + /* + * Need to do this...we somtimes get two events from the IRTOS + * in a row and that causes lots of problems. + */ + i2o_device_notify_off(d, &i2o_block_handler); + + printk(KERN_INFO "I2O Block Device Deleted\n"); + + for(unit = 0; unit < MAX_I2OB<<4; unit += 16) + { + if(i2ob_dev[unit].i2odev == d) + { + printk(KERN_INFO " /dev/%s: Controller %d Tid %d\n", + d->dev_name, c->unit, d->lct_data.tid); + break; + } + } + if(unit >= MAX_I2OB<<4) + { + printk(KERN_ERR "i2ob_del_device called, but not in dev table!\n"); + spin_unlock_irqrestore(&io_request_lock, flags); + return; + } + + /* + * This will force errors when i2ob_get_queue() is called + * by the kenrel. + */ + i2ob_dev[unit].req_queue = NULL; + for(i = unit; i <= unit+15; i++) + { + i2ob_dev[i].i2odev = NULL; + i2ob_sizes[i] = 0; + i2ob_hardsizes[i] = 0; + i2ob_max_sectors[i] = 0; + i2ob[i].nr_sects = 0; + i2ob_gendisk.part[i].nr_sects = 0; + } + spin_unlock_irqrestore(&io_request_lock, flags); + + /* + * Sync the device...this will force all outstanding I/Os + * to attempt to complete, thus causing error messages. + * We have to do this as the user could immediatelly create + * a new volume that gets assigned the same minor number. + * If there are still outstanding writes to the device, + * that could cause data corruption on the new volume! + * + * The truth is that deleting a volume that you are currently + * accessing will do _bad things_ to your system. This + * handler will keep it from crashing, but must probably + * you'll have to do a 'reboot' to get the system running + * properly. Deleting disks you are using is dumb. + * Umount them first and all will be good! + * + * It's not this driver's job to protect the system from + * dumb user mistakes :) + */ + if(i2ob_dev[unit].refcnt) + fsync_dev(MKDEV(MAJOR_NR,unit)); + + /* + * Decrease usage count for module + */ + while(i2ob_dev[unit].refcnt--) + MOD_DEC_USE_COUNT; + + i2ob_dev[unit].refcnt = 0; + + i2ob_dev[i].tid = 0; + + /* + * Do we need this? + * The media didn't really change...the device is just gone + */ + i2ob_media_change_flag[unit] = 1; + + i2ob_dev_count--; +} + +/* + * Have we seen a media change ? + */ +static int i2ob_media_change(kdev_t dev) +{ + int i=MINOR(dev); + i>>=4; + if(i2ob_media_change_flag[i]) + { + i2ob_media_change_flag[i]=0; + return 1; + } + return 0; +} + +static int i2ob_revalidate(kdev_t dev) +{ + return do_i2ob_revalidate(dev, 0); +} + +/* + * Reboot notifier. This is called by i2o_core when the system + * shuts down. + */ +static void i2ob_reboot_event(void) +{ + int i; + + for(i=0;irefcnt!=0) + { + /* + * Flush the onboard cache + */ + u32 msg[5]; + int *query_done = &dev->done_flag; + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid; + msg[2] = i2ob_context|0x40000000; + msg[3] = (u32)query_done; + msg[4] = 60<<16; + + DEBUG("Flushing..."); + i2o_post_wait(dev->controller, msg, 20, 60); + + DEBUG("Unlocking..."); + /* + * Unlock the media + */ + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid; + msg[2] = i2ob_context|0x40000000; + msg[3] = (u32)query_done; + msg[4] = -1; + i2o_post_wait(dev->controller, msg, 20, 2); + + DEBUG("Unlocked.\n"); + } + } +} + +static struct block_device_operations i2ob_fops = +{ + open: i2ob_open, + release: i2ob_release, + ioctl: i2ob_ioctl, + check_media_change: i2ob_media_change, + revalidate: i2ob_revalidate, +}; + +static struct gendisk i2ob_gendisk = +{ + MAJOR_NR, + "i2o/hd", + 4, + 1<<4, + i2ob, + i2ob_sizes, + MAX_I2OB, + NULL, + NULL, + &i2ob_fops, +}; + + +/* + * And here should be modules and kernel interface + * (Just smiley confuses emacs :-) + */ + +#ifdef MODULE +#define i2o_block_init init_module +#endif + +int i2o_block_init(void) +{ + int i; + + printk(KERN_INFO "I2O Block Storage OSM v0.9\n"); + printk(KERN_INFO " (c) Copyright 1999-2001 Red Hat Software.\n"); + + /* + * Register the block device interfaces + */ + + if (register_blkdev(MAJOR_NR, "i2o_block", &i2ob_fops)) { + printk(KERN_ERR "Unable to get major number %d for i2o_block\n", + MAJOR_NR); + return -EIO; + } +#ifdef MODULE + printk(KERN_INFO "i2o_block: registered device at major %d\n", MAJOR_NR); +#endif + + /* + * Now fill in the boiler plate + */ + + blksize_size[MAJOR_NR] = i2ob_blksizes; + hardsect_size[MAJOR_NR] = i2ob_hardsizes; + blk_size[MAJOR_NR] = i2ob_sizes; + max_sectors[MAJOR_NR] = i2ob_max_sectors; + blk_dev[MAJOR_NR].queue = i2ob_get_queue; + + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), i2ob_request); + blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR), 0); + + for (i = 0; i < MAX_I2OB << 4; i++) { + i2ob_dev[i].refcnt = 0; + i2ob_dev[i].flags = 0; + i2ob_dev[i].controller = NULL; + i2ob_dev[i].i2odev = NULL; + i2ob_dev[i].tid = 0; + i2ob_dev[i].head = NULL; + i2ob_dev[i].tail = NULL; + i2ob_dev[i].depth = MAX_I2OB_DEPTH; + i2ob_blksizes[i] = 1024; + i2ob_max_sectors[i] = 2; + } + + /* + * Set up the queue + */ + for(i = 0; i < MAX_I2O_CONTROLLERS; i++) + { + i2ob_queues[i] = NULL; + } + + /* + * Timers + */ + + init_timer(&i2ob_timer); + i2ob_timer.function = i2ob_timer_handler; + i2ob_timer.data = 0; + + /* + * Register the OSM handler as we will need this to probe for + * drives, geometry and other goodies. + */ + + if(i2o_install_handler(&i2o_block_handler)<0) + { + unregister_blkdev(MAJOR_NR, "i2o_block"); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + printk(KERN_ERR "i2o_block: unable to register OSM.\n"); + return -EINVAL; + } + i2ob_context = i2o_block_handler.context; + + /* + * Initialize event handling thread + */ + init_MUTEX_LOCKED(&i2ob_evt_sem); + evt_pid = kernel_thread(i2ob_evt, NULL, CLONE_SIGHAND); + if(evt_pid < 0) + { + printk(KERN_ERR + "i2o_block: Could not initialize event thread. Aborting\n"); + i2o_remove_handler(&i2o_block_handler); + return 0; + } + + /* + * Finally see what is actually plugged in to our controllers + */ + for (i = 0; i < MAX_I2OB; i++) + register_disk(&i2ob_gendisk, MKDEV(MAJOR_NR,i<<4), 1<<4, + &i2ob_fops, 0); + i2ob_probe(); + + /* + * Adding i2ob_gendisk into the gendisk list. + */ + i2ob_gendisk.next = gendisk_head; + gendisk_head = &i2ob_gendisk; + + return 0; +} + +#ifdef MODULE + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Red Hat Software"); +MODULE_DESCRIPTION("I2O Block Device OSM"); + +void cleanup_module(void) +{ + struct gendisk *gdp; + int i; + + if(evt_running) { + printk(KERN_INFO "Killing I2O block threads..."); + i = kill_proc(evt_pid, SIGTERM, 1); + if(!i) { + printk("waiting..."); + } + /* Be sure it died */ + down(&i2ob_thread_dead); + printk("done.\n"); + } + + /* + * Unregister for updates from any devices..otherwise we still + * get them and the core jumps to random memory :O + */ + if(i2ob_dev_count) { + struct i2o_device *d; + for(i = 0; i < MAX_I2OB; i++) + if((d=i2ob_dev[i<<4].i2odev)) { + i2o_device_notify_off(d, &i2o_block_handler); + i2o_event_register(d->controller, d->lct_data.tid, + i2ob_context, i<<4, 0); + } + } + + /* + * We may get further callbacks for ourself. The i2o_core + * code handles this case reasonably sanely. The problem here + * is we shouldn't get them .. but a couple of cards feel + * obliged to tell us stuff we dont care about. + * + * This isnt ideal at all but will do for now. + */ + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ); + + /* + * Flush the OSM + */ + + i2o_remove_handler(&i2o_block_handler); + + /* + * Return the block device + */ + if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0) + printk("i2o_block: cleanup_module failed\n"); + + /* + * free request queue + */ + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + + /* + * Why isnt register/unregister gendisk in the kernel ??? + */ + + if (gendisk_head == &i2ob_gendisk) { + gendisk_head = i2ob_gendisk.next; + } + else { + for (gdp = gendisk_head; gdp; gdp = gdp->next) + if (gdp->next == &i2ob_gendisk) + { + gdp->next = i2ob_gendisk.next; + break; + } + } +} +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_config.c linux.ac/drivers/message/i2o/i2o_config.c --- linux.vanilla/drivers/message/i2o/i2o_config.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_config.c Sat Apr 21 20:47:40 2001 @@ -0,0 +1,971 @@ +/* + * I2O Configuration Interface Driver + * + * (C) Copyright 1999 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * + * Modified 04/20/1999 by Deepak Saxena + * - Added basic ioctl() support + * Modified 06/07/1999 by Deepak Saxena + * - Added software download ioctl (still testing) + * Modified 09/10/1999 by Auvo Häkkinen + * - Changes to i2o_cfg_reply(), ioctl_parms() + * - Added ioct_validate() + * Modified 09/30/1999 by Taneli Vähäkangas + * - Fixed ioctl_swdl() + * Modified 10/04/1999 by Taneli Vähäkangas + * - Changed ioctl_swdl(), implemented ioctl_swul() and ioctl_swdel() + * Modified 11/18/199 by Deepak Saxena + * - Added event managmenet support + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int i2o_cfg_context = -1; +static void *page_buf; +static spinlock_t i2o_config_lock = SPIN_LOCK_UNLOCKED; +struct wait_queue *i2o_wait_queue; + +#define MODINC(x,y) (x = x++ % y) + +struct i2o_cfg_info +{ + struct file* fp; + struct fasync_struct *fasync; + struct i2o_evt_info event_q[I2O_EVT_Q_LEN]; + u16 q_in; // Queue head index + u16 q_out; // Queue tail index + u16 q_len; // Queue length + u16 q_lost; // Number of lost events + u32 q_id; // Event queue ID...used as tx_context + struct i2o_cfg_info *next; +}; +static struct i2o_cfg_info *open_files = NULL; +static int i2o_cfg_info_id = 0; + +static int ioctl_getiops(unsigned long); +static int ioctl_gethrt(unsigned long); +static int ioctl_getlct(unsigned long); +static int ioctl_parms(unsigned long, unsigned int); +static int ioctl_html(unsigned long); +static int ioctl_swdl(unsigned long); +static int ioctl_swul(unsigned long); +static int ioctl_swdel(unsigned long); +static int ioctl_validate(unsigned long); +static int ioctl_evt_reg(unsigned long, struct file *); +static int ioctl_evt_get(unsigned long, struct file *); +static int cfg_fasync(int, struct file*, int); + +/* + * This is the callback for any message we have posted. The message itself + * will be returned to the message pool when we return from the IRQ + * + * This runs in irq context so be short and sweet. + */ +static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *m) +{ + u32 *msg = (u32 *)m; + + if (msg[0] & MSG_FAIL) { + u32 *preserved_msg = (u32*)(c->mem_offset + msg[7]); + + printk(KERN_ERR "i2o_config: IOP failed to process the msg.\n"); + + /* Release the preserved msg frame by resubmitting it as a NOP */ + + preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; + preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; + preserved_msg[2] = 0; + i2o_post_message(c, msg[7]); + } + + if (msg[4] >> 24) // ReqStatus != SUCCESS + i2o_report_status(KERN_INFO,"i2o_config", msg); + + if(m->function == I2O_CMD_UTIL_EVT_REGISTER) + { + struct i2o_cfg_info *inf; + + for(inf = open_files; inf; inf = inf->next) + if(inf->q_id == msg[3]) + break; + + // + // If this is the case, it means that we're getting + // events for a file descriptor that's been close()'d + // w/o the user unregistering for events first. + // The code currently assumes that the user will + // take care of unregistering for events before closing + // a file. + // + // TODO: + // Should we track event registartion and deregister + // for events when a file is close()'d so this doesn't + // happen? That would get rid of the search through + // the linked list since file->private_data could point + // directly to the i2o_config_info data structure...but + // it would mean having all sorts of tables to track + // what each file is registered for...I think the + // current method is simpler. - DS + // + if(!inf) + return; + + inf->event_q[inf->q_in].id.iop = c->unit; + inf->event_q[inf->q_in].id.tid = m->target_tid; + inf->event_q[inf->q_in].id.evt_mask = msg[4]; + + // + // Data size = msg size - reply header + // + inf->event_q[inf->q_in].data_size = (m->size - 5) * 4; + if(inf->event_q[inf->q_in].data_size) + memcpy(inf->event_q[inf->q_in].evt_data, + (unsigned char *)(msg + 5), + inf->event_q[inf->q_in].data_size); + + spin_lock(&i2o_config_lock); + MODINC(inf->q_in, I2O_EVT_Q_LEN); + if(inf->q_len == I2O_EVT_Q_LEN) + { + MODINC(inf->q_out, I2O_EVT_Q_LEN); + inf->q_lost++; + } + else + { + // Keep I2OEVTGET on another CPU from touching this + inf->q_len++; + } + spin_unlock(&i2o_config_lock); + + +// printk(KERN_INFO "File %p w/id %d has %d events\n", +// inf->fp, inf->q_id, inf->q_len); + + kill_fasync(&inf->fasync, SIGIO, POLL_IN); + } + + return; +} + +/* + * Each of these describes an i2o message handler. They are + * multiplexed by the i2o_core code + */ + +struct i2o_handler cfg_handler= +{ + i2o_cfg_reply, + NULL, + NULL, + NULL, + "Configuration", + 0, + 0xffffffff // All classes +}; + +static long long cfg_llseek(struct file *file, long long offset, int origin) +{ + return -ESPIPE; +} + + +static ssize_t cfg_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + printk(KERN_INFO "i2o_config write not yet supported\n"); + + return 0; +} + + +static ssize_t cfg_read(struct file *file, char *buf, size_t count, loff_t *ptr) +{ + return 0; +} + +/* + * IOCTL Handler + */ +static int cfg_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, + unsigned long arg) +{ + int ret; + + switch(cmd) + { + case I2OGETIOPS: + ret = ioctl_getiops(arg); + break; + + case I2OHRTGET: + ret = ioctl_gethrt(arg); + break; + + case I2OLCTGET: + ret = ioctl_getlct(arg); + break; + + case I2OPARMSET: + ret = ioctl_parms(arg, I2OPARMSET); + break; + + case I2OPARMGET: + ret = ioctl_parms(arg, I2OPARMGET); + break; + + case I2OSWDL: + ret = ioctl_swdl(arg); + break; + + case I2OSWUL: + ret = ioctl_swul(arg); + break; + + case I2OSWDEL: + ret = ioctl_swdel(arg); + break; + + case I2OVALIDATE: + ret = ioctl_validate(arg); + break; + + case I2OHTML: + ret = ioctl_html(arg); + break; + + case I2OEVTREG: + ret = ioctl_evt_reg(arg, fp); + break; + + case I2OEVTGET: + ret = ioctl_evt_get(arg, fp); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +int ioctl_getiops(unsigned long arg) +{ + u8 *user_iop_table = (u8*)arg; + struct i2o_controller *c = NULL; + int i; + u8 foo[MAX_I2O_CONTROLLERS]; + + if(!access_ok(VERIFY_WRITE, user_iop_table, MAX_I2O_CONTROLLERS)) + return -EFAULT; + + for(i = 0; i < MAX_I2O_CONTROLLERS; i++) + { + c = i2o_find_controller(i); + if(c) + { + foo[i] = 1; + i2o_unlock_controller(c); + } + else + { + foo[i] = 0; + } + } + + __copy_to_user(user_iop_table, foo, MAX_I2O_CONTROLLERS); + return 0; +} + +int ioctl_gethrt(unsigned long arg) +{ + struct i2o_controller *c; + struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg; + struct i2o_cmd_hrtlct kcmd; + i2o_hrt *hrt; + int len; + u32 reslen; + int ret = 0; + + if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) + return -EFAULT; + + if(get_user(reslen, kcmd.reslen) < 0) + return -EFAULT; + + if(kcmd.resbuf == NULL) + return -EFAULT; + + c = i2o_find_controller(kcmd.iop); + if(!c) + return -ENXIO; + + hrt = (i2o_hrt *)c->hrt; + + i2o_unlock_controller(c); + + len = 8 + ((hrt->entry_len * hrt->num_entries) << 2); + + /* We did a get user...so assuming mem is ok...is this bad? */ + put_user(len, kcmd.reslen); + if(len > reslen) + ret = -ENOBUFS; + if(copy_to_user(kcmd.resbuf, (void*)hrt, len)) + ret = -EFAULT; + + return ret; +} + +int ioctl_getlct(unsigned long arg) +{ + struct i2o_controller *c; + struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg; + struct i2o_cmd_hrtlct kcmd; + i2o_lct *lct; + int len; + int ret = 0; + u32 reslen; + + if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) + return -EFAULT; + + if(get_user(reslen, kcmd.reslen) < 0) + return -EFAULT; + + if(kcmd.resbuf == NULL) + return -EFAULT; + + c = i2o_find_controller(kcmd.iop); + if(!c) + return -ENXIO; + + lct = (i2o_lct *)c->lct; + i2o_unlock_controller(c); + + len = (unsigned int)lct->table_size << 2; + put_user(len, kcmd.reslen); + if(len > reslen) + ret = -ENOBUFS; + else if(copy_to_user(kcmd.resbuf, (void*)lct, len)) + ret = -EFAULT; + + return ret; +} + +static int ioctl_parms(unsigned long arg, unsigned int type) +{ + int ret = 0; + struct i2o_controller *c; + struct i2o_cmd_psetget *cmd = (struct i2o_cmd_psetget*)arg; + struct i2o_cmd_psetget kcmd; + u32 reslen; + u8 *ops; + u8 *res; + int len; + + u32 i2o_cmd = (type == I2OPARMGET ? + I2O_CMD_UTIL_PARAMS_GET : + I2O_CMD_UTIL_PARAMS_SET); + + if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_psetget))) + return -EFAULT; + + if(get_user(reslen, kcmd.reslen)) + return -EFAULT; + + c = i2o_find_controller(kcmd.iop); + if(!c) + return -ENXIO; + + ops = (u8*)kmalloc(kcmd.oplen, GFP_KERNEL); + if(!ops) + { + i2o_unlock_controller(c); + return -ENOMEM; + } + + if(copy_from_user(ops, kcmd.opbuf, kcmd.oplen)) + { + i2o_unlock_controller(c); + kfree(ops); + return -EFAULT; + } + + /* + * It's possible to have a _very_ large table + * and that the user asks for all of it at once... + */ + res = (u8*)kmalloc(65536, GFP_KERNEL); + if(!res) + { + i2o_unlock_controller(c); + kfree(ops); + return -ENOMEM; + } + + len = i2o_issue_params(i2o_cmd, c, kcmd.tid, + ops, kcmd.oplen, res, 65536); + i2o_unlock_controller(c); + kfree(ops); + + if (len < 0) { + kfree(res); + return -EAGAIN; + } + + put_user(len, kcmd.reslen); + if(len > reslen) + ret = -ENOBUFS; + else if(copy_to_user(cmd->resbuf, res, len)) + ret = -EFAULT; + + kfree(res); + + return ret; +} + +int ioctl_html(unsigned long arg) +{ + struct i2o_html *cmd = (struct i2o_html*)arg; + struct i2o_html kcmd; + struct i2o_controller *c; + u8 *res = NULL; + void *query = NULL; + int ret = 0; + int token; + u32 len; + u32 reslen; + u32 msg[MSG_FRAME_SIZE/4]; + + if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_html))) + { + printk(KERN_INFO "i2o_config: can't copy html cmd\n"); + return -EFAULT; + } + + if(get_user(reslen, kcmd.reslen) < 0) + { + printk(KERN_INFO "i2o_config: can't copy html reslen\n"); + return -EFAULT; + } + + if(!kcmd.resbuf) + { + printk(KERN_INFO "i2o_config: NULL html buffer\n"); + return -EFAULT; + } + + c = i2o_find_controller(kcmd.iop); + if(!c) + return -ENXIO; + + if(kcmd.qlen) /* Check for post data */ + { + query = kmalloc(kcmd.qlen, GFP_KERNEL); + if(!query) + { + i2o_unlock_controller(c); + return -ENOMEM; + } + if(copy_from_user(query, kcmd.qbuf, kcmd.qlen)) + { + i2o_unlock_controller(c); + printk(KERN_INFO "i2o_config: could not get query\n"); + kfree(query); + return -EFAULT; + } + } + + res = kmalloc(65536, GFP_KERNEL); + if(!res) + { + i2o_unlock_controller(c); + kfree(query); + return -ENOMEM; + } + + msg[1] = (I2O_CMD_UTIL_CONFIG_DIALOG << 24)|HOST_TID<<12|kcmd.tid; + msg[2] = i2o_cfg_context; + msg[3] = 0; + msg[4] = kcmd.page; + msg[5] = 0xD0000000|65536; + msg[6] = virt_to_bus(res); + if(!kcmd.qlen) /* Check for post data */ + msg[0] = SEVEN_WORD_MSG_SIZE|SGL_OFFSET_5; + else + { + msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_5; + msg[5] = 0x50000000|65536; + msg[7] = 0xD4000000|(kcmd.qlen); + msg[8] = virt_to_bus(query); + } + /* + Wait for a considerable time till the Controller + does its job before timing out. The controller might + take more time to process this request if there are + many devices connected to it. + */ + token = i2o_post_wait_mem(c, msg, 9*4, 400, query, res); + if(token < 0) + { + printk(KERN_DEBUG "token = %#10x\n", token); + i2o_unlock_controller(c); + + if(token != -ETIMEDOUT) + { + kfree(res); + if(kcmd.qlen) kfree(query); + } + + return token; + } + i2o_unlock_controller(c); + + len = strnlen(res, 65536); + put_user(len, kcmd.reslen); + if(len > reslen) + ret = -ENOMEM; + if(copy_to_user(kcmd.resbuf, res, len)) + ret = -EFAULT; + + kfree(res); + if(kcmd.qlen) + kfree(query); + + return ret; +} + +int ioctl_swdl(unsigned long arg) +{ + struct i2o_sw_xfer kxfer; + struct i2o_sw_xfer *pxfer = (struct i2o_sw_xfer *)arg; + unsigned char maxfrag = 0, curfrag = 1; + unsigned char *buffer; + u32 msg[9]; + unsigned int status = 0, swlen = 0, fragsize = 8192; + struct i2o_controller *c; + + if(copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) + return -EFAULT; + + if(get_user(swlen, kxfer.swlen) < 0) + return -EFAULT; + + if(get_user(maxfrag, kxfer.maxfrag) < 0) + return -EFAULT; + + if(get_user(curfrag, kxfer.curfrag) < 0) + return -EFAULT; + + if(curfrag==maxfrag) fragsize = swlen-(maxfrag-1)*8192; + + if(!kxfer.buf || !access_ok(VERIFY_READ, kxfer.buf, fragsize)) + return -EFAULT; + + c = i2o_find_controller(kxfer.iop); + if(!c) + return -ENXIO; + + buffer=kmalloc(fragsize, GFP_KERNEL); + if (buffer==NULL) + { + i2o_unlock_controller(c); + return -ENOMEM; + } + __copy_from_user(buffer, kxfer.buf, fragsize); + + msg[0]= NINE_WORD_MSG_SIZE | SGL_OFFSET_7; + msg[1]= I2O_CMD_SW_DOWNLOAD<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[2]= (u32)cfg_handler.context; + msg[3]= 0; + msg[4]= (((u32)kxfer.flags)<<24) | (((u32)kxfer.sw_type)<<16) | + (((u32)maxfrag)<<8) | (((u32)curfrag)); + msg[5]= swlen; + msg[6]= kxfer.sw_id; + msg[7]= (0xD0000000 | fragsize); + msg[8]= virt_to_bus(buffer); + +// printk("i2o_config: swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); + status = i2o_post_wait_mem(c, msg, sizeof(msg), 60, buffer, NULL); + + i2o_unlock_controller(c); + if(status != -ETIMEDOUT) + kfree(buffer); + + if (status != I2O_POST_WAIT_OK) + { + // it fails if you try and send frags out of order + // and for some yet unknown reasons too + printk(KERN_INFO "i2o_config: swdl failed, DetailedStatus = %d\n", status); + return status; + } + + return 0; +} + +int ioctl_swul(unsigned long arg) +{ + struct i2o_sw_xfer kxfer; + struct i2o_sw_xfer *pxfer = (struct i2o_sw_xfer *)arg; + unsigned char maxfrag = 0, curfrag = 1; + unsigned char *buffer; + u32 msg[9]; + unsigned int status = 0, swlen = 0, fragsize = 8192; + struct i2o_controller *c; + + if(copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) + return -EFAULT; + + if(get_user(swlen, kxfer.swlen) < 0) + return -EFAULT; + + if(get_user(maxfrag, kxfer.maxfrag) < 0) + return -EFAULT; + + if(get_user(curfrag, kxfer.curfrag) < 0) + return -EFAULT; + + if(curfrag==maxfrag) fragsize = swlen-(maxfrag-1)*8192; + + if(!kxfer.buf || !access_ok(VERIFY_WRITE, kxfer.buf, fragsize)) + return -EFAULT; + + c = i2o_find_controller(kxfer.iop); + if(!c) + return -ENXIO; + + buffer=kmalloc(fragsize, GFP_KERNEL); + if (buffer==NULL) + { + i2o_unlock_controller(c); + return -ENOMEM; + } + + msg[0]= NINE_WORD_MSG_SIZE | SGL_OFFSET_7; + msg[1]= I2O_CMD_SW_UPLOAD<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[2]= (u32)cfg_handler.context; + msg[3]= 0; + msg[4]= (u32)kxfer.flags<<24|(u32)kxfer.sw_type<<16|(u32)maxfrag<<8|(u32)curfrag; + msg[5]= swlen; + msg[6]= kxfer.sw_id; + msg[7]= (0xD0000000 | fragsize); + msg[8]= virt_to_bus(buffer); + +// printk("i2o_config: swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); + status = i2o_post_wait_mem(c, msg, sizeof(msg), 60, buffer, NULL); + i2o_unlock_controller(c); + + if (status != I2O_POST_WAIT_OK) + { + if(status != -ETIMEDOUT) + kfree(buffer); + printk(KERN_INFO "i2o_config: swul failed, DetailedStatus = %d\n", status); + return status; + } + + __copy_to_user(kxfer.buf, buffer, fragsize); + kfree(buffer); + + return 0; +} + +int ioctl_swdel(unsigned long arg) +{ + struct i2o_controller *c; + struct i2o_sw_xfer kxfer, *pxfer = (struct i2o_sw_xfer *)arg; + u32 msg[7]; + unsigned int swlen; + int token; + + if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) + return -EFAULT; + + if (get_user(swlen, kxfer.swlen) < 0) + return -EFAULT; + + c = i2o_find_controller(kxfer.iop); + if (!c) + return -ENXIO; + + msg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0; + msg[1] = I2O_CMD_SW_REMOVE<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[2] = (u32)i2o_cfg_context; + msg[3] = 0; + msg[4] = (u32)kxfer.flags<<24 | (u32)kxfer.sw_type<<16; + msg[5] = swlen; + msg[6] = kxfer.sw_id; + + token = i2o_post_wait(c, msg, sizeof(msg), 10); + i2o_unlock_controller(c); + + if (token != I2O_POST_WAIT_OK) + { + printk(KERN_INFO "i2o_config: swdel failed, DetailedStatus = %d\n", token); + return -ETIMEDOUT; + } + + return 0; +} + +int ioctl_validate(unsigned long arg) +{ + int token; + int iop = (int)arg; + u32 msg[4]; + struct i2o_controller *c; + + c=i2o_find_controller(iop); + if (!c) + return -ENXIO; + + msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_CONFIG_VALIDATE<<24 | HOST_TID<<12 | iop; + msg[2] = (u32)i2o_cfg_context; + msg[3] = 0; + + token = i2o_post_wait(c, msg, sizeof(msg), 10); + i2o_unlock_controller(c); + + if (token != I2O_POST_WAIT_OK) + { + printk(KERN_INFO "Can't validate configuration, ErrorStatus = %d\n", + token); + return -ETIMEDOUT; + } + + return 0; +} + +static int ioctl_evt_reg(unsigned long arg, struct file *fp) +{ + u32 msg[5]; + struct i2o_evt_id *pdesc = (struct i2o_evt_id *)arg; + struct i2o_evt_id kdesc; + struct i2o_controller *iop; + struct i2o_device *d; + + if (copy_from_user(&kdesc, pdesc, sizeof(struct i2o_evt_id))) + return -EFAULT; + + /* IOP exists? */ + iop = i2o_find_controller(kdesc.iop); + if(!iop) + return -ENXIO; + i2o_unlock_controller(iop); + + /* Device exists? */ + for(d = iop->devices; d; d = d->next) + if(d->lct_data.tid == kdesc.tid) + break; + + if(!d) + return -ENODEV; + + msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_UTIL_EVT_REGISTER<<24 | HOST_TID<<12 | kdesc.tid; + msg[2] = (u32)i2o_cfg_context; + msg[3] = (u32)fp->private_data; + msg[4] = kdesc.evt_mask; + + i2o_post_this(iop, msg, 20); + + return 0; +} + +static int ioctl_evt_get(unsigned long arg, struct file *fp) +{ + u32 id = (u32)fp->private_data; + struct i2o_cfg_info *p = NULL; + struct i2o_evt_get *uget = (struct i2o_evt_get*)arg; + struct i2o_evt_get kget; + unsigned int flags; + + for(p = open_files; p; p = p->next) + if(p->q_id == id) + break; + + if(!p->q_len) + { + return -ENOENT; + return 0; + } + + memcpy(&kget.info, &p->event_q[p->q_out], sizeof(struct i2o_evt_info)); + MODINC(p->q_out, I2O_EVT_Q_LEN); + spin_lock_irqsave(&i2o_config_lock, flags); + p->q_len--; + kget.pending = p->q_len; + kget.lost = p->q_lost; + spin_unlock_irqrestore(&i2o_config_lock, flags); + + if(copy_to_user(uget, &kget, sizeof(struct i2o_evt_get))) + return -EFAULT; + return 0; +} + +static int cfg_open(struct inode *inode, struct file *file) +{ + struct i2o_cfg_info *tmp = + (struct i2o_cfg_info *)kmalloc(sizeof(struct i2o_cfg_info), GFP_KERNEL); + unsigned int flags; + + if(!tmp) + return -ENOMEM; + + file->private_data = (void*)(i2o_cfg_info_id++); + tmp->fp = file; + tmp->fasync = NULL; + tmp->q_id = (u32)file->private_data; + tmp->q_len = 0; + tmp->q_in = 0; + tmp->q_out = 0; + tmp->q_lost = 0; + tmp->next = open_files; + + spin_lock_irqsave(&i2o_config_lock, flags); + open_files = tmp; + spin_unlock_irqrestore(&i2o_config_lock, flags); + + return 0; +} + +static int cfg_release(struct inode *inode, struct file *file) +{ + u32 id = (u32)file->private_data; + struct i2o_cfg_info *p1, *p2; + unsigned int flags; + + lock_kernel(); + p1 = p2 = NULL; + + spin_lock_irqsave(&i2o_config_lock, flags); + for(p1 = open_files; p1; ) + { + if(p1->q_id == id) + { + + if(p1->fasync) + cfg_fasync(-1, file, 0); + if(p2) + p2->next = p1->next; + else + open_files = p1->next; + + kfree(p1); + break; + } + p2 = p1; + p1 = p1->next; + } + spin_unlock_irqrestore(&i2o_config_lock, flags); + unlock_kernel(); + + return 0; +} + +static int cfg_fasync(int fd, struct file *fp, int on) +{ + u32 id = (u32)fp->private_data; + struct i2o_cfg_info *p; + + for(p = open_files; p; p = p->next) + if(p->q_id == id) + break; + + if(!p) + return -EBADF; + + return fasync_helper(fd, fp, on, &p->fasync); +} + +static struct file_operations config_fops = +{ + owner: THIS_MODULE, + llseek: cfg_llseek, + read: cfg_read, + write: cfg_write, + ioctl: cfg_ioctl, + open: cfg_open, + release: cfg_release, + fasync: cfg_fasync, +}; + +static struct miscdevice i2o_miscdev = { + I2O_MINOR, + "i2octl", + &config_fops +}; + +#ifdef MODULE +int init_module(void) +#else +int __init i2o_config_init(void) +#endif +{ + printk(KERN_INFO "I2O configuration manager v 0.04.\n"); + printk(KERN_INFO " (C) Copyright 1999 Red Hat Software\n"); + + if((page_buf = kmalloc(4096, GFP_KERNEL))==NULL) + { + printk(KERN_ERR "i2o_config: no memory for page buffer.\n"); + return -ENOBUFS; + } + if(misc_register(&i2o_miscdev)==-1) + { + printk(KERN_ERR "i2o_config: can't register device.\n"); + kfree(page_buf); + return -EBUSY; + } + /* + * Install our handler + */ + if(i2o_install_handler(&cfg_handler)<0) + { + kfree(page_buf); + printk(KERN_ERR "i2o_config: handler register failed.\n"); + misc_deregister(&i2o_miscdev); + return -EBUSY; + } + /* + * The low 16bits of the transaction context must match this + * for everything we post. Otherwise someone else gets our mail + */ + i2o_cfg_context = cfg_handler.context; + return 0; +} + +#ifdef MODULE + +void cleanup_module(void) +{ + misc_deregister(&i2o_miscdev); + + if(page_buf) + kfree(page_buf); + if(i2o_cfg_context != -1) + i2o_remove_handler(&cfg_handler); +} + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR("Red Hat Software"); +MODULE_DESCRIPTION("I2O Configuration"); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_core.c linux.ac/drivers/message/i2o/i2o_core.c --- linux.vanilla/drivers/message/i2o/i2o_core.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_core.c Sat Apr 21 23:32:50 2001 @@ -0,0 +1,3503 @@ +/* + * Core I2O structure management + * + * (C) Copyright 1999 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * A lot of the I2O message side code from this is taken from the + * Red Creek RCPCI45 adapter driver by Red Creek Communications + * + * Fixes by: + * Philipp Rumpf + * Juha Sievänen + * Auvo Häkkinen + * Deepak Saxena + * Boji T Kannanthanam + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "i2o_lan.h" + +//#define DRIVERDEBUG + +#ifdef DRIVERDEBUG +#define dprintk(s, args...) printk(s, ## args) +#else +#define dprintk(s, args...) +#endif + +/* OSM table */ +static struct i2o_handler *i2o_handlers[MAX_I2O_MODULES] = {NULL}; + +/* Controller list */ +static struct i2o_controller *i2o_controllers[MAX_I2O_CONTROLLERS] = {NULL}; +struct i2o_controller *i2o_controller_chain = NULL; +int i2o_num_controllers = 0; + +/* Initiator Context for Core message */ +static int core_context = 0; + +/* Initialization && shutdown functions */ +static void i2o_sys_init(void); +static void i2o_sys_shutdown(void); +static int i2o_reset_controller(struct i2o_controller *); +static int i2o_reboot_event(struct notifier_block *, unsigned long , void *); +static int i2o_online_controller(struct i2o_controller *); +static int i2o_init_outbound_q(struct i2o_controller *); +static int i2o_post_outbound_messages(struct i2o_controller *); + +/* Reply handler */ +static void i2o_core_reply(struct i2o_handler *, struct i2o_controller *, + struct i2o_message *); + +/* Various helper functions */ +static int i2o_lct_get(struct i2o_controller *); +static int i2o_lct_notify(struct i2o_controller *); +static int i2o_hrt_get(struct i2o_controller *); + +static int i2o_build_sys_table(void); +static int i2o_systab_send(struct i2o_controller *c); + +/* I2O core event handler */ +static int i2o_core_evt(void *); +static int evt_pid; +static int evt_running; + +/* Dynamic LCT update handler */ +static int i2o_dyn_lct(void *); + +void i2o_report_controller_unit(struct i2o_controller *, struct i2o_device *); + +/* + * I2O System Table. Contains information about + * all the IOPs in the system. Used to inform IOPs + * about each other's existence. + * + * sys_tbl_ver is the CurrentChangeIndicator that is + * used by IOPs to track changes. + */ +static struct i2o_sys_tbl *sys_tbl = NULL; +static int sys_tbl_ind = 0; +static int sys_tbl_len = 0; + +/* + * This spin lock is used to keep a device from being + * added and deleted concurrently across CPUs or interrupts. + * This can occur when a user creates a device and immediatelly + * deletes it before the new_dev_notify() handler is called. + */ +static spinlock_t i2o_dev_lock = SPIN_LOCK_UNLOCKED; + +#ifdef MODULE +/* + * Function table to send to bus specific layers + * See for explanation of this + */ +static struct i2o_core_func_table i2o_core_functions = +{ + i2o_install_controller, + i2o_activate_controller, + i2o_find_controller, + i2o_unlock_controller, + i2o_run_queue, + i2o_delete_controller +}; + +#ifdef CONFIG_I2O_PCI_MODULE +extern int i2o_pci_core_attach(struct i2o_core_func_table *); +extern void i2o_pci_core_detach(void); +#endif /* CONFIG_I2O_PCI_MODULE */ + +#endif /* MODULE */ + +/* + * Structures and definitions for synchronous message posting. + * See i2o_post_wait() for description. + */ +struct i2o_post_wait_data +{ + int *status; /* Pointer to status block on caller stack */ + int *complete; /* Pointer to completion flag on caller stack */ + u32 id; /* Unique identifier */ + wait_queue_head_t *wq; /* Wake up for caller (NULL for dead) */ + struct i2o_post_wait_data *next; /* Chain */ + void *mem[2]; /* Memory blocks to recover on failure path */ +}; +static struct i2o_post_wait_data *post_wait_queue = NULL; +static u32 post_wait_id = 0; // Unique ID for each post_wait +static spinlock_t post_wait_lock = SPIN_LOCK_UNLOCKED; +static void i2o_post_wait_complete(u32, int); + +/* OSM descriptor handler */ +static struct i2o_handler i2o_core_handler = +{ + (void *)i2o_core_reply, + NULL, + NULL, + NULL, + "I2O core layer", + 0, + I2O_CLASS_EXECUTIVE +}; + +/* + * Used when queueing a reply to be handled later + */ + +struct reply_info +{ + struct i2o_controller *iop; + u32 msg[MSG_FRAME_SIZE]; +}; +static struct reply_info evt_reply; +static struct reply_info events[I2O_EVT_Q_LEN]; +static int evt_in = 0; +static int evt_out = 0; +static int evt_q_len = 0; +#define MODINC(x,y) ((x) = ((x) + 1) % (y)) + +/* + * I2O configuration spinlock. This isnt a big deal for contention + * so we have one only + */ + +static DECLARE_MUTEX(i2o_configuration_lock); + +/* + * Event spinlock. Used to keep event queue sane and from + * handling multiple events simultaneously. + */ +static spinlock_t i2o_evt_lock = SPIN_LOCK_UNLOCKED; + +/* + * Semaphore used to synchronize event handling thread with + * interrupt handler. + */ + +static DECLARE_MUTEX(evt_sem); +static DECLARE_MUTEX_LOCKED(evt_dead); +DECLARE_WAIT_QUEUE_HEAD(evt_wait); + +static struct notifier_block i2o_reboot_notifier = +{ + i2o_reboot_event, + NULL, + 0 +}; + +/* + * Config options + */ + +static int verbose = 0; +MODULE_PARM(verbose, "i"); + +/* + * I2O Core reply handler + */ +static void i2o_core_reply(struct i2o_handler *h, struct i2o_controller *c, + struct i2o_message *m) +{ + u32 *msg=(u32 *)m; + u32 status; + u32 context = msg[2]; + + if (msg[0] & MSG_FAIL) // Fail bit is set + { + u32 *preserved_msg = (u32*)(c->mem_offset + msg[7]); + + i2o_report_status(KERN_INFO, "i2o_core", msg); + i2o_dump_message(preserved_msg); + + /* If the failed request needs special treatment, + * it should be done here. */ + + /* Release the preserved msg by resubmitting it as a NOP */ + + preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; + preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; + preserved_msg[2] = 0; + i2o_post_message(c, msg[7]); + + /* If reply to i2o_post_wait failed, return causes a timeout */ + + return; + } + +#ifdef DRIVERDEBUG + i2o_report_status(KERN_INFO, "i2o_core", msg); +#endif + + if(msg[2]&0x80000000) // Post wait message + { + if (msg[4] >> 24) + status = (msg[4] & 0xFFFF); + else + status = I2O_POST_WAIT_OK; + + i2o_post_wait_complete(context, status); + return; + } + + if(m->function == I2O_CMD_UTIL_EVT_REGISTER) + { + memcpy(events[evt_in].msg, msg, (msg[0]>>16)<<2); + events[evt_in].iop = c; + + spin_lock(&i2o_evt_lock); + MODINC(evt_in, I2O_EVT_Q_LEN); + if(evt_q_len == I2O_EVT_Q_LEN) + MODINC(evt_out, I2O_EVT_Q_LEN); + else + evt_q_len++; + spin_unlock(&i2o_evt_lock); + + up(&evt_sem); + wake_up_interruptible(&evt_wait); + return; + } + + if(m->function == I2O_CMD_LCT_NOTIFY) + { + up(&c->lct_sem); + return; + } + + /* + * If this happens, we want to dump the message to the syslog so + * it can be sent back to the card manufacturer by the end user + * to aid in debugging. + * + */ + printk(KERN_WARNING "%s: Unsolicited message reply sent to core!" + "Message dumped to syslog\n", + c->name); + i2o_dump_message(msg); + + return; +} + +/** + * i2o_install_handler - install a message handler + * @h: Handler structure + * + * Install an I2O handler - these handle the asynchronous messaging + * from the card once it has initialised. If the table of handlers is + * full then -ENOSPC is returned. On a success 0 is returned and the + * context field is set by the function. The structure is part of the + * system from this time onwards. It must not be freed until it has + * been uninstalled + */ + +int i2o_install_handler(struct i2o_handler *h) +{ + int i; + down(&i2o_configuration_lock); + for(i=0;icontext = i; + i2o_handlers[i]=h; + up(&i2o_configuration_lock); + return 0; + } + } + up(&i2o_configuration_lock); + return -ENOSPC; +} + +/** + * i2o_remove_handler - remove an i2o message handler + * @h: handler + * + * Remove a message handler previously installed with i2o_install_handler. + * After this function returns the handler object can be freed or re-used + */ + +int i2o_remove_handler(struct i2o_handler *h) +{ + i2o_handlers[h->context]=NULL; + return 0; +} + + +/* + * Each I2O controller has a chain of devices on it. + * Each device has a pointer to it's LCT entry to be used + * for fun purposes. + */ + +/** + * i2o_install_device - attach a device to a controller + * @c: controller + * @d: device + * + * Add a new device to an i2o controller. This can be called from + * non interrupt contexts only. It adds the device and marks it as + * unclaimed. The device memory becomes part of the kernel and must + * be uninstalled before being freed or reused. Zero is returned + * on success. + */ + +int i2o_install_device(struct i2o_controller *c, struct i2o_device *d) +{ + int i; + + down(&i2o_configuration_lock); + d->controller=c; + d->owner=NULL; + d->next=c->devices; + d->prev=NULL; + if (c->devices != NULL) + c->devices->prev=d; + c->devices=d; + *d->dev_name = 0; + + for(i = 0; i < I2O_MAX_MANAGERS; i++) + d->managers[i] = NULL; + + up(&i2o_configuration_lock); + return 0; +} + +/* we need this version to call out of i2o_delete_controller */ + +int __i2o_delete_device(struct i2o_device *d) +{ + struct i2o_device **p; + int i; + + p=&(d->controller->devices); + + /* + * Hey we have a driver! + * Check to see if the driver wants us to notify it of + * device deletion. If it doesn't we assume that it + * is unsafe to delete a device with an owner and + * fail. + */ + if(d->owner) + { + if(d->owner->dev_del_notify) + { + dprintk(KERN_INFO "Device has owner, notifying\n"); + d->owner->dev_del_notify(d->controller, d); + if(d->owner) + { + printk(KERN_WARNING + "Driver \"%s\" did not release device!\n", d->owner->name); + return -EBUSY; + } + } + else + return -EBUSY; + } + + /* + * Tell any other users who are talking to this device + * that it's going away. We assume that everything works. + */ + for(i=0; i < I2O_MAX_MANAGERS; i++) + { + if(d->managers[i] && d->managers[i]->dev_del_notify) + d->managers[i]->dev_del_notify(d->controller, d); + } + + while(*p!=NULL) + { + if(*p==d) + { + /* + * Destroy + */ + *p=d->next; + kfree(d); + return 0; + } + p=&((*p)->next); + } + printk(KERN_ERR "i2o_delete_device: passed invalid device.\n"); + return -EINVAL; +} + +/** + * i2o_delete_device - remove an i2o device + * @d: device to remove + * + * This function unhooks a device from a controller. The device + * will not be unhooked if it has an owner who does not wish to free + * it, or if the owner lacks a dev_del_notify function. In that case + * -EBUSY is returned. On success 0 is returned. Other errors cause + * negative errno values to be returned + */ + +int i2o_delete_device(struct i2o_device *d) +{ + int ret; + + down(&i2o_configuration_lock); + + /* + * Seek, locate + */ + + ret = __i2o_delete_device(d); + + up(&i2o_configuration_lock); + + return ret; +} + +/** + * i2o_install_controller - attach a controller + * @c: controller + * + * Add a new controller to the i2o layer. This can be called from + * non interrupt contexts only. It adds the controller and marks it as + * unused with no devices. If the tables are full or memory allocations + * fail then a negative errno code is returned. On success zero is + * returned and the controller is bound to the system. The structure + * must not be freed or reused until being uninstalled. + */ + +int i2o_install_controller(struct i2o_controller *c) +{ + int i; + down(&i2o_configuration_lock); + for(i=0;idlct = (i2o_lct*)kmalloc(8192, GFP_KERNEL); + if(c->dlct==NULL) + { + up(&i2o_configuration_lock); + return -ENOMEM; + } + i2o_controllers[i]=c; + c->devices = NULL; + c->next=i2o_controller_chain; + i2o_controller_chain=c; + c->unit = i; + c->page_frame = NULL; + c->hrt = NULL; + c->lct = NULL; + c->status_block = NULL; + sprintf(c->name, "i2o/iop%d", i); + i2o_num_controllers++; + init_MUTEX_LOCKED(&c->lct_sem); + up(&i2o_configuration_lock); + return 0; + } + } + printk(KERN_ERR "No free i2o controller slots.\n"); + up(&i2o_configuration_lock); + return -EBUSY; +} + +/** + * i2o_delete_controller - delete a controller + * @c: controller + * + * Remove an i2o controller from the system. If the controller or its + * devices are busy then -EBUSY is returned. On a failure a negative + * errno code is returned. On success zero is returned. + */ + +int i2o_delete_controller(struct i2o_controller *c) +{ + struct i2o_controller **p; + int users; + char name[16]; + int stat; + + dprintk(KERN_INFO "Deleting controller %s\n", c->name); + + /* + * Clear event registration as this can cause weird behavior + */ + if(c->status_block->iop_state == ADAPTER_STATE_OPERATIONAL) + i2o_event_register(c, core_context, 0, 0, 0); + + down(&i2o_configuration_lock); + if((users=atomic_read(&c->users))) + { + dprintk(KERN_INFO "I2O: %d users for controller %s\n", users, + c->name); + up(&i2o_configuration_lock); + return -EBUSY; + } + while(c->devices) + { + if(__i2o_delete_device(c->devices)<0) + { + /* Shouldnt happen */ + c->bus_disable(c); + up(&i2o_configuration_lock); + return -EBUSY; + } + } + + /* + * If this is shutdown time, the thread's already been killed + */ + if(c->lct_running) { + stat = kill_proc(c->lct_pid, SIGTERM, 1); + if(!stat) { + int count = 10 * 100; + while(c->lct_running && --count) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + if(!count) + printk(KERN_ERR + "%s: LCT thread still running!\n", + c->name); + } + } + + p=&i2o_controller_chain; + + while(*p) + { + if(*p==c) + { + /* Ask the IOP to switch to RESET state */ + i2o_reset_controller(c); + + /* Release IRQ */ + c->destructor(c); + + *p=c->next; + up(&i2o_configuration_lock); + + if(c->page_frame) + kfree(c->page_frame); + if(c->hrt) + kfree(c->hrt); + if(c->lct) + kfree(c->lct); + if(c->status_block) + kfree(c->status_block); + if(c->dlct) + kfree(c->dlct); + + i2o_controllers[c->unit]=NULL; + memcpy(name, c->name, strlen(c->name)+1); + kfree(c); + dprintk(KERN_INFO "%s: Deleted from controller chain.\n", name); + + i2o_num_controllers--; + return 0; + } + p=&((*p)->next); + } + up(&i2o_configuration_lock); + printk(KERN_ERR "i2o_delete_controller: bad pointer!\n"); + return -ENOENT; +} + +/** + * i2o_unlock_controller - unlock a controller + * @c: controller to unlock + * + * Take a lock on an i2o controller. This prevents it being deleted. + * i2o controllers are not refcounted so a deletion of an in use device + * will fail, not take affect on the last dereference. + */ + +void i2o_unlock_controller(struct i2o_controller *c) +{ + atomic_dec(&c->users); +} + +/** + * i2o_find_controller - return a locked controller + * @n: controller number + * + * Returns a pointer to the controller object. The controller is locked + * on return. NULL is returned if the controller is not found. + */ + +struct i2o_controller *i2o_find_controller(int n) +{ + struct i2o_controller *c; + + if(n<0 || n>=MAX_I2O_CONTROLLERS) + return NULL; + + down(&i2o_configuration_lock); + c=i2o_controllers[n]; + if(c!=NULL) + atomic_inc(&c->users); + up(&i2o_configuration_lock); + return c; +} + +/** + * i2o_issue_claim - claim or release a device + * @cmd: command + * @c: controller to claim for + * @tid: i2o task id + * @type: type of claim + * + * Issue I2O UTIL_CLAIM and UTIL_RELEASE messages. The message to be sent + * is set by cmd. The tid is the task id of the object to claim and the + * type is the claim type (see the i2o standard) + * + * Zero is returned on success. + */ + +static int i2o_issue_claim(u32 cmd, struct i2o_controller *c, int tid, u32 type) +{ + u32 msg[5]; + + msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; + msg[1] = cmd << 24 | HOST_TID<<12 | tid; + msg[3] = 0; + msg[4] = type; + + return i2o_post_wait(c, msg, sizeof(msg), 60); +} + +/* + * i2o_claim_device - claim a device for use by an OSM + * @d: device to claim + * @h: handler for this device + * + * Do the leg work to assign a device to a given OSM on Linux. The + * kernel updates the internal handler data for the device and then + * performs an I2O claim for the device, attempting to claim the + * device as primary. If the attempt fails a negative errno code + * is returned. On success zero is returned. + */ + +int i2o_claim_device(struct i2o_device *d, struct i2o_handler *h) +{ + down(&i2o_configuration_lock); + if (d->owner) { + printk(KERN_INFO "Device claim called, but dev already owned by %s!", + h->name); + up(&i2o_configuration_lock); + return -EBUSY; + } + d->owner=h; + + if(i2o_issue_claim(I2O_CMD_UTIL_CLAIM ,d->controller,d->lct_data.tid, + I2O_CLAIM_PRIMARY)) + { + d->owner = NULL; + return -EBUSY; + } + up(&i2o_configuration_lock); + return 0; +} + +/** + * i2o_release_device - release a device that the OSM is using + * @d: device to claim + * @h: handler for this device + * + * Drop a claim by an OSM on a given I2O device. The handler is cleared + * and 0 is returned on success. + * + * AC - some devices seem to want to refuse an unclaim until they have + * finished internal processing. It makes sense since you don't want a + * new device to go reconfiguring the entire system until you are done. + * Thus we are prepared to wait briefly. + */ + +int i2o_release_device(struct i2o_device *d, struct i2o_handler *h) +{ + int err = 0; + int tries; + + down(&i2o_configuration_lock); + if (d->owner != h) { + printk(KERN_INFO "Claim release called, but not owned by %s!\n", + h->name); + up(&i2o_configuration_lock); + return -ENOENT; + } + + for(tries=0;tries<10;tries++) + { + d->owner = NULL; + + /* + * If the controller takes a nonblocking approach to + * releases we have to sleep/poll for a few times. + */ + + if((err=i2o_issue_claim(I2O_CMD_UTIL_RELEASE, d->controller, d->lct_data.tid, I2O_CLAIM_PRIMARY)) ) + { + err = -ENXIO; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ); + } + else + { + err=0; + break; + } + } + up(&i2o_configuration_lock); + return err; +} + +/** + * i2o_device_notify_on - Enable deletion notifiers + * @d: device for notification + * @h: handler to install + * + * Called by OSMs to let the core know that they want to be + * notified if the given device is deleted from the system. + */ + +int i2o_device_notify_on(struct i2o_device *d, struct i2o_handler *h) +{ + int i; + + if(d->num_managers == I2O_MAX_MANAGERS) + return -ENOSPC; + + for(i = 0; i < I2O_MAX_MANAGERS; i++) + { + if(!d->managers[i]) + { + d->managers[i] = h; + break; + } + } + + d->num_managers++; + + return 0; +} + +/** + * i2o_device_notify_off - Remove deletion notifiers + * @d: device for notification + * @h: handler to remove + * + * Called by OSMs to let the core know that they no longer + * are interested in the fate of the given device. + */ +int i2o_device_notify_off(struct i2o_device *d, struct i2o_handler *h) +{ + int i; + + for(i=0; i < I2O_MAX_MANAGERS; i++) + { + if(d->managers[i] == h) + { + d->managers[i] = NULL; + d->num_managers--; + return 0; + } + } + + return -ENOENT; +} + +/** + * i2o_event_register - register interest in an event + * @c: Controller to register interest with + * @tid: I2O task id + * @init_context: initiator context to use with this notifier + * @tr_context: transaction context to use with this notifier + * @evt_mask: mask of events + * + * Create and posts an event registration message to the task. No reply + * is waited for, or expected. Errors in posting will be reported. + */ + +int i2o_event_register(struct i2o_controller *c, u32 tid, + u32 init_context, u32 tr_context, u32 evt_mask) +{ + u32 msg[5]; // Not performance critical, so we just + // i2o_post_this it instead of building it + // in IOP memory + + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_UTIL_EVT_REGISTER<<24 | HOST_TID<<12 | tid; + msg[2] = init_context; + msg[3] = tr_context; + msg[4] = evt_mask; + + return i2o_post_this(c, msg, sizeof(msg)); +} + +/* + * i2o_event_ack - acknowledge an event + * @c: controller + * @msg: pointer to the UTIL_EVENT_REGISTER reply we received + * + * We just take a pointer to the original UTIL_EVENT_REGISTER reply + * message and change the function code since that's what spec + * describes an EventAck message looking like. + */ + +int i2o_event_ack(struct i2o_controller *c, u32 *msg) +{ + struct i2o_message *m = (struct i2o_message *)msg; + + m->function = I2O_CMD_UTIL_EVT_ACK; + + return i2o_post_wait(c, msg, m->size * 4, 2); +} + +/* + * Core event handler. Runs as a separate thread and is woken + * up whenever there is an Executive class event. + */ +static int i2o_core_evt(void *reply_data) +{ + struct reply_info *reply = (struct reply_info *) reply_data; + u32 *msg = reply->msg; + struct i2o_controller *c = NULL; + int flags; + + lock_kernel(); + daemonize(); + unlock_kernel(); + + strcpy(current->comm, "i2oevtd"); + evt_running = 1; + + while(1) + { + if(down_interruptible(&evt_sem)) + { + dprintk(KERN_INFO "I2O event thread dead\n"); + printk("exiting..."); + evt_running = 0; + up_and_exit(&evt_dead, 0); + } + + /* + * Copy the data out of the queue so that we don't have to lock + * around the whole function and just around the qlen update + */ + spin_lock_irqsave(&i2o_evt_lock, flags); + memcpy(reply, &events[evt_out], sizeof(struct reply_info)); + MODINC(evt_out, I2O_EVT_Q_LEN); + evt_q_len--; + spin_unlock_irqrestore(&i2o_evt_lock, flags); + + c = reply->iop; + dprintk(KERN_INFO "I2O IRTOS EVENT: iop%d, event %#10x\n", c->unit, msg[4]); + + /* + * We do not attempt to delete/quiesce/etc. the controller if + * some sort of error indidication occurs. We may want to do + * so in the future, but for now we just let the user deal with + * it. One reason for this is that what to do with an error + * or when to send what ćrror is not really agreed on, so + * we get errors that may not be fatal but just look like they + * are...so let the user deal with it. + */ + switch(msg[4]) + { + case I2O_EVT_IND_EXEC_RESOURCE_LIMITS: + printk(KERN_ERR "%s: Out of resources\n", c->name); + break; + + case I2O_EVT_IND_EXEC_POWER_FAIL: + printk(KERN_ERR "%s: Power failure\n", c->name); + break; + + case I2O_EVT_IND_EXEC_HW_FAIL: + { + char *fail[] = + { + "Unknown Error", + "Power Lost", + "Code Violation", + "Parity Error", + "Code Execution Exception", + "Watchdog Timer Expired" + }; + + if(msg[5] <= 6) + printk(KERN_ERR "%s: Hardware Failure: %s\n", + c->name, fail[msg[5]]); + else + printk(KERN_ERR "%s: Unknown Hardware Failure\n", c->name); + + break; + } + + /* + * New device created + * - Create a new i2o_device entry + * - Inform all interested drivers about this device's existence + */ + case I2O_EVT_IND_EXEC_NEW_LCT_ENTRY: + { + struct i2o_device *d = (struct i2o_device *) + kmalloc(sizeof(struct i2o_device), GFP_KERNEL); + int i; + + if (d == NULL) { + printk(KERN_EMERG "i2oevtd: out of memory\n"); + break; + } + memcpy(&d->lct_data, &msg[5], sizeof(i2o_lct_entry)); + + d->next = NULL; + d->controller = c; + d->flags = 0; + + i2o_report_controller_unit(c, d); + i2o_install_device(c,d); + + for(i = 0; i < MAX_I2O_MODULES; i++) + { + if(i2o_handlers[i] && + i2o_handlers[i]->new_dev_notify && + (i2o_handlers[i]->class&d->lct_data.class_id)) + { + spin_lock(&i2o_dev_lock); + i2o_handlers[i]->new_dev_notify(c,d); + spin_unlock(&i2o_dev_lock); + } + } + + break; + } + + /* + * LCT entry for a device has been modified, so update it + * internally. + */ + case I2O_EVT_IND_EXEC_MODIFIED_LCT: + { + struct i2o_device *d; + i2o_lct_entry *new_lct = (i2o_lct_entry *)&msg[5]; + + for(d = c->devices; d; d = d->next) + { + if(d->lct_data.tid == new_lct->tid) + { + memcpy(&d->lct_data, new_lct, sizeof(i2o_lct_entry)); + break; + } + } + break; + } + + case I2O_EVT_IND_CONFIGURATION_FLAG: + printk(KERN_WARNING "%s requires user configuration\n", c->name); + break; + + case I2O_EVT_IND_GENERAL_WARNING: + printk(KERN_WARNING "%s: Warning notification received!" + "Check configuration for errors!\n", c->name); + break; + + case I2O_EVT_IND_EVT_MASK_MODIFIED: + /* Well I guess that was us hey .. */ + break; + + default: + printk(KERN_WARNING "%s: No handler for event (0x%08x)\n", c->name, msg[4]); + break; + } + } + + return 0; +} + +/* + * Dynamic LCT update. This compares the LCT with the currently + * installed devices to check for device deletions..this needed b/c there + * is no DELETED_LCT_ENTRY EventIndicator for the Executive class so + * we can't just have the event handler do this...annoying + * + * This is a hole in the spec that will hopefully be fixed someday. + */ +static int i2o_dyn_lct(void *foo) +{ + struct i2o_controller *c = (struct i2o_controller *)foo; + struct i2o_device *d = NULL; + struct i2o_device *d1 = NULL; + int i = 0; + int found = 0; + int entries; + void *tmp; + char name[16]; + + lock_kernel(); + daemonize(); + unlock_kernel(); + + sprintf(name, "iop%d_lctd", c->unit); + strcpy(current->comm, name); + + c->lct_running = 1; + + while(1) + { + down_interruptible(&c->lct_sem); + if(signal_pending(current)) + { + dprintk(KERN_ERR "%s: LCT thread dead\n", c->name); + c->lct_running = 0; + return 0; + } + + entries = c->dlct->table_size; + entries -= 3; + entries /= 9; + + dprintk(KERN_INFO "%s: Dynamic LCT Update\n",c->name); + dprintk(KERN_INFO "%s: Dynamic LCT contains %d entries\n", c->name, entries); + + if(!entries) + { + printk(KERN_INFO "%s: Empty LCT???\n", c->name); + continue; + } + + /* + * Loop through all the devices on the IOP looking for their + * LCT data in the LCT. We assume that TIDs are not repeated. + * as that is the only way to really tell. It's been confirmed + * by the IRTOS vendor(s?) that TIDs are not reused until they + * wrap arround(4096), and I doubt a system will up long enough + * to create/delete that many devices. + */ + for(d = c->devices; d; ) + { + found = 0; + d1 = d->next; + + for(i = 0; i < entries; i++) + { + if(d->lct_data.tid == c->dlct->lct_entry[i].tid) + { + found = 1; + break; + } + } + if(!found) + { + dprintk(KERN_INFO "i2o_core: Deleted device!\n"); + spin_lock(&i2o_dev_lock); + i2o_delete_device(d); + spin_unlock(&i2o_dev_lock); + } + d = d1; + } + + /* + * Tell LCT to renotify us next time there is a change + */ + i2o_lct_notify(c); + + /* + * Copy new LCT into public LCT + * + * Possible race if someone is reading LCT while we are copying + * over it. If this happens, we'll fix it then. but I doubt that + * the LCT will get updated often enough or will get read by + * a user often enough to worry. + */ + if(c->lct->table_size < c->dlct->table_size) + { + tmp = c->lct; + c->lct = kmalloc(c->dlct->table_size<<2, GFP_KERNEL); + if(!c->lct) + { + printk(KERN_ERR "%s: No memory for LCT!\n", c->name); + c->lct = tmp; + continue; + } + kfree(tmp); + } + memcpy(c->lct, c->dlct, c->dlct->table_size<<2); + } + + return 0; +} + +/** + * i2o_run_queue - process pending events on a controller + * @c: controller to process + * + * This is called by the bus specific driver layer when an interrupt + * or poll of this card interface is desired. + */ + +void i2o_run_queue(struct i2o_controller *c) +{ + struct i2o_message *m; + u32 mv; + u32 *msg; + + /* + * Old 960 steppings had a bug in the I2O unit that caused + * the queue to appear empty when it wasn't. + */ + if((mv=I2O_REPLY_READ32(c))==0xFFFFFFFF) + mv=I2O_REPLY_READ32(c); + + while(mv!=0xFFFFFFFF) + { + struct i2o_handler *i; + m=(struct i2o_message *)bus_to_virt(mv); + msg=(u32*)m; + + i=i2o_handlers[m->initiator_context&(MAX_I2O_MODULES-1)]; + if(i && i->reply) + i->reply(i,c,m); + else + { + printk(KERN_WARNING "I2O: Spurious reply to handler %d\n", + m->initiator_context&(MAX_I2O_MODULES-1)); + } + i2o_flush_reply(c,mv); + mb(); + + /* That 960 bug again... */ + if((mv=I2O_REPLY_READ32(c))==0xFFFFFFFF) + mv=I2O_REPLY_READ32(c); + } +} + + +/** + * i2o_get_class_name - do i2o class name lookup + * @class: class number + * + * Return a descriptive string for an i2o class + */ + +const char *i2o_get_class_name(int class) +{ + int idx = 16; + static char *i2o_class_name[] = { + "Executive", + "Device Driver Module", + "Block Device", + "Tape Device", + "LAN Interface", + "WAN Interface", + "Fibre Channel Port", + "Fibre Channel Device", + "SCSI Device", + "ATE Port", + "ATE Device", + "Floppy Controller", + "Floppy Device", + "Secondary Bus Port", + "Peer Transport Agent", + "Peer Transport", + "Unknown" + }; + + switch(class&0xFFF) + { + case I2O_CLASS_EXECUTIVE: + idx = 0; break; + case I2O_CLASS_DDM: + idx = 1; break; + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + idx = 2; break; + case I2O_CLASS_SEQUENTIAL_STORAGE: + idx = 3; break; + case I2O_CLASS_LAN: + idx = 4; break; + case I2O_CLASS_WAN: + idx = 5; break; + case I2O_CLASS_FIBRE_CHANNEL_PORT: + idx = 6; break; + case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL: + idx = 7; break; + case I2O_CLASS_SCSI_PERIPHERAL: + idx = 8; break; + case I2O_CLASS_ATE_PORT: + idx = 9; break; + case I2O_CLASS_ATE_PERIPHERAL: + idx = 10; break; + case I2O_CLASS_FLOPPY_CONTROLLER: + idx = 11; break; + case I2O_CLASS_FLOPPY_DEVICE: + idx = 12; break; + case I2O_CLASS_BUS_ADAPTER_PORT: + idx = 13; break; + case I2O_CLASS_PEER_TRANSPORT_AGENT: + idx = 14; break; + case I2O_CLASS_PEER_TRANSPORT: + idx = 15; break; + } + + return i2o_class_name[idx]; +} + + +/** + * i2o_wait_message - obtain an i2o message from the IOP + * @c: controller + * @why: explanation + * + * This function waits up to 5 seconds for a message slot to be + * available. If no message is available it prints an error message + * that is expected to be what the message will be used for (eg + * "get_status"). 0xFFFFFFFF is returned on a failure. + * + * On a success the message is returned. This is the physical page + * frame offset address from the read port. (See the i2o spec) + */ + +u32 i2o_wait_message(struct i2o_controller *c, char *why) +{ + long time=jiffies; + u32 m; + while((m=I2O_POST_READ32(c))==0xFFFFFFFF) + { + if((jiffies-time)>=5*HZ) + { + dprintk(KERN_ERR "%s: Timeout waiting for message frame to send %s.\n", + c->name, why); + return 0xFFFFFFFF; + } + schedule(); + barrier(); + } + return m; +} + +/** + * i2o_report_controller_unit - print information about a tid + * @c: controller + * @d: device + * + * Dump an information block associated with a given unit (TID). The + * tables are read and a block of text is output to printk that is + * formatted intended for the user. + */ + +void i2o_report_controller_unit(struct i2o_controller *c, struct i2o_device *d) +{ + char buf[64]; + char str[22]; + int ret; + int unit = d->lct_data.tid; + + if(verbose==0) + return; + + printk(KERN_INFO "Target ID %d.\n", unit); + if((ret=i2o_query_scalar(c, unit, 0xF100, 3, buf, 16))>=0) + { + buf[16]=0; + printk(KERN_INFO " Vendor: %s\n", buf); + } + if((ret=i2o_query_scalar(c, unit, 0xF100, 4, buf, 16))>=0) + { + buf[16]=0; + printk(KERN_INFO " Device: %s\n", buf); + } + if(i2o_query_scalar(c, unit, 0xF100, 5, buf, 16)>=0) + { + buf[16]=0; + printk(KERN_INFO " Description: %s\n", buf); + } + if((ret=i2o_query_scalar(c, unit, 0xF100, 6, buf, 8))>=0) + { + buf[8]=0; + printk(KERN_INFO " Rev: %s\n", buf); + } + + printk(KERN_INFO " Class: "); + sprintf(str, "%-21s", i2o_get_class_name(d->lct_data.class_id)); + printk("%s\n", str); + + printk(KERN_INFO " Subclass: 0x%04X\n", d->lct_data.sub_class); + printk(KERN_INFO " Flags: "); + + if(d->lct_data.device_flags&(1<<0)) + printk("C"); // ConfigDialog requested + if(d->lct_data.device_flags&(1<<1)) + printk("U"); // Multi-user capable + if(!(d->lct_data.device_flags&(1<<4))) + printk("P"); // Peer service enabled! + if(!(d->lct_data.device_flags&(1<<5))) + printk("M"); // Mgmt service enabled! + printk("\n"); + +} + + +/* + * Parse the hardware resource table. Right now we print it out + * and don't do a lot with it. We should collate these and then + * interact with the Linux resource allocation block. + * + * Lets prove we can read it first eh ? + * + * This is full of endianisms! + */ + +static int i2o_parse_hrt(struct i2o_controller *c) +{ +#ifdef DRIVERDEBUG + u32 *rows=(u32*)c->hrt; + u8 *p=(u8 *)c->hrt; + u8 *d; + int count; + int length; + int i; + int state; + + if(p[3]!=0) + { + printk(KERN_ERR "%s: HRT table for controller is too new a version.\n", + c->name); + return -1; + } + + count=p[0]|(p[1]<<8); + length = p[2]; + + printk(KERN_INFO "%s: HRT has %d entries of %d bytes each.\n", + c->name, count, length<<2); + + rows+=2; + + for(i=0;i>=12; + if(state&(1<<0)) + printk("H"); /* Hidden */ + if(state&(1<<2)) + { + printk("P"); /* Present */ + if(state&(1<<1)) + printk("C"); /* Controlled */ + } + if(state>9) + printk("*"); /* Hard */ + + printk("]:"); + + switch(p[3]&0xFFFF) + { + case 0: + /* Adapter private bus - easy */ + printk("Local bus %d: I/O at 0x%04X Mem 0x%08X", + p[2], d[1]<<8|d[0], *(u32 *)(d+4)); + break; + case 1: + /* ISA bus */ + printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X", + p[2], d[2], d[1]<<8|d[0], *(u32 *)(d+4)); + break; + + case 2: /* EISA bus */ + printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X", + p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4)); + break; + + case 3: /* MCA bus */ + printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X", + p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4)); + break; + + case 4: /* PCI bus */ + printk("PCI %d: Bus %d Device %d Function %d", + p[2], d[2], d[1], d[0]); + break; + + case 0x80: /* Other */ + default: + printk("Unsupported bus type."); + break; + } + printk("\n"); + rows+=length; + } +#endif + return 0; +} + +/* + * The logical configuration table tells us what we can talk to + * on the board. Most of the stuff isn't interesting to us. + */ + +static int i2o_parse_lct(struct i2o_controller *c) +{ + int i; + int max; + int tid; + struct i2o_device *d; + i2o_lct *lct = c->lct; + + if (lct == NULL) { + printk(KERN_ERR "%s: LCT is empty???\n", c->name); + return -1; + } + + max = lct->table_size; + max -= 3; + max /= 9; + + printk(KERN_INFO "%s: LCT has %d entries.\n", c->name, max); + + if(lct->iop_flags&(1<<0)) + printk(KERN_WARNING "%s: Configuration dialog desired.\n", c->name); + + for(i=0;icontroller = c; + d->next = NULL; + + memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry)); + + d->flags = 0; + tid = d->lct_data.tid; + + i2o_report_controller_unit(c, d); + + i2o_install_device(c, d); + } + return 0; +} + + +/** + * i2o_quiesce_controller - quiesce controller + * @c: controller + * + * Quiesce an IOP. Causes IOP to make external operation quiescent + * (i2o 'READY' state). Internal operation of the IOP continues normally. + */ + +int i2o_quiesce_controller(struct i2o_controller *c) +{ + u32 msg[4]; + int ret; + + i2o_status_get(c); + + /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */ + + if ((c->status_block->iop_state != ADAPTER_STATE_READY) && + (c->status_block->iop_state != ADAPTER_STATE_OPERATIONAL)) + { + return 0; + } + + msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID; + msg[3] = 0; + + /* Long timeout needed for quiesce if lots of devices */ + + if ((ret = i2o_post_wait(c, msg, sizeof(msg), 240))) + printk(KERN_INFO "%s: Unable to quiesce (status=%#x).\n", + c->name, -ret); + else + dprintk(KERN_INFO "%s: Quiesced.\n", c->name); + + i2o_status_get(c); // Entered READY state + return ret; +} + +/** + * i2o_enable_controller - move controller from ready to operational + * @c: controller + * + * Enable IOP. This allows the IOP to resume external operations and + * reverses the effect of a quiesce. In the event of an error a negative + * errno code is returned. + */ + +int i2o_enable_controller(struct i2o_controller *c) +{ + u32 msg[4]; + int ret; + + i2o_status_get(c); + + /* Enable only allowed on READY state */ + if(c->status_block->iop_state != ADAPTER_STATE_READY) + return -EINVAL; + + msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1]=I2O_CMD_SYS_ENABLE<<24|HOST_TID<<12|ADAPTER_TID; + + /* How long of a timeout do we need? */ + + if ((ret = i2o_post_wait(c, msg, sizeof(msg), 240))) + printk(KERN_ERR "%s: Could not enable (status=%#x).\n", + c->name, -ret); + else + dprintk(KERN_INFO "%s: Enabled.\n", c->name); + + i2o_status_get(c); // entered OPERATIONAL state + + return ret; +} + +/** + * i2o_clear_controller - clear a controller + * @c: controller + * + * Clear an IOP to HOLD state, ie. terminate external operations, clear all + * input queues and prepare for a system restart. IOP's internal operation + * continues normally and the outbound queue is alive. + * The IOP is not expected to rebuild its LCT. + */ + +int i2o_clear_controller(struct i2o_controller *c) +{ + struct i2o_controller *iop; + u32 msg[4]; + int ret; + + /* Quiesce all IOPs first */ + + for (iop = i2o_controller_chain; iop; iop = iop->next) + i2o_quiesce_controller(iop); + + msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1]=I2O_CMD_ADAPTER_CLEAR<<24|HOST_TID<<12|ADAPTER_TID; + msg[3]=0; + + if ((ret=i2o_post_wait(c, msg, sizeof(msg), 30))) + printk(KERN_INFO "%s: Unable to clear (status=%#x).\n", + c->name, -ret); + else + dprintk(KERN_INFO "%s: Cleared.\n",c->name); + + i2o_status_get(c); + + /* Enable other IOPs */ + + for (iop = i2o_controller_chain; iop; iop = iop->next) + if (iop != c) + i2o_enable_controller(iop); + + return ret; +} + + +/** + * i2o_reset_controller - reset an IOP + * @c: controller to reset + * + * Reset the IOP into INIT state and wait until IOP gets into RESET state. + * Terminate all external operations, clear IOP's inbound and outbound + * queues, terminate all DDMs, and reload the IOP's operating environment + * and all local DDMs. The IOP rebuilds its LCT. + */ + +static int i2o_reset_controller(struct i2o_controller *c) +{ + struct i2o_controller *iop; + u32 m; + u8 *status; + u32 *msg; + long time; + + /* Quiesce all IOPs first */ + + for (iop = i2o_controller_chain; iop; iop = iop->next) + { + if(iop->type != I2O_TYPE_PCI || !iop->bus.pci.dpt) + i2o_quiesce_controller(iop); + } + + m=i2o_wait_message(c, "AdapterReset"); + if(m==0xFFFFFFFF) + return -ETIMEDOUT; + msg=(u32 *)(c->mem_offset+m); + + status=(void *)kmalloc(4, GFP_KERNEL); + if(status==NULL) { + printk(KERN_ERR "IOP reset failed - no free memory.\n"); + return -ENOMEM; + } + memset(status, 0, 4); + + msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID; + msg[2]=core_context; + msg[3]=0; + msg[4]=0; + msg[5]=0; + msg[6]=virt_to_bus(status); + msg[7]=0; /* 64bit host FIXME */ + + i2o_post_message(c,m); + + /* Wait for a reply */ + time=jiffies; + while(*status==0) + { + if((jiffies-time)>=20*HZ) + { + printk(KERN_ERR "IOP reset timeout.\n"); + // Better to leak this for safety: kfree(status); + return -ETIMEDOUT; + } + schedule(); + barrier(); + } + + if (*status==I2O_CMD_IN_PROGRESS) + { + /* + * Once the reset is sent, the IOP goes into the INIT state + * which is indeterminate. We need to wait until the IOP + * has rebooted before we can let the system talk to + * it. We read the inbound Free_List until a message is + * available. If we can't read one in the given ammount of + * time, we assume the IOP could not reboot properly. + */ + + dprintk(KERN_INFO "%s: Reset in progress, waiting for reboot...\n", + c->name); + + time = jiffies; + m = I2O_POST_READ32(c); + while(m == 0XFFFFFFFF) + { + if((jiffies-time) >= 30*HZ) + { + printk(KERN_ERR "%s: Timeout waiting for IOP reset.\n", + c->name); + return -ETIMEDOUT; + } + schedule(); + barrier(); + m = I2O_POST_READ32(c); + } + i2o_flush_reply(c,m); + } + + /* If IopReset was rejected or didn't perform reset, try IopClear */ + + i2o_status_get(c); + if (status[0] == I2O_CMD_REJECTED || + c->status_block->iop_state != ADAPTER_STATE_RESET) + { + printk(KERN_WARNING "%s: Reset rejected, trying to clear\n",c->name); + i2o_clear_controller(c); + } + else + dprintk(KERN_INFO "%s: Reset completed.\n", c->name); + + /* Enable other IOPs */ + + for (iop = i2o_controller_chain; iop; iop = iop->next) + if (iop != c) + i2o_enable_controller(iop); + + kfree(status); + return 0; +} + + +/** + * i2o_status_get - get the status block for the IOP + * @c: controller + * + * Issue a status query on the controller. This updates the + * attached status_block. If the controller fails to reply or an + * error occurs then a negative errno code is returned. On success + * zero is returned and the status_blok is updated. + */ + +int i2o_status_get(struct i2o_controller *c) +{ + long time; + u32 m; + u32 *msg; + u8 *status_block; + + if (c->status_block == NULL) + { + c->status_block = (i2o_status_block *) + kmalloc(sizeof(i2o_status_block),GFP_KERNEL); + if (c->status_block == NULL) + { + printk(KERN_CRIT "%s: Get Status Block failed; Out of memory.\n", + c->name); + return -ENOMEM; + } + } + + status_block = (u8*)c->status_block; + memset(c->status_block,0,sizeof(i2o_status_block)); + + m=i2o_wait_message(c, "StatusGet"); + if(m==0xFFFFFFFF) + return -ETIMEDOUT; + msg=(u32 *)(c->mem_offset+m); + + msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1]=I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID; + msg[2]=core_context; + msg[3]=0; + msg[4]=0; + msg[5]=0; + msg[6]=virt_to_bus(c->status_block); + msg[7]=0; /* 64bit host FIXME */ + msg[8]=sizeof(i2o_status_block); /* always 88 bytes */ + + i2o_post_message(c,m); + + /* Wait for a reply */ + + time=jiffies; + while(status_block[87]!=0xFF) + { + if((jiffies-time)>=5*HZ) + { + printk(KERN_ERR "%s: Get status timeout.\n",c->name); + return -ETIMEDOUT; + } + schedule(); + barrier(); + } + +#ifdef DRIVERDEBUG + printk(KERN_INFO "%s: State = ", c->name); + switch (c->status_block->iop_state) { + case 0x01: + printk("INIT\n"); + break; + case 0x02: + printk("RESET\n"); + break; + case 0x04: + printk("HOLD\n"); + break; + case 0x05: + printk("READY\n"); + break; + case 0x08: + printk("OPERATIONAL\n"); + break; + case 0x10: + printk("FAILED\n"); + break; + case 0x11: + printk("FAULTED\n"); + break; + default: + printk("%x (unknown !!)\n",c->status_block->iop_state); +} +#endif + + return 0; +} + +/* + * Get the Hardware Resource Table for the device. + * The HRT contains information about possible hidden devices + * but is mostly useless to us + */ +int i2o_hrt_get(struct i2o_controller *c) +{ + u32 msg[6]; + int ret, size = sizeof(i2o_hrt); + + /* First read just the header to figure out the real size */ + + do { + if (c->hrt == NULL) { + c->hrt=kmalloc(size, GFP_KERNEL); + if (c->hrt == NULL) { + printk(KERN_CRIT "%s: Hrt Get failed; Out of memory.\n", c->name); + return -ENOMEM; + } + } + + msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4; + msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[3]= 0; + msg[4]= (0xD0000000 | size); /* Simple transaction */ + msg[5]= virt_to_bus(c->hrt); /* Dump it here */ + + ret = i2o_post_wait_mem(c, msg, sizeof(msg), 20, c->hrt, NULL); + + if(ret == -ETIMEDOUT) + { + /* The HRT block we used is in limbo somewhere. When the iop wakes up + we will recover it */ + c->hrt = NULL; + return ret; + } + + if(ret<0) + { + printk(KERN_ERR "%s: Unable to get HRT (status=%#x)\n", + c->name, -ret); + return ret; + } + + if (c->hrt->num_entries * c->hrt->entry_len << 2 > size) { + size = c->hrt->num_entries * c->hrt->entry_len << 2; + kfree(c->hrt); + c->hrt = NULL; + } + } while (c->hrt == NULL); + + i2o_parse_hrt(c); // just for debugging + + return 0; +} + +/* + * Send the I2O System Table to the specified IOP + * + * The system table contains information about all the IOPs in the + * system. It is build and then sent to each IOP so that IOPs can + * establish connections between each other. + * + */ +static int i2o_systab_send(struct i2o_controller *iop) +{ + u32 msg[12]; + int ret; + u32 *privbuf = kmalloc(16, GFP_KERNEL); + if(privbuf == NULL) + return -ENOMEM; + + privbuf[0] = iop->status_block->current_mem_base; + privbuf[1] = iop->status_block->current_mem_size; + privbuf[2] = iop->status_block->current_io_base; + privbuf[3] = iop->status_block->current_io_size; + + msg[0] = I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6; + msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[3] = 0; + msg[4] = (0<<16) | ((iop->unit+2) << 12); /* Host 0 IOP ID (unit + 2) */ + msg[5] = 0; /* Segment 0 */ + + /* + * Provide three SGL-elements: + * System table (SysTab), Private memory space declaration and + * Private i/o space declaration + * + * FIXME: provide these for controllers needing them + */ + msg[6] = 0x54000000 | sys_tbl_len; + msg[7] = virt_to_bus(sys_tbl); + msg[8] = 0x54000000 | 0; + msg[9] = virt_to_bus(privbuf); + msg[10] = 0xD4000000 | 0; + msg[11] = virt_to_bus(privbuf+8); + + ret=i2o_post_wait_mem(iop, msg, sizeof(msg), 120, privbuf, NULL); + + if(ret==-ETIMEDOUT) + { + printk(KERN_ERR "%s: SysTab setup timed out.\n", iop->name); + } + else if(ret<0) + { + printk(KERN_ERR "%s: Unable to set SysTab (status=%#x).\n", + iop->name, -ret); + kfree(privbuf); + } + else + { + dprintk(KERN_INFO "%s: SysTab set.\n", iop->name); + kfree(privbuf); + } + i2o_status_get(iop); // Entered READY state + + return ret; + + } + +/* + * Initialize I2O subsystem. + */ +static void __init i2o_sys_init(void) +{ + struct i2o_controller *iop, *niop = NULL; + + printk(KERN_INFO "Activating I2O controllers...\n"); + printk(KERN_INFO "This may take a few minutes if there are many devices\n"); + + /* In INIT state, Activate IOPs */ + for (iop = i2o_controller_chain; iop; iop = niop) { + dprintk(KERN_INFO "Calling i2o_activate_controller for %s...\n", + iop->name); + niop = iop->next; + if (i2o_activate_controller(iop) < 0) + i2o_delete_controller(iop); + } + + /* Active IOPs in HOLD state */ + +rebuild_sys_tab: + if (i2o_controller_chain == NULL) + return; + + /* + * If build_sys_table fails, we kill everything and bail + * as we can't init the IOPs w/o a system table + */ + dprintk(KERN_INFO "i2o_core: Calling i2o_build_sys_table...\n"); + if (i2o_build_sys_table() < 0) { + i2o_sys_shutdown(); + return; + } + + /* If IOP don't get online, we need to rebuild the System table */ + for (iop = i2o_controller_chain; iop; iop = niop) { + niop = iop->next; + dprintk(KERN_INFO "Calling i2o_online_controller for %s...\n", iop->name); + if (i2o_online_controller(iop) < 0) { + i2o_delete_controller(iop); + goto rebuild_sys_tab; + } + } + + /* Active IOPs now in OPERATIONAL state */ + + /* + * Register for status updates from all IOPs + */ + for(iop = i2o_controller_chain; iop; iop=iop->next) { + + /* Create a kernel thread to deal with dynamic LCT updates */ + iop->lct_pid = kernel_thread(i2o_dyn_lct, iop, CLONE_SIGHAND); + + /* Update change ind on DLCT */ + iop->dlct->change_ind = iop->lct->change_ind; + + /* Start dynamic LCT updates */ + i2o_lct_notify(iop); + + /* Register for all events from IRTOS */ + i2o_event_register(iop, core_context, 0, 0, 0xFFFFFFFF); + } +} + +/** + * i2o_sys_shutdown - shutdown I2O system + * + * Bring down each i2o controller and then return. Each controller + * is taken through an orderly shutdown + */ + +static void i2o_sys_shutdown(void) +{ + struct i2o_controller *iop, *niop; + + /* Delete all IOPs from the controller chain */ + /* that will reset all IOPs too */ + + for (iop = i2o_controller_chain; iop; iop = niop) { + niop = iop->next; + i2o_delete_controller(iop); + } +} + +/** + * i2o_activate_controller - bring controller up to HOLD + * @iop: controller + * + * This function brings an I2O controller into HOLD state. The adapter + * is reset if neccessary and then the queues and resource table + * are read. -1 is returned on a failure, 0 on success. + * + */ + +int i2o_activate_controller(struct i2o_controller *iop) +{ + /* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */ + /* In READY state, Get status */ + + if (i2o_status_get(iop) < 0) { + printk(KERN_INFO "Unable to obtain status of %s, " + "attempting a reset.\n", iop->name); + if (i2o_reset_controller(iop) < 0) + return -1; + } + + if(iop->status_block->iop_state == ADAPTER_STATE_FAULTED) { + printk(KERN_CRIT "%s: hardware fault\n", iop->name); + return -1; + } + + if (iop->status_block->i2o_version > I2OVER15) { + printk(KERN_ERR "%s: Not running vrs. 1.5. of the I2O Specification.\n", + iop->name); + return -1; + } + + if (iop->status_block->iop_state == ADAPTER_STATE_READY || + iop->status_block->iop_state == ADAPTER_STATE_OPERATIONAL || + iop->status_block->iop_state == ADAPTER_STATE_HOLD || + iop->status_block->iop_state == ADAPTER_STATE_FAILED) + { + dprintk(KERN_INFO "%s: Already running, trying to reset...\n", + iop->name); + if (i2o_reset_controller(iop) < 0) + return -1; + } + + if (i2o_init_outbound_q(iop) < 0) + return -1; + + if (i2o_post_outbound_messages(iop)) + return -1; + + /* In HOLD state */ + + if (i2o_hrt_get(iop) < 0) + return -1; + + return 0; +} + + +/** + * i2o_init_outbound_queue - setup the outbound queue + * @c: controller + * + * Clear and (re)initialize IOP's outbound queue. Returns 0 on + * success or a negative errno code on a failure. + */ + +int i2o_init_outbound_q(struct i2o_controller *c) +{ + u8 *status; + u32 m; + u32 *msg; + u32 time; + + dprintk(KERN_INFO "%s: Initializing Outbound Queue...\n", c->name); + m=i2o_wait_message(c, "OutboundInit"); + if(m==0xFFFFFFFF) + return -ETIMEDOUT; + msg=(u32 *)(c->mem_offset+m); + + status = kmalloc(4,GFP_KERNEL); + if (status==NULL) { + printk(KERN_ERR "%s: Outbound Queue initialization failed - no free memory.\n", + c->name); + return -ENOMEM; + } + memset(status, 0, 4); + + msg[0]= EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6; + msg[1]= I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[2]= core_context; + msg[3]= 0x0106; /* Transaction context */ + msg[4]= 4096; /* Host page frame size */ + /* Frame size is in words. Pick 128, its what everyone elses uses and + other sizes break some adapters. */ + msg[5]= MSG_FRAME_SIZE<<16|0x80; /* Outbound msg frame size and Initcode */ + msg[6]= 0xD0000004; /* Simple SG LE, EOB */ + msg[7]= virt_to_bus(status); + + i2o_post_message(c,m); + + barrier(); + time=jiffies; + while(status[0] < I2O_CMD_REJECTED) + { + if((jiffies-time)>=30*HZ) + { + if(status[0]==0x00) + printk(KERN_ERR "%s: Ignored queue initialize request.\n", + c->name); + else + printk(KERN_ERR "%s: Outbound queue initialize timeout.\n", + c->name); + kfree(status); + return -ETIMEDOUT; + } + schedule(); + barrier(); + } + + if(status[0] != I2O_CMD_COMPLETED) + { + printk(KERN_ERR "%s: IOP outbound initialise failed.\n", c->name); + kfree(status); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * i2o_post_outbound_messages - fill message queue + * @c: controller + * + * Allocate a message frame and load the messages into the IOP. The + * function returns zero on success or a negative errno code on + * failure. + */ + +int i2o_post_outbound_messages(struct i2o_controller *c) +{ + int i; + u32 m; + /* Alloc space for IOP's outbound queue message frames */ + + c->page_frame = kmalloc(MSG_POOL_SIZE, GFP_KERNEL); + if(c->page_frame==NULL) { + printk(KERN_CRIT "%s: Outbound Q initialize failed; out of memory.\n", + c->name); + return -ENOMEM; + } + m=virt_to_bus(c->page_frame); + + /* Post frames */ + + for(i=0; i< NMBR_MSG_FRAMES; i++) { + I2O_REPLY_WRITE32(c,m); + mb(); + m += MSG_FRAME_SIZE; + } + + return 0; +} + +/* + * Get the IOP's Logical Configuration Table + */ +int i2o_lct_get(struct i2o_controller *c) +{ + u32 msg[8]; + int ret, size = c->status_block->expected_lct_size; + + do { + if (c->lct == NULL) { + c->lct = kmalloc(size, GFP_KERNEL); + if(c->lct == NULL) { + printk(KERN_CRIT "%s: Lct Get failed. Out of memory.\n", + c->name); + return -ENOMEM; + } + } + memset(c->lct, 0, size); + + msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; + msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; + /* msg[2] filled in i2o_post_wait */ + msg[3] = 0; + msg[4] = 0xFFFFFFFF; /* All devices */ + msg[5] = 0x00000000; /* Report now */ + msg[6] = 0xD0000000|size; + msg[7] = virt_to_bus(c->lct); + + ret=i2o_post_wait_mem(c, msg, sizeof(msg), 120, c->lct, NULL); + + if(ret == -ETIMEDOUT) + { + c->lct = NULL; + return ret; + } + + if(ret<0) + { + printk(KERN_ERR "%s: LCT Get failed (status=%#x.\n", + c->name, -ret); + return ret; + } + + if (c->lct->table_size << 2 > size) { + size = c->lct->table_size << 2; + kfree(c->lct); + c->lct = NULL; + } + } while (c->lct == NULL); + + if ((ret=i2o_parse_lct(c)) < 0) + return ret; + + return 0; +} + +/* + * Like above, but used for async notification. The main + * difference is that we keep track of the CurrentChangeIndiicator + * so that we only get updates when it actually changes. + * + */ +int i2o_lct_notify(struct i2o_controller *c) +{ + u32 msg[8]; + + msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; + msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[2] = core_context; + msg[3] = 0xDEADBEEF; + msg[4] = 0xFFFFFFFF; /* All devices */ + msg[5] = c->dlct->change_ind+1; /* Next change */ + msg[6] = 0xD0000000|8192; + msg[7] = virt_to_bus(c->dlct); + + return i2o_post_this(c, msg, sizeof(msg)); +} + +/* + * Bring a controller online into OPERATIONAL state. + */ + +int i2o_online_controller(struct i2o_controller *iop) +{ + u32 v; + + if (i2o_systab_send(iop) < 0) + return -1; + + /* In READY state */ + + dprintk(KERN_INFO "%s: Attempting to enable...\n", iop->name); + if (i2o_enable_controller(iop) < 0) + return -1; + + /* In OPERATIONAL state */ + + dprintk(KERN_INFO "%s: Attempting to get/parse lct...\n", iop->name); + if (i2o_lct_get(iop) < 0) + return -1; + + /* Check battery status */ + + iop->battery = 0; + if(i2o_query_scalar(iop, ADAPTER_TID, 0x0000, 4, &v, 4)>=0) + { + if(v&16) + iop->battery = 1; + } + + return 0; +} + +/* + * Build system table + * + * The system table contains information about all the IOPs in the + * system (duh) and is used by the Executives on the IOPs to establish + * peer2peer connections. We're not supporting peer2peer at the moment, + * but this will be needed down the road for things like lan2lan forwarding. + */ +static int i2o_build_sys_table(void) +{ + struct i2o_controller *iop = NULL; + struct i2o_controller *niop = NULL; + int count = 0; + + sys_tbl_len = sizeof(struct i2o_sys_tbl) + // Header + IOPs + (i2o_num_controllers) * + sizeof(struct i2o_sys_tbl_entry); + + if(sys_tbl) + kfree(sys_tbl); + + sys_tbl = kmalloc(sys_tbl_len, GFP_KERNEL); + if(!sys_tbl) { + printk(KERN_CRIT "SysTab Set failed. Out of memory.\n"); + return -ENOMEM; + } + memset((void*)sys_tbl, 0, sys_tbl_len); + + sys_tbl->num_entries = i2o_num_controllers; + sys_tbl->version = I2OVERSION; /* TODO: Version 2.0 */ + sys_tbl->change_ind = sys_tbl_ind++; + + for(iop = i2o_controller_chain; iop; iop = niop) + { + niop = iop->next; + + /* + * Get updated IOP state so we have the latest information + * + * We should delete the controller at this point if it + * doesn't respond since if it's not on the system table + * it is techninically not part of the I2O subsyßtem... + */ + if(i2o_status_get(iop)) { + printk(KERN_ERR "%s: Deleting b/c could not get status while" + "attempting to build system table\n", iop->name); + i2o_delete_controller(iop); + sys_tbl->num_entries--; + continue; // try the next one + } + + sys_tbl->iops[count].org_id = iop->status_block->org_id; + sys_tbl->iops[count].iop_id = iop->unit + 2; + sys_tbl->iops[count].seg_num = 0; + sys_tbl->iops[count].i2o_version = + iop->status_block->i2o_version; + sys_tbl->iops[count].iop_state = + iop->status_block->iop_state; + sys_tbl->iops[count].msg_type = + iop->status_block->msg_type; + sys_tbl->iops[count].frame_size = + iop->status_block->inbound_frame_size; + sys_tbl->iops[count].last_changed = sys_tbl_ind - 1; // ?? + sys_tbl->iops[count].iop_capabilities = + iop->status_block->iop_capabilities; + sys_tbl->iops[count].inbound_low = + (u32)virt_to_bus(iop->post_port); + sys_tbl->iops[count].inbound_high = 0; // TODO: 64-bit support + + count++; + } + +#ifdef DRIVERDEBUG +{ + u32 *table; + table = (u32*)sys_tbl; + for(count = 0; count < (sys_tbl_len >>2); count++) + printk(KERN_INFO "sys_tbl[%d] = %0#10x\n", count, table[count]); +} +#endif + + return 0; +} + + +/* + * Run time support routines + */ + +/* + * Generic "post and forget" helpers. This is less efficient - we do + * a memcpy for example that isnt strictly needed, but for most uses + * this is simply not worth optimising + */ + +int i2o_post_this(struct i2o_controller *c, u32 *data, int len) +{ + u32 m; + u32 *msg; + unsigned long t=jiffies; + + do + { + mb(); + m = I2O_POST_READ32(c); + } + while(m==0xFFFFFFFF && (jiffies-t)name); + return -ETIMEDOUT; + } + msg = (u32 *)(c->mem_offset + m); + memcpy_toio(msg, data, len); + i2o_post_message(c,m); + return 0; +} + +/** + * i2o_post_wait_mem - I2O query/reply with DMA buffers + * @c: controller + * @msg: message to send + * @len: length of message + * @timeout: time in seconds to wait + * @mem1: attached memory buffer 1 + * @mem2: attached memory buffer 2 + * + * This core API allows an OSM to post a message and then be told whether + * or not the system received a successful reply. + * + * If the message times out then the value '-ETIMEDOUT' is returned. This + * is a special case. In this situation the message may (should) complete + * at an indefinite time in the future. When it completes it will use the + * memory buffers attached to the request. If -ETIMEDOUT is returned then + * the memory buffers must not be freed. Instead the event completion will + * free them for you. In all other cases the buffers are your problem. + * + * Pass NULL for unneeded buffers. + */ + +int i2o_post_wait_mem(struct i2o_controller *c, u32 *msg, int len, int timeout, void *mem1, void *mem2) +{ + DECLARE_WAIT_QUEUE_HEAD(wq_i2o_post); + int complete = 0; + int status; + int flags = 0; + struct i2o_post_wait_data *wait_data = + kmalloc(sizeof(struct i2o_post_wait_data), GFP_KERNEL); + + if(!wait_data) + return -ENOMEM; + + /* + * Create a new notification object + */ + wait_data->status = &status; + wait_data->complete = &complete; + wait_data->mem[0] = mem1; + wait_data->mem[1] = mem2; + /* + * Queue the event with its unique id + */ + spin_lock_irqsave(&post_wait_lock, flags); + + wait_data->next = post_wait_queue; + post_wait_queue = wait_data; + wait_data->id = (++post_wait_id) & 0x7fff; + wait_data->wq = &wq_i2o_post; + + spin_unlock_irqrestore(&post_wait_lock, flags); + + /* + * Fill in the message id + */ + + msg[2] = 0x80000000|(u32)core_context|((u32)wait_data->id<<16); + + /* + * Post the message to the controller. At some point later it + * will return. If we time out before it returns then + * complete will be zero. From the point post_this returns + * the wait_data may have been deleted. + */ + if ((status = i2o_post_this(c, msg, len))==0) { + sleep_on_timeout(&wq_i2o_post, HZ * timeout); + } + else + return -EIO; + + if(signal_pending(current)) + status = -EINTR; + + spin_lock_irqsave(&post_wait_lock, flags); + barrier(); /* Be sure we see complete as it is locked */ + if(!complete) + { + /* + * Mark the entry dead. We cannot remove it. This is important. + * When it does terminate (which it must do if the controller hasnt + * died..) then it will otherwise scribble on stuff. + * !complete lets us safely check if the entry is still + * allocated and thus we can write into it + */ + wait_data->wq = NULL; + status = -ETIMEDOUT; + } + else + { + /* Debugging check - remove me soon */ + if(status == -ETIMEDOUT) + { + printk("TIMEDOUT BUG!\n"); + status = -EIO; + } + } + /* And the wait_data is not leaked either! */ + spin_unlock_irqrestore(&post_wait_lock, flags); + return status; +} + +/** + * i2o_post_wait - I2O query/reply + * @c: controller + * @msg: message to send + * @len: length of message + * @timeout: time in seconds to wait + * + * This core API allows an OSM to post a message and then be told whether + * or not the system received a successful reply. + */ + +int i2o_post_wait(struct i2o_controller *c, u32 *msg, int len, int timeout) +{ + return i2o_post_wait_mem(c, msg, len, timeout, NULL, NULL); +} + +/* + * i2o_post_wait is completed and we want to wake up the + * sleeping proccess. Called by core's reply handler. + */ + +static void i2o_post_wait_complete(u32 context, int status) +{ + struct i2o_post_wait_data **p1, *q; + unsigned long flags; + + /* + * We need to search through the post_wait + * queue to see if the given message is still + * outstanding. If not, it means that the IOP + * took longer to respond to the message than we + * had allowed and timer has already expired. + * Not much we can do about that except log + * it for debug purposes, increase timeout, and recompile + * + * Lock needed to keep anyone from moving queue pointers + * around while we're looking through them. + */ + + spin_lock_irqsave(&post_wait_lock, flags); + + for(p1 = &post_wait_queue; *p1!=NULL; p1 = &((*p1)->next)) + { + q = (*p1); + if(q->id == ((context >> 16) & 0x7fff)) { + /* + * Delete it + */ + + *p1 = q->next; + + /* + * Live or dead ? + */ + + if(q->wq) + { + /* Live entry - wakeup and set status */ + *q->status = status; + *q->complete = 1; + wake_up(q->wq); + } + else + { + /* + * Free resources. Caller is dead + */ + if(q->mem[0]) + kfree(q->mem[0]); + if(q->mem[1]) + kfree(q->mem[1]); + printk(KERN_WARNING "i2o_post_wait event completed after timeout.\n"); + } + kfree(q); + spin_unlock(&post_wait_lock); + return; + } + } + spin_unlock(&post_wait_lock); + + printk(KERN_DEBUG "i2o_post_wait: Bogus reply!\n"); +} + +/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET + * + * This function can be used for all UtilParamsGet/Set operations. + * The OperationList is given in oplist-buffer, + * and results are returned in reslist-buffer. + * Note that the minimum sized reslist is 8 bytes and contains + * ResultCount, ErrorInfoSize, BlockStatus and BlockSize. + */ +int i2o_issue_params(int cmd, struct i2o_controller *iop, int tid, + void *oplist, int oplen, void *reslist, int reslen) +{ + u32 msg[9]; + u32 *res32 = (u32*)reslist; + u32 *restmp = (u32*)reslist; + int len = 0; + int i = 0; + int wait_status; + u32 *opmem, *resmem; + + /* Get DMAable memory */ + opmem = kmalloc(oplen, GFP_KERNEL); + if(opmem == NULL) + return -ENOMEM; + memcpy(opmem, oplist, oplen); + + resmem = kmalloc(reslen, GFP_KERNEL); + if(resmem == NULL) + { + kfree(opmem); + return -ENOMEM; + } + + msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_5; + msg[1] = cmd << 24 | HOST_TID << 12 | tid; + msg[3] = 0; + msg[4] = 0; + msg[5] = 0x54000000 | oplen; /* OperationList */ + msg[6] = virt_to_bus(opmem); + msg[7] = 0xD0000000 | reslen; /* ResultList */ + msg[8] = virt_to_bus(resmem); + + wait_status = i2o_post_wait_mem(iop, msg, sizeof(msg), 10, opmem, resmem); + + /* + * This only looks like a memory leak - don't "fix" it. + */ + if(wait_status == -ETIMEDOUT) + return wait_status; + + /* Query failed */ + if(wait_status != 0) + { + kfree(resmem); + kfree(opmem); + return wait_status; + } + + memcpy(reslist, resmem, reslen); + /* + * Calculate number of bytes of Result LIST + * We need to loop through each Result BLOCK and grab the length + */ + restmp = res32 + 1; + len = 1; + for(i = 0; i < (res32[0]&0X0000FFFF); i++) + { + if(restmp[0]&0x00FF0000) /* BlockStatus != SUCCESS */ + { + printk(KERN_WARNING "%s - Error:\n ErrorInfoSize = 0x%02x, " + "BlockStatus = 0x%02x, BlockSize = 0x%04x\n", + (cmd == I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET" + : "PARAMS_GET", + res32[1]>>24, (res32[1]>>16)&0xFF, res32[1]&0xFFFF); + + /* + * If this is the only request,than we return an error + */ + if((res32[0]&0x0000FFFF) == 1) + { + return -((res32[1] >> 16) & 0xFF); /* -BlockStatus */ + } + } + len += restmp[0] & 0x0000FFFF; /* Length of res BLOCK */ + restmp += restmp[0] & 0x0000FFFF; /* Skip to next BLOCK */ + } + return (len << 2); /* bytes used by result list */ +} + +/* + * Query one scalar group value or a whole scalar group. + */ +int i2o_query_scalar(struct i2o_controller *iop, int tid, + int group, int field, void *buf, int buflen) +{ + u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field }; + u8 resblk[8+buflen]; /* 8 bytes for header */ + int size; + + if (field == -1) /* whole group */ + opblk[4] = -1; + + size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET, iop, tid, + opblk, sizeof(opblk), resblk, sizeof(resblk)); + + memcpy(buf, resblk+8, buflen); /* cut off header */ + + if(size>buflen) + return buflen; + return size; +} + +/* + * Set a scalar group value or a whole group. + */ +int i2o_set_scalar(struct i2o_controller *iop, int tid, + int group, int field, void *buf, int buflen) +{ + u16 *opblk; + u8 resblk[8+buflen]; /* 8 bytes for header */ + int size; + + opblk = kmalloc(buflen+64, GFP_KERNEL); + if (opblk == NULL) + { + printk(KERN_ERR "i2o: no memory for operation buffer.\n"); + return -ENOMEM; + } + + opblk[0] = 1; /* operation count */ + opblk[1] = 0; /* pad */ + opblk[2] = I2O_PARAMS_FIELD_SET; + opblk[3] = group; + + if(field == -1) { /* whole group */ + opblk[4] = -1; + memcpy(opblk+5, buf, buflen); + } + else /* single field */ + { + opblk[4] = 1; + opblk[5] = field; + memcpy(opblk+6, buf, buflen); + } + + size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, + opblk, 12+buflen, resblk, sizeof(resblk)); + + kfree(opblk); + if(size>buflen) + return buflen; + return size; +} + +/* + * if oper == I2O_PARAMS_TABLE_GET, get from all rows + * if fieldcount == -1 return all fields + * ibuf and ibuflen are unused (use NULL, 0) + * else return specific fields + * ibuf contains fieldindexes + * + * if oper == I2O_PARAMS_LIST_GET, get from specific rows + * if fieldcount == -1 return all fields + * ibuf contains rowcount, keyvalues + * else return specific fields + * fieldcount is # of fieldindexes + * ibuf contains fieldindexes, rowcount, keyvalues + * + * You could also use directly function i2o_issue_params(). + */ +int i2o_query_table(int oper, struct i2o_controller *iop, int tid, int group, + int fieldcount, void *ibuf, int ibuflen, + void *resblk, int reslen) +{ + u16 *opblk; + int size; + + opblk = kmalloc(10 + ibuflen, GFP_KERNEL); + if (opblk == NULL) + { + printk(KERN_ERR "i2o: no memory for query buffer.\n"); + return -ENOMEM; + } + + opblk[0] = 1; /* operation count */ + opblk[1] = 0; /* pad */ + opblk[2] = oper; + opblk[3] = group; + opblk[4] = fieldcount; + memcpy(opblk+5, ibuf, ibuflen); /* other params */ + + size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET,iop, tid, + opblk, 10+ibuflen, resblk, reslen); + + kfree(opblk); + if(size>reslen) + return reslen; + return size; +} + +/* + * Clear table group, i.e. delete all rows. + */ +int i2o_clear_table(struct i2o_controller *iop, int tid, int group) +{ + u16 opblk[] = { 1, 0, I2O_PARAMS_TABLE_CLEAR, group }; + u8 resblk[32]; /* min 8 bytes for result header */ + + return i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, + opblk, sizeof(opblk), resblk, sizeof(resblk)); +} + +/* + * Add a new row into a table group. + * + * if fieldcount==-1 then we add whole rows + * buf contains rowcount, keyvalues + * else just specific fields are given, rest use defaults + * buf contains fieldindexes, rowcount, keyvalues + */ +int i2o_row_add_table(struct i2o_controller *iop, int tid, + int group, int fieldcount, void *buf, int buflen) +{ + u16 *opblk; + u8 resblk[32]; /* min 8 bytes for header */ + int size; + + opblk = kmalloc(buflen+64, GFP_KERNEL); + if (opblk == NULL) + { + printk(KERN_ERR "i2o: no memory for operation buffer.\n"); + return -ENOMEM; + } + + opblk[0] = 1; /* operation count */ + opblk[1] = 0; /* pad */ + opblk[2] = I2O_PARAMS_ROW_ADD; + opblk[3] = group; + opblk[4] = fieldcount; + memcpy(opblk+5, buf, buflen); + + size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_SET, iop, tid, + opblk, 10+buflen, resblk, sizeof(resblk)); + + kfree(opblk); + if(size>buflen) + return buflen; + return size; +} + + +/* + * Used for error reporting/debugging purposes. + * Following fail status are common to all classes. + * The preserved message must be handled in the reply handler. + */ +void i2o_report_fail_status(u8 req_status, u32* msg) +{ + static char *FAIL_STATUS[] = { + "0x80", /* not used */ + "SERVICE_SUSPENDED", /* 0x81 */ + "SERVICE_TERMINATED", /* 0x82 */ + "CONGESTION", + "FAILURE", + "STATE_ERROR", + "TIME_OUT", + "ROUTING_FAILURE", + "INVALID_VERSION", + "INVALID_OFFSET", + "INVALID_MSG_FLAGS", + "FRAME_TOO_SMALL", + "FRAME_TOO_LARGE", + "INVALID_TARGET_ID", + "INVALID_INITIATOR_ID", + "INVALID_INITIATOR_CONTEX", /* 0x8F */ + "UNKNOWN_FAILURE" /* 0xFF */ + }; + + if (req_status == I2O_FSC_TRANSPORT_UNKNOWN_FAILURE) + printk("TRANSPORT_UNKNOWN_FAILURE (%0#2x)\n.", req_status); + else + printk("TRANSPORT_%s.\n", FAIL_STATUS[req_status & 0x0F]); + + /* Dump some details */ + + printk(KERN_ERR " InitiatorId = %d, TargetId = %d\n", + (msg[1] >> 12) & 0xFFF, msg[1] & 0xFFF); + printk(KERN_ERR " LowestVersion = 0x%02X, HighestVersion = 0x%02X\n", + (msg[4] >> 8) & 0xFF, msg[4] & 0xFF); + printk(KERN_ERR " FailingHostUnit = 0x%04X, FailingIOP = 0x%03X\n", + msg[5] >> 16, msg[5] & 0xFFF); + + printk(KERN_ERR " Severity: 0x%02X ", (msg[4] >> 16) & 0xFF); + if (msg[4] & (1<<16)) + printk("(FormatError), " + "this msg can never be delivered/processed.\n"); + if (msg[4] & (1<<17)) + printk("(PathError), " + "this msg can no longer be delivered/processed.\n"); + if (msg[4] & (1<<18)) + printk("(PathState), " + "the system state does not allow delivery.\n"); + if (msg[4] & (1<<19)) + printk("(Congestion), resources temporarily not available;" + "do not retry immediately.\n"); +} + +/* + * Used for error reporting/debugging purposes. + * Following reply status are common to all classes. + */ +void i2o_report_common_status(u8 req_status) +{ + static char *REPLY_STATUS[] = { + "SUCCESS", + "ABORT_DIRTY", + "ABORT_NO_DATA_TRANSFER", + "ABORT_PARTIAL_TRANSFER", + "ERROR_DIRTY", + "ERROR_NO_DATA_TRANSFER", + "ERROR_PARTIAL_TRANSFER", + "PROCESS_ABORT_DIRTY", + "PROCESS_ABORT_NO_DATA_TRANSFER", + "PROCESS_ABORT_PARTIAL_TRANSFER", + "TRANSACTION_ERROR", + "PROGRESS_REPORT" + }; + + if (req_status > I2O_REPLY_STATUS_PROGRESS_REPORT) + printk("RequestStatus = %0#2x", req_status); + else + printk("%s", REPLY_STATUS[req_status]); +} + +/* + * Used for error reporting/debugging purposes. + * Following detailed status are valid for executive class, + * utility class, DDM class and for transaction error replies. + */ +static void i2o_report_common_dsc(u16 detailed_status) +{ + static char *COMMON_DSC[] = { + "SUCCESS", + "0x01", // not used + "BAD_KEY", + "TCL_ERROR", + "REPLY_BUFFER_FULL", + "NO_SUCH_PAGE", + "INSUFFICIENT_RESOURCE_SOFT", + "INSUFFICIENT_RESOURCE_HARD", + "0x08", // not used + "CHAIN_BUFFER_TOO_LARGE", + "UNSUPPORTED_FUNCTION", + "DEVICE_LOCKED", + "DEVICE_RESET", + "INAPPROPRIATE_FUNCTION", + "INVALID_INITIATOR_ADDRESS", + "INVALID_MESSAGE_FLAGS", + "INVALID_OFFSET", + "INVALID_PARAMETER", + "INVALID_REQUEST", + "INVALID_TARGET_ADDRESS", + "MESSAGE_TOO_LARGE", + "MESSAGE_TOO_SMALL", + "MISSING_PARAMETER", + "TIMEOUT", + "UNKNOWN_ERROR", + "UNKNOWN_FUNCTION", + "UNSUPPORTED_VERSION", + "DEVICE_BUSY", + "DEVICE_NOT_AVAILABLE" + }; + + if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE) + printk(" / DetailedStatus = %0#4x.\n", detailed_status); + else + printk(" / %s.\n", COMMON_DSC[detailed_status]); +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_lan_dsc(u16 detailed_status) +{ + static char *LAN_DSC[] = { // Lan detailed status code strings + "SUCCESS", + "DEVICE_FAILURE", + "DESTINATION_NOT_FOUND", + "TRANSMIT_ERROR", + "TRANSMIT_ABORTED", + "RECEIVE_ERROR", + "RECEIVE_ABORTED", + "DMA_ERROR", + "BAD_PACKET_DETECTED", + "OUT_OF_MEMORY", + "BUCKET_OVERRUN", + "IOP_INTERNAL_ERROR", + "CANCELED", + "INVALID_TRANSACTION_CONTEXT", + "DEST_ADDRESS_DETECTED", + "DEST_ADDRESS_OMITTED", + "PARTIAL_PACKET_RETURNED", + "TEMP_SUSPENDED_STATE", // last Lan detailed status code + "INVALID_REQUEST" // general detailed status code + }; + + if (detailed_status > I2O_DSC_INVALID_REQUEST) + printk(" / %0#4x.\n", detailed_status); + else + printk(" / %s.\n", LAN_DSC[detailed_status]); +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_util_cmd(u8 cmd) +{ + switch (cmd) { + case I2O_CMD_UTIL_NOP: + printk("UTIL_NOP, "); + break; + case I2O_CMD_UTIL_ABORT: + printk("UTIL_ABORT, "); + break; + case I2O_CMD_UTIL_CLAIM: + printk("UTIL_CLAIM, "); + break; + case I2O_CMD_UTIL_RELEASE: + printk("UTIL_CLAIM_RELEASE, "); + break; + case I2O_CMD_UTIL_CONFIG_DIALOG: + printk("UTIL_CONFIG_DIALOG, "); + break; + case I2O_CMD_UTIL_DEVICE_RESERVE: + printk("UTIL_DEVICE_RESERVE, "); + break; + case I2O_CMD_UTIL_DEVICE_RELEASE: + printk("UTIL_DEVICE_RELEASE, "); + break; + case I2O_CMD_UTIL_EVT_ACK: + printk("UTIL_EVENT_ACKNOWLEDGE, "); + break; + case I2O_CMD_UTIL_EVT_REGISTER: + printk("UTIL_EVENT_REGISTER, "); + break; + case I2O_CMD_UTIL_LOCK: + printk("UTIL_LOCK, "); + break; + case I2O_CMD_UTIL_LOCK_RELEASE: + printk("UTIL_LOCK_RELEASE, "); + break; + case I2O_CMD_UTIL_PARAMS_GET: + printk("UTIL_PARAMS_GET, "); + break; + case I2O_CMD_UTIL_PARAMS_SET: + printk("UTIL_PARAMS_SET, "); + break; + case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY: + printk("UTIL_REPLY_FAULT_NOTIFY, "); + break; + default: + printk("Cmd = %0#2x, ",cmd); + } +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_exec_cmd(u8 cmd) +{ + switch (cmd) { + case I2O_CMD_ADAPTER_ASSIGN: + printk("EXEC_ADAPTER_ASSIGN, "); + break; + case I2O_CMD_ADAPTER_READ: + printk("EXEC_ADAPTER_READ, "); + break; + case I2O_CMD_ADAPTER_RELEASE: + printk("EXEC_ADAPTER_RELEASE, "); + break; + case I2O_CMD_BIOS_INFO_SET: + printk("EXEC_BIOS_INFO_SET, "); + break; + case I2O_CMD_BOOT_DEVICE_SET: + printk("EXEC_BOOT_DEVICE_SET, "); + break; + case I2O_CMD_CONFIG_VALIDATE: + printk("EXEC_CONFIG_VALIDATE, "); + break; + case I2O_CMD_CONN_SETUP: + printk("EXEC_CONN_SETUP, "); + break; + case I2O_CMD_DDM_DESTROY: + printk("EXEC_DDM_DESTROY, "); + break; + case I2O_CMD_DDM_ENABLE: + printk("EXEC_DDM_ENABLE, "); + break; + case I2O_CMD_DDM_QUIESCE: + printk("EXEC_DDM_QUIESCE, "); + break; + case I2O_CMD_DDM_RESET: + printk("EXEC_DDM_RESET, "); + break; + case I2O_CMD_DDM_SUSPEND: + printk("EXEC_DDM_SUSPEND, "); + break; + case I2O_CMD_DEVICE_ASSIGN: + printk("EXEC_DEVICE_ASSIGN, "); + break; + case I2O_CMD_DEVICE_RELEASE: + printk("EXEC_DEVICE_RELEASE, "); + break; + case I2O_CMD_HRT_GET: + printk("EXEC_HRT_GET, "); + break; + case I2O_CMD_ADAPTER_CLEAR: + printk("EXEC_IOP_CLEAR, "); + break; + case I2O_CMD_ADAPTER_CONNECT: + printk("EXEC_IOP_CONNECT, "); + break; + case I2O_CMD_ADAPTER_RESET: + printk("EXEC_IOP_RESET, "); + break; + case I2O_CMD_LCT_NOTIFY: + printk("EXEC_LCT_NOTIFY, "); + break; + case I2O_CMD_OUTBOUND_INIT: + printk("EXEC_OUTBOUND_INIT, "); + break; + case I2O_CMD_PATH_ENABLE: + printk("EXEC_PATH_ENABLE, "); + break; + case I2O_CMD_PATH_QUIESCE: + printk("EXEC_PATH_QUIESCE, "); + break; + case I2O_CMD_PATH_RESET: + printk("EXEC_PATH_RESET, "); + break; + case I2O_CMD_STATIC_MF_CREATE: + printk("EXEC_STATIC_MF_CREATE, "); + break; + case I2O_CMD_STATIC_MF_RELEASE: + printk("EXEC_STATIC_MF_RELEASE, "); + break; + case I2O_CMD_STATUS_GET: + printk("EXEC_STATUS_GET, "); + break; + case I2O_CMD_SW_DOWNLOAD: + printk("EXEC_SW_DOWNLOAD, "); + break; + case I2O_CMD_SW_UPLOAD: + printk("EXEC_SW_UPLOAD, "); + break; + case I2O_CMD_SW_REMOVE: + printk("EXEC_SW_REMOVE, "); + break; + case I2O_CMD_SYS_ENABLE: + printk("EXEC_SYS_ENABLE, "); + break; + case I2O_CMD_SYS_MODIFY: + printk("EXEC_SYS_MODIFY, "); + break; + case I2O_CMD_SYS_QUIESCE: + printk("EXEC_SYS_QUIESCE, "); + break; + case I2O_CMD_SYS_TAB_SET: + printk("EXEC_SYS_TAB_SET, "); + break; + default: + printk("Cmd = %#02x, ",cmd); + } +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_lan_cmd(u8 cmd) +{ + switch (cmd) { + case LAN_PACKET_SEND: + printk("LAN_PACKET_SEND, "); + break; + case LAN_SDU_SEND: + printk("LAN_SDU_SEND, "); + break; + case LAN_RECEIVE_POST: + printk("LAN_RECEIVE_POST, "); + break; + case LAN_RESET: + printk("LAN_RESET, "); + break; + case LAN_SUSPEND: + printk("LAN_SUSPEND, "); + break; + default: + printk("Cmd = %0#2x, ",cmd); + } +} + +/* + * Used for error reporting/debugging purposes. + * Report Cmd name, Request status, Detailed Status. + */ +void i2o_report_status(const char *severity, const char *str, u32 *msg) +{ + u8 cmd = (msg[1]>>24)&0xFF; + u8 req_status = (msg[4]>>24)&0xFF; + u16 detailed_status = msg[4]&0xFFFF; + struct i2o_handler *h = i2o_handlers[msg[2] & (MAX_I2O_MODULES-1)]; + + printk("%s%s: ", severity, str); + + if (cmd < 0x1F) // Utility cmd + i2o_report_util_cmd(cmd); + + else if (cmd >= 0xA0 && cmd <= 0xEF) // Executive cmd + i2o_report_exec_cmd(cmd); + + else if (h->class == I2O_CLASS_LAN && cmd >= 0x30 && cmd <= 0x3F) + i2o_report_lan_cmd(cmd); // LAN cmd + else + printk("Cmd = %0#2x, ", cmd); // Other cmds + + if (msg[0] & MSG_FAIL) { + i2o_report_fail_status(req_status, msg); + return; + } + + i2o_report_common_status(req_status); + + if (cmd < 0x1F || (cmd >= 0xA0 && cmd <= 0xEF)) + i2o_report_common_dsc(detailed_status); + else if (h->class == I2O_CLASS_LAN && cmd >= 0x30 && cmd <= 0x3F) + i2o_report_lan_dsc(detailed_status); + else + printk(" / DetailedStatus = %0#4x.\n", detailed_status); +} + +/* Used to dump a message to syslog during debugging */ +void i2o_dump_message(u32 *msg) +{ +#ifdef DRIVERDEBUG + int i; + printk(KERN_INFO "Dumping I2O message size %d @ %p\n", + msg[0]>>16&0xffff, msg); + for(i = 0; i < ((msg[0]>>16)&0xffff); i++) + printk(KERN_INFO " msg[%d] = %0#10x\n", i, msg[i]); +#endif +} + +/* + * I2O reboot/shutdown notification. + * + * - Call each OSM's reboot notifier (if one exists) + * - Quiesce each IOP in the system + * + * Each IOP has to be quiesced before we can ensure that the system + * can be properly shutdown as a transaction that has already been + * acknowledged still needs to be placed in permanent store on the IOP. + * The SysQuiesce causes the IOP to force all HDMs to complete their + * transactions before returning, so only at that point is it safe + * + */ +static int i2o_reboot_event(struct notifier_block *n, unsigned long code, void +*p) +{ + int i = 0; + struct i2o_controller *c = NULL; + + if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF) + return NOTIFY_DONE; + + printk(KERN_INFO "Shutting down I2O system.\n"); + printk(KERN_INFO + " This could take a few minutes if there are many devices attached\n"); + + for(i = 0; i < MAX_I2O_MODULES; i++) + { + if(i2o_handlers[i] && i2o_handlers[i]->reboot_notify) + i2o_handlers[i]->reboot_notify(); + } + + for(c = i2o_controller_chain; c; c = c->next) + { + if(i2o_quiesce_controller(c)) + { + printk(KERN_WARNING "i2o: Could not quiesce %s." " + Verify setup on next system power up.\n", c->name); + } + } + + printk(KERN_INFO "I2O system down.\n"); + return NOTIFY_DONE; +} + + +EXPORT_SYMBOL(i2o_controller_chain); +EXPORT_SYMBOL(i2o_num_controllers); +EXPORT_SYMBOL(i2o_find_controller); +EXPORT_SYMBOL(i2o_unlock_controller); +EXPORT_SYMBOL(i2o_status_get); + +EXPORT_SYMBOL(i2o_install_handler); +EXPORT_SYMBOL(i2o_remove_handler); + +EXPORT_SYMBOL(i2o_claim_device); +EXPORT_SYMBOL(i2o_release_device); +EXPORT_SYMBOL(i2o_device_notify_on); +EXPORT_SYMBOL(i2o_device_notify_off); + +EXPORT_SYMBOL(i2o_post_this); +EXPORT_SYMBOL(i2o_post_wait); +EXPORT_SYMBOL(i2o_post_wait_mem); + +EXPORT_SYMBOL(i2o_query_scalar); +EXPORT_SYMBOL(i2o_set_scalar); +EXPORT_SYMBOL(i2o_query_table); +EXPORT_SYMBOL(i2o_clear_table); +EXPORT_SYMBOL(i2o_row_add_table); +EXPORT_SYMBOL(i2o_issue_params); + +EXPORT_SYMBOL(i2o_event_register); +EXPORT_SYMBOL(i2o_event_ack); + +EXPORT_SYMBOL(i2o_report_status); +EXPORT_SYMBOL(i2o_dump_message); + +EXPORT_SYMBOL(i2o_get_class_name); + +#ifdef MODULE + +MODULE_AUTHOR("Red Hat Software"); +MODULE_DESCRIPTION("I2O Core"); + + +int init_module(void) +{ + printk(KERN_INFO "I2O Core - (C) Copyright 1999 Red Hat Software\n"); + if (i2o_install_handler(&i2o_core_handler) < 0) + { + printk(KERN_ERR + "i2o_core: Unable to install core handler.\nI2O stack not loaded!"); + return 0; + } + + core_context = i2o_core_handler.context; + + /* + * Attach core to I2O PCI transport (and others as they are developed) + */ +#ifdef CONFIG_I2O_PCI_MODULE + if(i2o_pci_core_attach(&i2o_core_functions) < 0) + printk(KERN_INFO "i2o: No PCI I2O controllers found\n"); +#endif + + /* + * Initialize event handling thread + */ + init_MUTEX_LOCKED(&evt_sem); + evt_pid = kernel_thread(i2o_core_evt, &evt_reply, CLONE_SIGHAND); + if(evt_pid < 0) + { + printk(KERN_ERR "I2O: Could not create event handler kernel thread\n"); + i2o_remove_handler(&i2o_core_handler); + return 0; + } + else + printk(KERN_INFO "I2O: Event thread created as pid %d\n", evt_pid); + + if(i2o_num_controllers) + i2o_sys_init(); + + register_reboot_notifier(&i2o_reboot_notifier); + + return 0; +} + +void cleanup_module(void) +{ + int stat; + + unregister_reboot_notifier(&i2o_reboot_notifier); + + if(i2o_num_controllers) + i2o_sys_shutdown(); + + /* + * If this is shutdown time, the thread has already been killed + */ + if(evt_running) { + printk("Terminating i2o threads..."); + stat = kill_proc(evt_pid, SIGTERM, 1); + if(!stat) { + printk("waiting..."); + down(&evt_dead); + } + printk("done.\n"); + } + +#ifdef CONFIG_I2O_PCI_MODULE + i2o_pci_core_detach(); +#endif + + i2o_remove_handler(&i2o_core_handler); + + unregister_reboot_notifier(&i2o_reboot_notifier); +} + +#else + +extern int i2o_block_init(void); +extern int i2o_config_init(void); +extern int i2o_lan_init(void); +extern int i2o_pci_init(void); +extern int i2o_proc_init(void); +extern int i2o_scsi_init(void); + +int __init i2o_init(void) +{ + printk(KERN_INFO "Loading I2O Core - (c) Copyright 1999 Red Hat Software\n"); + + if (i2o_install_handler(&i2o_core_handler) < 0) + { + printk(KERN_ERR + "i2o_core: Unable to install core handler.\nI2O stack not loaded!"); + return 0; + } + + core_context = i2o_core_handler.context; + + /* + * Initialize event handling thread + * We may not find any controllers, but still want this as + * down the road we may have hot pluggable controllers that + * need to be dealt with. + */ + init_MUTEX_LOCKED(&evt_sem); + if((evt_pid = kernel_thread(i2o_core_evt, &evt_reply, CLONE_SIGHAND)) < 0) + { + printk(KERN_ERR "I2O: Could not create event handler kernel thread\n"); + i2o_remove_handler(&i2o_core_handler); + return 0; + } + + +#ifdef CONFIG_I2O_PCI + i2o_pci_init(); +#endif + + if(i2o_num_controllers) + i2o_sys_init(); + + register_reboot_notifier(&i2o_reboot_notifier); + + i2o_config_init(); +#ifdef CONFIG_I2O_BLOCK + i2o_block_init(); +#endif +#ifdef CONFIG_I2O_LAN + i2o_lan_init(); +#endif +#ifdef CONFIG_I2O_PROC + i2o_proc_init(); +#endif + return 0; +} + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_lan.c linux.ac/drivers/message/i2o/i2o_lan.c --- linux.vanilla/drivers/message/i2o/i2o_lan.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_lan.c Tue Apr 3 17:54:47 2001 @@ -0,0 +1,1575 @@ +/* + * drivers/i2o/i2o_lan.c + * + * I2O LAN CLASS OSM May 26th 2000 + * + * (C) Copyright 1999, 2000 University of Helsinki, + * Department of Computer Science + * + * This code is still under development / test. + * + * 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. + * + * Authors: Auvo Häkkinen + * Fixes: Juha Sievänen + * Taneli Vähäkangas + * Deepak Saxena + * + * Tested: in FDDI environment (using SysKonnect's DDM) + * in Gigabit Eth environment (using SysKonnect's DDM) + * in Fast Ethernet environment (using Intel 82558 DDM) + * + * TODO: tests for other LAN classes (Token Ring, Fibre Channel) + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "i2o_lan.h" + +//#define DRIVERDEBUG +#ifdef DRIVERDEBUG +#define dprintk(s, args...) printk(s, ## args) +#else +#define dprintk(s, args...) +#endif + +/* The following module parameters are used as default values + * for per interface values located in the net_device private area. + * Private values are changed via /proc filesystem. + */ +static u32 max_buckets_out = I2O_LAN_MAX_BUCKETS_OUT; +static u32 bucket_thresh = I2O_LAN_BUCKET_THRESH; +static u32 rx_copybreak = I2O_LAN_RX_COPYBREAK; +static u8 tx_batch_mode = I2O_LAN_TX_BATCH_MODE; +static u32 i2o_event_mask = I2O_LAN_EVENT_MASK; + +#define MAX_LAN_CARDS 16 +static struct net_device *i2o_landevs[MAX_LAN_CARDS+1]; +static int unit = -1; /* device unit number */ + +static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); +static void i2o_lan_send_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); +static int i2o_lan_receive_post(struct net_device *dev); +static void i2o_lan_receive_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); +static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg); + +static int i2o_lan_reset(struct net_device *dev); +static void i2o_lan_handle_event(struct net_device *dev, u32 *msg); + +/* Structures to register handlers for the incoming replies. */ + +static struct i2o_handler i2o_lan_send_handler = { + i2o_lan_send_post_reply, // For send replies + NULL, + NULL, + NULL, + "I2O LAN OSM send", + -1, + I2O_CLASS_LAN +}; +static int lan_send_context; + +static struct i2o_handler i2o_lan_receive_handler = { + i2o_lan_receive_post_reply, // For receive replies + NULL, + NULL, + NULL, + "I2O LAN OSM receive", + -1, + I2O_CLASS_LAN +}; +static int lan_receive_context; + +static struct i2o_handler i2o_lan_handler = { + i2o_lan_reply, // For other replies + NULL, + NULL, + NULL, + "I2O LAN OSM", + -1, + I2O_CLASS_LAN +}; +static int lan_context; + +DECLARE_TASK_QUEUE(i2o_post_buckets_task); +struct tq_struct run_i2o_post_buckets_task = { + routine: (void (*)(void *)) run_task_queue, + data: (void *) 0 +}; + +/* Functions to handle message failures and transaction errors: +==============================================================*/ + +/* + * i2o_lan_handle_failure(): Fail bit has been set since IOP's message + * layer cannot deliver the request to the target, or the target cannot + * process the request. + */ +static void i2o_lan_handle_failure(struct net_device *dev, u32 *msg) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + + u32 *preserved_msg = (u32*)(iop->mem_offset + msg[7]); + u32 *sgl_elem = &preserved_msg[4]; + struct sk_buff *skb = NULL; + u8 le_flag; + + i2o_report_status(KERN_INFO, dev->name, msg); + + /* If PacketSend failed, free sk_buffs reserved by upper layers */ + + if (msg[1] >> 24 == LAN_PACKET_SEND) { + do { + skb = (struct sk_buff *)(sgl_elem[1]); + dev_kfree_skb_irq(skb); + + atomic_dec(&priv->tx_out); + + le_flag = *sgl_elem >> 31; + sgl_elem +=3; + } while (le_flag == 0); /* Last element flag not set */ + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } + + /* If ReceivePost failed, free sk_buffs we have reserved */ + + if (msg[1] >> 24 == LAN_RECEIVE_POST) { + do { + skb = (struct sk_buff *)(sgl_elem[1]); + dev_kfree_skb_irq(skb); + + atomic_dec(&priv->buckets_out); + + le_flag = *sgl_elem >> 31; + sgl_elem +=3; + } while (le_flag == 0); /* Last element flag not set */ + } + + /* Release the preserved msg frame by resubmitting it as a NOP */ + + preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; + preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; + preserved_msg[2] = 0; + i2o_post_message(iop, msg[7]); +} +/* + * i2o_lan_handle_transaction_error(): IOP or DDM has rejected the request + * for general cause (format error, bad function code, insufficient resources, + * etc.). We get one transaction_error for each failed transaction. + */ +static void i2o_lan_handle_transaction_error(struct net_device *dev, u32 *msg) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct sk_buff *skb; + + i2o_report_status(KERN_INFO, dev->name, msg); + + /* If PacketSend was rejected, free sk_buff reserved by upper layers */ + + if (msg[1] >> 24 == LAN_PACKET_SEND) { + skb = (struct sk_buff *)(msg[3]); // TransactionContext + dev_kfree_skb_irq(skb); + atomic_dec(&priv->tx_out); + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } + + /* If ReceivePost was rejected, free sk_buff we have reserved */ + + if (msg[1] >> 24 == LAN_RECEIVE_POST) { + skb = (struct sk_buff *)(msg[3]); + dev_kfree_skb_irq(skb); + atomic_dec(&priv->buckets_out); + } +} + +/* + * i2o_lan_handle_status(): Common parts of handling a not succeeded request + * (status != SUCCESS). + */ +static int i2o_lan_handle_status(struct net_device *dev, u32 *msg) +{ + /* Fail bit set? */ + + if (msg[0] & MSG_FAIL) { + i2o_lan_handle_failure(dev, msg); + return -1; + } + + /* Message rejected for general cause? */ + + if ((msg[4]>>24) == I2O_REPLY_STATUS_TRANSACTION_ERROR) { + i2o_lan_handle_transaction_error(dev, msg); + return -1; + } + + /* Else have to handle it in the callback function */ + + return 0; +} + +/* Callback functions called from the interrupt routine: +=======================================================*/ + +/* + * i2o_lan_send_post_reply(): Callback function to handle PostSend replies. + */ +static void i2o_lan_send_post_reply(struct i2o_handler *h, + struct i2o_controller *iop, struct i2o_message *m) +{ + u32 *msg = (u32 *)m; + u8 unit = (u8)(msg[2]>>16); // InitiatorContext + struct net_device *dev = i2o_landevs[unit]; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + u8 trl_count = msg[3] & 0x000000FF; + + if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { + if (i2o_lan_handle_status(dev, msg)) + return; + } + +#ifdef DRIVERDEBUG + i2o_report_status(KERN_INFO, dev->name, msg); +#endif + + /* DDM has handled transmit request(s), free sk_buffs. + * We get similar single transaction reply also in error cases + * (except if msg failure or transaction error). + */ + while (trl_count) { + dev_kfree_skb_irq((struct sk_buff *)msg[4 + trl_count]); + dprintk(KERN_INFO "%s: tx skb freed (trl_count=%d).\n", + dev->name, trl_count); + atomic_dec(&priv->tx_out); + trl_count--; + } + + /* If priv->tx_out had reached tx_max_out, the queue was stopped */ + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); +} + +/* + * i2o_lan_receive_post_reply(): Callback function to process incoming packets. + */ +static void i2o_lan_receive_post_reply(struct i2o_handler *h, + struct i2o_controller *iop, struct i2o_message *m) +{ + u32 *msg = (u32 *)m; + u8 unit = (u8)(msg[2]>>16); // InitiatorContext + struct net_device *dev = i2o_landevs[unit]; + + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_bucket_descriptor *bucket = (struct i2o_bucket_descriptor *)&msg[6]; + struct i2o_packet_info *packet; + u8 trl_count = msg[3] & 0x000000FF; + struct sk_buff *skb, *old_skb; + unsigned long flags = 0; + + if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { + if (i2o_lan_handle_status(dev, msg)) + return; + + i2o_lan_release_buckets(dev, msg); + return; + } + +#ifdef DRIVERDEBUG + i2o_report_status(KERN_INFO, dev->name, msg); +#endif + + /* Else we are receiving incoming post. */ + + while (trl_count--) { + skb = (struct sk_buff *)bucket->context; + packet = (struct i2o_packet_info *)bucket->packet_info; + atomic_dec(&priv->buckets_out); + + /* Sanity checks: Any weird characteristics in bucket? */ + + if (packet->flags & 0x0f || ! packet->flags & 0x40) { + if (packet->flags & 0x01) + printk(KERN_WARNING "%s: packet with errors, error code=0x%02x.\n", + dev->name, packet->status & 0xff); + + /* The following shouldn't happen, unless parameters in + * LAN_OPERATION group are changed during the run time. + */ + if (packet->flags & 0x0c) + printk(KERN_DEBUG "%s: multi-bucket packets not supported!\n", + dev->name); + + if (! packet->flags & 0x40) + printk(KERN_DEBUG "%s: multiple packets in a bucket not supported!\n", + dev->name); + + dev_kfree_skb_irq(skb); + + bucket++; + continue; + } + + /* Copy short packet to a new skb */ + + if (packet->len < priv->rx_copybreak) { + old_skb = skb; + skb = (struct sk_buff *)dev_alloc_skb(packet->len+2); + if (skb == NULL) { + printk(KERN_ERR "%s: Can't allocate skb.\n", dev->name); + return; + } + skb_reserve(skb, 2); + memcpy(skb_put(skb, packet->len), old_skb->data, packet->len); + + spin_lock_irqsave(&priv->fbl_lock, flags); + if (priv->i2o_fbl_tail < I2O_LAN_MAX_BUCKETS_OUT) + priv->i2o_fbl[++priv->i2o_fbl_tail] = old_skb; + else + dev_kfree_skb_irq(old_skb); + + spin_unlock_irqrestore(&priv->fbl_lock, flags); + } else + skb_put(skb, packet->len); + + /* Deliver to upper layers */ + + skb->dev = dev; + skb->protocol = priv->type_trans(skb, dev); + netif_rx(skb); + + dev->last_rx = jiffies; + + dprintk(KERN_INFO "%s: Incoming packet (%d bytes) delivered " + "to upper level.\n", dev->name, packet->len); + + bucket++; // to next Packet Descriptor Block + } + +#ifdef DRIVERDEBUG + if (msg[5] == 0) + printk(KERN_INFO "%s: DDM out of buckets (priv->count = %d)!\n", + dev->name, atomic_read(&priv->buckets_out)); +#endif + + /* If DDM has already consumed bucket_thresh buckets, post new ones */ + + if (atomic_read(&priv->buckets_out) <= priv->max_buckets_out - priv->bucket_thresh) { + run_i2o_post_buckets_task.data = (void *)dev; + queue_task(&run_i2o_post_buckets_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + + return; +} + +/* + * i2o_lan_reply(): Callback function to handle other incoming messages + * except SendPost and ReceivePost. + */ +static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, + struct i2o_message *m) +{ + u32 *msg = (u32 *)m; + u8 unit = (u8)(msg[2]>>16); // InitiatorContext + struct net_device *dev = i2o_landevs[unit]; + + if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { + if (i2o_lan_handle_status(dev, msg)) + return; + + /* In other error cases just report and continue */ + + i2o_report_status(KERN_INFO, dev->name, msg); + } + +#ifdef DRIVERDEBUG + i2o_report_status(KERN_INFO, dev->name, msg); +#endif + switch (msg[1] >> 24) { + case LAN_RESET: + case LAN_SUSPEND: + /* default reply without payload */ + break; + + case I2O_CMD_UTIL_EVT_REGISTER: + case I2O_CMD_UTIL_EVT_ACK: + i2o_lan_handle_event(dev, msg); + break; + + case I2O_CMD_UTIL_PARAMS_SET: + /* default reply, results in ReplyPayload (not examined) */ + switch (msg[3] >> 16) { + case 1: dprintk(KERN_INFO "%s: Reply to set MAC filter mask.\n", + dev->name); + break; + case 2: dprintk(KERN_INFO "%s: Reply to set MAC table.\n", + dev->name); + break; + default: printk(KERN_WARNING "%s: Bad group 0x%04X\n", + dev->name,msg[3] >> 16); + } + break; + + default: + printk(KERN_ERR "%s: No handler for the reply.\n", + dev->name); + i2o_report_status(KERN_INFO, dev->name, msg); + } +} + +/* Functions used by the above callback functions: +=================================================*/ +/* + * i2o_lan_release_buckets(): Free unused buckets (sk_buffs). + */ +static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + u8 trl_elem_size = (u8)(msg[3]>>8 & 0x000000FF); + u8 trl_count = (u8)(msg[3] & 0x000000FF); + u32 *pskb = &msg[6]; + + while (trl_count--) { + dprintk(KERN_DEBUG "%s: Releasing unused rx skb %p (trl_count=%d).\n", + dev->name, (struct sk_buff*)(*pskb),trl_count+1); + dev_kfree_skb_irq((struct sk_buff *)(*pskb)); + pskb += 1 + trl_elem_size; + atomic_dec(&priv->buckets_out); + } +} + +/* + * i2o_lan_event_reply(): Handle events. + */ +static void i2o_lan_handle_event(struct net_device *dev, u32 *msg) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 max_evt_data_size =iop->status_block->inbound_frame_size-5; + struct i2o_reply { + u32 header[4]; + u32 evt_indicator; + u32 data[max_evt_data_size]; + } *evt = (struct i2o_reply *)msg; + int evt_data_len = ((msg[0]>>16) - 5) * 4; /* real size*/ + + printk(KERN_INFO "%s: I2O event - ", dev->name); + + if (msg[1]>>24 == I2O_CMD_UTIL_EVT_ACK) { + printk("Event acknowledgement reply.\n"); + return; + } + + /* Else evt->function == I2O_CMD_UTIL_EVT_REGISTER) */ + + switch (evt->evt_indicator) { + case I2O_EVT_IND_STATE_CHANGE: { + struct state_data { + u16 status; + u8 state; + u8 data; + } *evt_data = (struct state_data *)(evt->data[0]); + + printk("State chance 0x%08x.\n", evt->data[0]); + + /* If the DDM is in error state, recovery may be + * possible if status = Transmit or Receive Control + * Unit Inoperable. + */ + if (evt_data->state==0x05 && evt_data->status==0x0003) + i2o_lan_reset(dev); + break; + } + + case I2O_EVT_IND_FIELD_MODIFIED: { + u16 *work16 = (u16 *)evt->data; + printk("Group 0x%04x, field %d changed.\n", work16[0], work16[1]); + break; + } + + case I2O_EVT_IND_VENDOR_EVT: { + int i; + printk("Vendor event:\n"); + for (i = 0; i < evt_data_len / 4; i++) + printk(" 0x%08x\n", evt->data[i]); + break; + } + + case I2O_EVT_IND_DEVICE_RESET: + /* Spec 2.0 p. 6-121: + * The event of _DEVICE_RESET should also be responded + */ + printk("Device reset.\n"); + if (i2o_event_ack(iop, msg) < 0) + printk("%s: Event Acknowledge timeout.\n", dev->name); + break; + +#if 0 + case I2O_EVT_IND_EVT_MASK_MODIFIED: + printk("Event mask modified, 0x%08x.\n", evt->data[0]); + break; + + case I2O_EVT_IND_GENERAL_WARNING: + printk("General warning 0x%04x.\n", evt->data[0]); + break; + + case I2O_EVT_IND_CONFIGURATION_FLAG: + printk("Configuration requested.\n"); + break; + + case I2O_EVT_IND_CAPABILITY_CHANGE: + printk("Capability change 0x%04x.\n", evt->data[0]); + break; + + case I2O_EVT_IND_DEVICE_STATE: + printk("Device state changed 0x%08x.\n", evt->data[0]); + break; +#endif + case I2O_LAN_EVT_LINK_DOWN: + netif_carrier_off(dev); + printk("Link to the physical device is lost.\n"); + break; + + case I2O_LAN_EVT_LINK_UP: + netif_carrier_on(dev); + printk("Link to the physical device is (re)established.\n"); + break; + + case I2O_LAN_EVT_MEDIA_CHANGE: + printk("Media change.\n"); + break; + default: + printk("0x%08x. No handler.\n", evt->evt_indicator); + } +} + +/* + * i2o_lan_receive_post(): Post buckets to receive packets. + */ +static int i2o_lan_receive_post(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + struct sk_buff *skb; + u32 m, *msg; + u32 bucket_len = (dev->mtu + dev->hard_header_len); + u32 total = priv->max_buckets_out - atomic_read(&priv->buckets_out); + u32 bucket_count; + u32 *sgl_elem; + unsigned long flags; + + /* Send (total/bucket_count) separate I2O requests */ + + while (total) { + m = I2O_POST_READ32(iop); + if (m == 0xFFFFFFFF) + return -ETIMEDOUT; + msg = (u32 *)(iop->mem_offset + m); + + bucket_count = (total >= priv->sgl_max) ? priv->sgl_max : total; + total -= bucket_count; + atomic_add(bucket_count, &priv->buckets_out); + + dprintk(KERN_INFO "%s: Sending %d buckets (size %d) to LAN DDM.\n", + dev->name, bucket_count, bucket_len); + + /* Fill in the header */ + + __raw_writel(I2O_MESSAGE_SIZE(4 + 3 * bucket_count) | SGL_OFFSET_4, msg); + __raw_writel(LAN_RECEIVE_POST<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); + __raw_writel(priv->unit << 16 | lan_receive_context, msg+2); + __raw_writel(bucket_count, msg+3); + sgl_elem = &msg[4]; + + /* Fill in the payload - contains bucket_count SGL elements */ + + while (bucket_count--) { + spin_lock_irqsave(&priv->fbl_lock, flags); + if (priv->i2o_fbl_tail >= 0) + skb = priv->i2o_fbl[priv->i2o_fbl_tail--]; + else { + skb = dev_alloc_skb(bucket_len + 2); + if (skb == NULL) { + spin_unlock_irqrestore(&priv->fbl_lock, flags); + return -ENOMEM; + } + skb_reserve(skb, 2); + } + spin_unlock_irqrestore(&priv->fbl_lock, flags); + + __raw_writel(0x51000000 | bucket_len, sgl_elem); + __raw_writel((u32)skb, sgl_elem+1); + __raw_writel(virt_to_bus(skb->data), sgl_elem+2); + sgl_elem += 3; + } + + /* set LE flag and post */ + __raw_writel(__raw_readl(sgl_elem-3) | 0x80000000, (sgl_elem-3)); + i2o_post_message(iop, m); + } + + return 0; +} + +/* Functions called from the network stack, and functions called by them: +========================================================================*/ + +/* + * i2o_lan_reset(): Reset the LAN adapter into the operational state and + * restore it to full operation. + */ +static int i2o_lan_reset(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 msg[5]; + + dprintk(KERN_INFO "%s: LAN RESET MESSAGE.\n", dev->name); + msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; + msg[1] = LAN_RESET<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid; + msg[2] = priv->unit << 16 | lan_context; // InitiatorContext + msg[3] = 0; // TransactionContext + msg[4] = 0; // Keep posted buckets + + if (i2o_post_this(iop, msg, sizeof(msg)) < 0) + return -ETIMEDOUT; + + return 0; +} + +/* + * i2o_lan_suspend(): Put LAN adapter into a safe, non-active state. + * IOP replies to any LAN class message with status error_no_data_transfer + * / suspended. + */ +static int i2o_lan_suspend(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 msg[5]; + + dprintk(KERN_INFO "%s: LAN SUSPEND MESSAGE.\n", dev->name); + msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; + msg[1] = LAN_SUSPEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid; + msg[2] = priv->unit << 16 | lan_context; // InitiatorContext + msg[3] = 0; // TransactionContext + msg[4] = 1 << 16; // return posted buckets + + if (i2o_post_this(iop, msg, sizeof(msg)) < 0) + return -ETIMEDOUT; + + return 0; +} + +/* + * i2o_set_ddm_parameters: + * These settings are done to ensure proper initial values for DDM. + * They can be changed via proc file system or vai configuration utility. + */ +static void i2o_set_ddm_parameters(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 val; + + /* + * When PacketOrphanlimit is set to the maximum packet length, + * the packets will never be split into two separate buckets + */ + val = dev->mtu + dev->hard_header_len; + if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0004, 2, &val, sizeof(val)) < 0) + printk(KERN_WARNING "%s: Unable to set PacketOrphanLimit.\n", + dev->name); + else + dprintk(KERN_INFO "%s: PacketOrphanLimit set to %d.\n", + dev->name, val); + + /* When RxMaxPacketsBucket = 1, DDM puts only one packet into bucket */ + + val = 1; + if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0008, 4, &val, sizeof(val)) <0) + printk(KERN_WARNING "%s: Unable to set RxMaxPacketsBucket.\n", + dev->name); + else + dprintk(KERN_INFO "%s: RxMaxPacketsBucket set to %d.\n", + dev->name, val); + return; +} + +/* Functions called from the network stack: +==========================================*/ + +/* + * i2o_lan_open(): Open the device to send/receive packets via + * the network device. + */ +static int i2o_lan_open(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 mc_addr_group[64]; + + MOD_INC_USE_COUNT; + + if (i2o_claim_device(i2o_dev, &i2o_lan_handler)) { + printk(KERN_WARNING "%s: Unable to claim the I2O LAN device.\n", dev->name); + MOD_DEC_USE_COUNT; + return -EAGAIN; + } + dprintk(KERN_INFO "%s: I2O LAN device (tid=%d) claimed by LAN OSM.\n", + dev->name, i2o_dev->lct_data.tid); + + if (i2o_event_register(iop, i2o_dev->lct_data.tid, + priv->unit << 16 | lan_context, 0, priv->i2o_event_mask) < 0) + printk(KERN_WARNING "%s: Unable to set the event mask.\n", dev->name); + + i2o_lan_reset(dev); + + /* Get the max number of multicast addresses */ + + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0001, -1, + &mc_addr_group, sizeof(mc_addr_group)) < 0 ) { + printk(KERN_WARNING "%s: Unable to query LAN_MAC_ADDRESS group.\n", dev->name); + MOD_DEC_USE_COUNT; + return -EAGAIN; + } + priv->max_size_mc_table = mc_addr_group[8]; + + /* Malloc space for free bucket list to resuse reveive post buckets */ + + priv->i2o_fbl = kmalloc(priv->max_buckets_out * sizeof(struct sk_buff *), + GFP_KERNEL); + if (priv->i2o_fbl == NULL) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + priv->i2o_fbl_tail = -1; + priv->send_active = 0; + + i2o_set_ddm_parameters(dev); + i2o_lan_receive_post(dev); + + netif_start_queue(dev); + + return 0; +} + +/* + * i2o_lan_close(): End the transfering. + */ +static int i2o_lan_close(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + int ret = 0; + + netif_stop_queue(dev); + i2o_lan_suspend(dev); + + if (i2o_event_register(iop, i2o_dev->lct_data.tid, + priv->unit << 16 | lan_context, 0, 0) < 0) + printk(KERN_WARNING "%s: Unable to clear the event mask.\n", + dev->name); + + while (priv->i2o_fbl_tail >= 0) + dev_kfree_skb(priv->i2o_fbl[priv->i2o_fbl_tail--]); + + kfree(priv->i2o_fbl); + + if (i2o_release_device(i2o_dev, &i2o_lan_handler)) { + printk(KERN_WARNING "%s: Unable to unclaim I2O LAN device " + "(tid=%d).\n", dev->name, i2o_dev->lct_data.tid); + ret = -EBUSY; + } + + MOD_DEC_USE_COUNT; + + return ret; +} + +/* + * i2o_lan_tx_timeout(): Tx timeout handler. + */ +static void i2o_lan_tx_timeout(struct net_device *dev) +{ + if (!netif_queue_stopped(dev)) + netif_start_queue(dev); +} + +/* + * i2o_lan_batch_send(): Send packets in batch. + * Both i2o_lan_sdu_send and i2o_lan_packet_send use this. + */ +static void i2o_lan_batch_send(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_controller *iop = priv->i2o_dev->controller; + + spin_lock_irq(&priv->tx_lock); + if (priv->tx_count != 0) { + dev->trans_start = jiffies; + i2o_post_message(iop, priv->m); + dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); + priv->tx_count = 0; + } + priv->send_active = 0; + spin_unlock_irq(&priv->tx_lock); + MOD_DEC_USE_COUNT; +} + +#ifdef CONFIG_NET_FC +/* + * i2o_lan_sdu_send(): Send a packet, MAC header added by the DDM. + * Must be supported by Fibre Channel, optional for Ethernet/802.3, + * Token Ring, FDDI + */ +static int i2o_lan_sdu_send(struct sk_buff *skb, struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + int tickssofar = jiffies - dev->trans_start; + u32 m, *msg; + u32 *sgl_elem; + + spin_lock_irq(&priv->tx_lock); + + priv->tx_count++; + atomic_inc(&priv->tx_out); + + /* + * If tx_batch_mode = 0x00 forced to immediate mode + * If tx_batch_mode = 0x01 forced to batch mode + * If tx_batch_mode = 0x10 switch automatically, current mode immediate + * If tx_batch_mode = 0x11 switch automatically, current mode batch + * If gap between two packets is > 0 ticks, switch to immediate + */ + if (priv->tx_batch_mode >> 1) // switch automatically + priv->tx_batch_mode = tickssofar ? 0x02 : 0x03; + + if (priv->tx_count == 1) { + m = I2O_POST_READ32(iop); + if (m == 0xFFFFFFFF) { + spin_unlock_irq(&priv->tx_lock); + return 1; + } + msg = (u32 *)(iop->mem_offset + m); + priv->m = m; + + __raw_writel(NINE_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4, msg); + __raw_writel(LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); + __raw_writel(priv->unit << 16 | lan_send_context, msg+2); // InitiatorContext + __raw_writel(1 << 30 | 1 << 3, msg+3); // TransmitControlWord + + __raw_writel(0xD7000000 | skb->len, msg+4); // MAC hdr included + __raw_writel((u32)skb, msg+5); // TransactionContext + __raw_writel(virt_to_bus(skb->data), msg+6); + __raw_writel((u32)skb->mac.raw, msg+7); + __raw_writel((u32)skb->mac.raw+4, msg+8); + + if ((priv->tx_batch_mode & 0x01) && !priv->send_active) { + priv->send_active = 1; + MOD_INC_USE_COUNT; + if (schedule_task(&priv->i2o_batch_send_task) == 0) + MOD_DEC_USE_COUNT; + } + } else { /* Add new SGL element to the previous message frame */ + + msg = (u32 *)(iop->mem_offset + priv->m); + sgl_elem = &msg[priv->tx_count * 5 + 1]; + + __raw_writel(I2O_MESSAGE_SIZE((__raw_readl(msg)>>16) + 5) | 1<<12 | SGL_OFFSET_4, msg); + __raw_writel(__raw_readl(sgl_elem-5) & 0x7FFFFFFF, sgl_elem-5); /* clear LE flag */ + __raw_writel(0xD5000000 | skb->len, sgl_elem); + __raw_writel((u32)skb, sgl_elem+1); + __raw_writel(virt_to_bus(skb->data), sgl_elem+2); + __raw_writel((u32)(skb->mac.raw), sgl_elem+3); + __raw_writel((u32)(skb->mac.raw)+1, sgl_elem+4); + } + + /* If tx not in batch mode or frame is full, send immediatelly */ + + if (!(priv->tx_batch_mode & 0x01) || priv->tx_count == priv->sgl_max) { + dev->trans_start = jiffies; + i2o_post_message(iop, priv->m); + dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); + priv->tx_count = 0; + } + + /* If DDMs TxMaxPktOut reached, stop queueing layer to send more */ + + if (atomic_read(&priv->tx_out) >= priv->tx_max_out) + netif_stop_queue(dev); + + spin_unlock_irq(&priv->tx_lock); + return 0; +} +#endif /* CONFIG_NET_FC */ + +/* + * i2o_lan_packet_send(): Send a packet as is, including the MAC header. + * + * Must be supported by Ethernet/802.3, Token Ring, FDDI, optional for + * Fibre Channel + */ +static int i2o_lan_packet_send(struct sk_buff *skb, struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + int tickssofar = jiffies - dev->trans_start; + u32 m, *msg; + u32 *sgl_elem; + + spin_lock_irq(&priv->tx_lock); + + priv->tx_count++; + atomic_inc(&priv->tx_out); + + /* + * If tx_batch_mode = 0x00 forced to immediate mode + * If tx_batch_mode = 0x01 forced to batch mode + * If tx_batch_mode = 0x10 switch automatically, current mode immediate + * If tx_batch_mode = 0x11 switch automatically, current mode batch + * If gap between two packets is > 0 ticks, switch to immediate + */ + if (priv->tx_batch_mode >> 1) // switch automatically + priv->tx_batch_mode = tickssofar ? 0x02 : 0x03; + + if (priv->tx_count == 1) { + m = I2O_POST_READ32(iop); + if (m == 0xFFFFFFFF) { + spin_unlock_irq(&priv->tx_lock); + return 1; + } + msg = (u32 *)(iop->mem_offset + m); + priv->m = m; + + __raw_writel(SEVEN_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4, msg); + __raw_writel(LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); + __raw_writel(priv->unit << 16 | lan_send_context, msg+2); // InitiatorContext + __raw_writel(1 << 30 | 1 << 3, msg+3); // TransmitControlWord + // bit 30: reply as soon as transmission attempt is complete + // bit 3: Suppress CRC generation + __raw_writel(0xD5000000 | skb->len, msg+4); // MAC hdr included + __raw_writel((u32)skb, msg+5); // TransactionContext + __raw_writel(virt_to_bus(skb->data), msg+6); + + if ((priv->tx_batch_mode & 0x01) && !priv->send_active) { + priv->send_active = 1; + MOD_INC_USE_COUNT; + if (schedule_task(&priv->i2o_batch_send_task) == 0) + MOD_DEC_USE_COUNT; + } + } else { /* Add new SGL element to the previous message frame */ + + msg = (u32 *)(iop->mem_offset + priv->m); + sgl_elem = &msg[priv->tx_count * 3 + 1]; + + __raw_writel(I2O_MESSAGE_SIZE((__raw_readl(msg)>>16) + 3) | 1<<12 | SGL_OFFSET_4, msg); + __raw_writel(__raw_readl(sgl_elem-3) & 0x7FFFFFFF, sgl_elem-3); /* clear LE flag */ + __raw_writel(0xD5000000 | skb->len, sgl_elem); + __raw_writel((u32)skb, sgl_elem+1); + __raw_writel(virt_to_bus(skb->data), sgl_elem+2); + } + + /* If tx is in immediate mode or frame is full, send now */ + + if (!(priv->tx_batch_mode & 0x01) || priv->tx_count == priv->sgl_max) { + dev->trans_start = jiffies; + i2o_post_message(iop, priv->m); + dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); + priv->tx_count = 0; + } + + /* If DDMs TxMaxPktOut reached, stop queueing layer to send more */ + + if (atomic_read(&priv->tx_out) >= priv->tx_max_out) + netif_stop_queue(dev); + + spin_unlock_irq(&priv->tx_lock); + return 0; +} + +/* + * i2o_lan_get_stats(): Fill in the statistics. + */ +static struct net_device_stats *i2o_lan_get_stats(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u64 val64[16]; + u64 supported_group[4] = { 0, 0, 0, 0 }; + + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0100, -1, val64, + sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_HISTORICAL_STATS.\n", dev->name); + else { + dprintk(KERN_DEBUG "%s: LAN_HISTORICAL_STATS queried.\n", dev->name); + priv->stats.tx_packets = val64[0]; + priv->stats.tx_bytes = val64[1]; + priv->stats.rx_packets = val64[2]; + priv->stats.rx_bytes = val64[3]; + priv->stats.tx_errors = val64[4]; + priv->stats.rx_errors = val64[5]; + priv->stats.rx_dropped = val64[6]; + } + + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0180, -1, + &supported_group, sizeof(supported_group)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_SUPPORTED_OPTIONAL_HISTORICAL_STATS.\n", dev->name); + + if (supported_group[2]) { + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0183, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_OPTIONAL_RX_HISTORICAL_STATS.\n", dev->name); + else { + dprintk(KERN_DEBUG "%s: LAN_OPTIONAL_RX_HISTORICAL_STATS queried.\n", dev->name); + priv->stats.multicast = val64[4]; + priv->stats.rx_length_errors = val64[10]; + priv->stats.rx_crc_errors = val64[0]; + } + } + + if (i2o_dev->lct_data.sub_class == I2O_LAN_ETHERNET) { + u64 supported_stats = 0; + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0200, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_802_3_HISTORICAL_STATS.\n", dev->name); + else { + dprintk(KERN_DEBUG "%s: LAN_802_3_HISTORICAL_STATS queried.\n", dev->name); + priv->stats.transmit_collision = val64[1] + val64[2]; + priv->stats.rx_frame_errors = val64[0]; + priv->stats.tx_carrier_errors = val64[6]; + } + + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0280, -1, + &supported_stats, sizeof(supported_stats)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_SUPPORTED_802_3_HISTORICAL_STATS.\n", dev->name); + + if (supported_stats != 0) { + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0281, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_OPTIONAL_802_3_HISTORICAL_STATS.\n", dev->name); + else { + dprintk(KERN_DEBUG "%s: LAN_OPTIONAL_802_3_HISTORICAL_STATS queried.\n", dev->name); + if (supported_stats & 0x1) + priv->stats.rx_over_errors = val64[0]; + if (supported_stats & 0x4) + priv->stats.tx_heartbeat_errors = val64[2]; + } + } + } + +#ifdef CONFIG_TR + if (i2o_dev->lct_data.sub_class == I2O_LAN_TR) { + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0300, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_802_5_HISTORICAL_STATS.\n", dev->name); + else { + struct tr_statistics *stats = + (struct tr_statistics *)&priv->stats; + dprintk(KERN_DEBUG "%s: LAN_802_5_HISTORICAL_STATS queried.\n", dev->name); + + stats->line_errors = val64[0]; + stats->internal_errors = val64[7]; + stats->burst_errors = val64[4]; + stats->A_C_errors = val64[2]; + stats->abort_delimiters = val64[3]; + stats->lost_frames = val64[1]; + /* stats->recv_congest_count = ?; FIXME ??*/ + stats->frame_copied_errors = val64[5]; + stats->frequency_errors = val64[6]; + stats->token_errors = val64[9]; + } + /* Token Ring optional stats not yet defined */ + } +#endif + +#ifdef CONFIG_FDDI + if (i2o_dev->lct_data.sub_class == I2O_LAN_FDDI) { + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0400, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_FDDI_HISTORICAL_STATS.\n", dev->name); + else { + dprintk(KERN_DEBUG "%s: LAN_FDDI_HISTORICAL_STATS queried.\n", dev->name); + priv->stats.smt_cf_state = val64[0]; + memcpy(priv->stats.mac_upstream_nbr, &val64[1], FDDI_K_ALEN); + memcpy(priv->stats.mac_downstream_nbr, &val64[2], FDDI_K_ALEN); + priv->stats.mac_error_cts = val64[3]; + priv->stats.mac_lost_cts = val64[4]; + priv->stats.mac_rmt_state = val64[5]; + memcpy(priv->stats.port_lct_fail_cts, &val64[6], 8); + memcpy(priv->stats.port_lem_reject_cts, &val64[7], 8); + memcpy(priv->stats.port_lem_cts, &val64[8], 8); + memcpy(priv->stats.port_pcm_state, &val64[9], 8); + } + /* FDDI optional stats not yet defined */ + } +#endif + +#ifdef CONFIG_NET_FC + /* Fibre Channel Statistics not yet defined in 1.53 nor 2.0 */ +#endif + + return (struct net_device_stats *)&priv->stats; +} + +/* + * i2o_lan_set_mc_filter(): Post a request to set multicast filter. + */ +int i2o_lan_set_mc_filter(struct net_device *dev, u32 filter_mask) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 msg[10]; + + msg[0] = TEN_WORD_MSG_SIZE | SGL_OFFSET_5; + msg[1] = I2O_CMD_UTIL_PARAMS_SET << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid; + msg[2] = priv->unit << 16 | lan_context; + msg[3] = 0x0001 << 16 | 3 ; // TransactionContext: group&field + msg[4] = 0; + msg[5] = 0xCC000000 | 16; // Immediate data SGL + msg[6] = 1; // OperationCount + msg[7] = 0x0001<<16 | I2O_PARAMS_FIELD_SET; // Group, Operation + msg[8] = 3 << 16 | 1; // FieldIndex, FieldCount + msg[9] = filter_mask; // Value + + return i2o_post_this(iop, msg, sizeof(msg)); +} + +/* + * i2o_lan_set_mc_table(): Post a request to set LAN_MULTICAST_MAC_ADDRESS table. + */ +int i2o_lan_set_mc_table(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + struct dev_mc_list *mc; + u32 msg[10 + 2 * dev->mc_count]; + u8 *work8 = (u8 *)(msg + 10); + + msg[0] = I2O_MESSAGE_SIZE(10 + 2 * dev->mc_count) | SGL_OFFSET_5; + msg[1] = I2O_CMD_UTIL_PARAMS_SET << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid; + msg[2] = priv->unit << 16 | lan_context; // InitiatorContext + msg[3] = 0x0002 << 16 | (u16)-1; // TransactionContext + msg[4] = 0; // OperationFlags + msg[5] = 0xCC000000 | (16 + 8 * dev->mc_count); // Immediate data SGL + msg[6] = 2; // OperationCount + msg[7] = 0x0002 << 16 | I2O_PARAMS_TABLE_CLEAR; // Group, Operation + msg[8] = 0x0002 << 16 | I2O_PARAMS_ROW_ADD; // Group, Operation + msg[9] = dev->mc_count << 16 | (u16)-1; // RowCount, FieldCount + + for (mc = dev->mc_list; mc ; mc = mc->next, work8 += 8) { + memset(work8, 0, 8); + memcpy(work8, mc->dmi_addr, mc->dmi_addrlen); // Values + } + + return i2o_post_this(iop, msg, sizeof(msg)); +} + +/* + * i2o_lan_set_multicast_list(): Enable a network device to receive packets + * not send to the protocol address. + */ +static void i2o_lan_set_multicast_list(struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + u32 filter_mask; + + if (dev->flags & IFF_PROMISC) { + filter_mask = 0x00000002; + dprintk(KERN_INFO "%s: Enabling promiscuous mode...\n", dev->name); + } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > priv->max_size_mc_table) { + filter_mask = 0x00000004; + dprintk(KERN_INFO "%s: Enabling all multicast mode...\n", dev->name); + } else if (dev->mc_count) { + filter_mask = 0x00000000; + dprintk(KERN_INFO "%s: Enabling multicast mode...\n", dev->name); + if (i2o_lan_set_mc_table(dev) < 0) + printk(KERN_WARNING "%s: Unable to send MAC table.\n", dev->name); + } else { + filter_mask = 0x00000300; // Broadcast, Multicast disabled + dprintk(KERN_INFO "%s: Enabling unicast mode...\n", dev->name); + } + + /* Finally copy new FilterMask to DDM */ + + if (i2o_lan_set_mc_filter(dev, filter_mask) < 0) + printk(KERN_WARNING "%s: Unable to send MAC FilterMask.\n", dev->name); +} + +/* + * i2o_lan_change_mtu(): Change maximum transfer unit size. + */ +static int i2o_lan_change_mtu(struct net_device *dev, int new_mtu) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + u32 max_pkt_size; + + if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, + 0x0000, 6, &max_pkt_size, 4) < 0) + return -EFAULT; + + if (new_mtu < 68 || new_mtu > 9000 || new_mtu > max_pkt_size) + return -EINVAL; + + dev->mtu = new_mtu; + + i2o_lan_suspend(dev); // to SUSPENDED state, return buckets + + while (priv->i2o_fbl_tail >= 0) // free buffered buckets + dev_kfree_skb(priv->i2o_fbl[priv->i2o_fbl_tail--]); + + i2o_lan_reset(dev); // to OPERATIONAL state + i2o_set_ddm_parameters(dev); // reset some parameters + i2o_lan_receive_post(dev); // post new buckets (new size) + + return 0; +} + +/* Functions to initialize I2O LAN OSM: +======================================*/ + +/* + * i2o_lan_register_device(): Register LAN class device to kernel. + */ +struct net_device *i2o_lan_register_device(struct i2o_device *i2o_dev) +{ + struct net_device *dev = NULL; + struct i2o_lan_local *priv = NULL; + u8 hw_addr[8]; + u32 tx_max_out = 0; + unsigned short (*type_trans)(struct sk_buff *, struct net_device *); + void (*unregister_dev)(struct net_device *dev); + + switch (i2o_dev->lct_data.sub_class) { + case I2O_LAN_ETHERNET: + dev = init_etherdev(NULL, sizeof(struct i2o_lan_local)); + if (dev == NULL) + return NULL; + type_trans = eth_type_trans; + unregister_dev = unregister_netdev; + break; + +#ifdef CONFIG_ANYLAN + case I2O_LAN_100VG: + printk(KERN_ERR "i2o_lan: 100base VG not yet supported.\n"); + return NULL; + break; +#endif + +#ifdef CONFIG_TR + case I2O_LAN_TR: + dev = init_trdev(NULL, sizeof(struct i2o_lan_local)); + if (dev==NULL) + return NULL; + type_trans = tr_type_trans; + unregister_dev = unregister_trdev; + break; +#endif + +#ifdef CONFIG_FDDI + case I2O_LAN_FDDI: + { + int size = sizeof(struct net_device) + sizeof(struct i2o_lan_local); + + dev = (struct net_device *) kmalloc(size, GFP_KERNEL); + if (dev == NULL) + return NULL; + memset((char *)dev, 0, size); + dev->priv = (void *)(dev + 1); + + if (dev_alloc_name(dev, "fddi%d") < 0) { + printk(KERN_WARNING "i2o_lan: Too many FDDI devices.\n"); + kfree(dev); + return NULL; + } + type_trans = fddi_type_trans; + unregister_dev = (void *)unregister_netdevice; + + fddi_setup(dev); + register_netdev(dev); + } + break; +#endif + +#ifdef CONFIG_NET_FC + case I2O_LAN_FIBRE_CHANNEL: + dev = init_fcdev(NULL, sizeof(struct i2o_lan_local)); + if (dev == NULL) + return NULL; + type_trans = NULL; +/* FIXME: Move fc_type_trans() from drivers/net/fc/iph5526.c to net/802/fc.c + * and export it in include/linux/fcdevice.h + * type_trans = fc_type_trans; + */ + unregister_dev = (void *)unregister_fcdev; + break; +#endif + + case I2O_LAN_UNKNOWN: + default: + printk(KERN_ERR "i2o_lan: LAN type 0x%04x not supported.\n", + i2o_dev->lct_data.sub_class); + return NULL; + } + + priv = (struct i2o_lan_local *)dev->priv; + priv->i2o_dev = i2o_dev; + priv->type_trans = type_trans; + priv->sgl_max = (i2o_dev->controller->status_block->inbound_frame_size - 4) / 3; + atomic_set(&priv->buckets_out, 0); + + /* Set default values for user configurable parameters */ + /* Private values are changed via /proc file system */ + + priv->max_buckets_out = max_buckets_out; + priv->bucket_thresh = bucket_thresh; + priv->rx_copybreak = rx_copybreak; + priv->tx_batch_mode = tx_batch_mode & 0x03; + priv->i2o_event_mask = i2o_event_mask; + + priv->tx_lock = SPIN_LOCK_UNLOCKED; + priv->fbl_lock = SPIN_LOCK_UNLOCKED; + + unit++; + i2o_landevs[unit] = dev; + priv->unit = unit; + + if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, + 0x0001, 0, &hw_addr, sizeof(hw_addr)) < 0) { + printk(KERN_ERR "%s: Unable to query hardware address.\n", dev->name); + unit--; + unregister_dev(dev); + kfree(dev); + return NULL; + } + dprintk(KERN_DEBUG "%s: hwaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + dev->name, hw_addr[0], hw_addr[1], hw_addr[2], hw_addr[3], + hw_addr[4], hw_addr[5]); + + dev->addr_len = 6; + memcpy(dev->dev_addr, hw_addr, 6); + + if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, + 0x0007, 2, &tx_max_out, sizeof(tx_max_out)) < 0) { + printk(KERN_ERR "%s: Unable to query max TX queue.\n", dev->name); + unit--; + unregister_dev(dev); + kfree(dev); + return NULL; + } + dprintk(KERN_INFO "%s: Max TX Outstanding = %d.\n", dev->name, tx_max_out); + priv->tx_max_out = tx_max_out; + atomic_set(&priv->tx_out, 0); + priv->tx_count = 0; + + INIT_LIST_HEAD(&priv->i2o_batch_send_task.list); + priv->i2o_batch_send_task.sync = 0; + priv->i2o_batch_send_task.routine = (void *)i2o_lan_batch_send; + priv->i2o_batch_send_task.data = (void *)dev; + + dev->open = i2o_lan_open; + dev->stop = i2o_lan_close; + dev->get_stats = i2o_lan_get_stats; + dev->set_multicast_list = i2o_lan_set_multicast_list; + dev->tx_timeout = i2o_lan_tx_timeout; + dev->watchdog_timeo = I2O_LAN_TX_TIMEOUT; + +#ifdef CONFIG_NET_FC + if (i2o_dev->lct_data.sub_class == I2O_LAN_FIBRE_CHANNEL) + dev->hard_start_xmit = i2o_lan_sdu_send; + else +#endif + dev->hard_start_xmit = i2o_lan_packet_send; + + if (i2o_dev->lct_data.sub_class == I2O_LAN_ETHERNET) + dev->change_mtu = i2o_lan_change_mtu; + + return dev; +} + +#ifdef MODULE +#define i2o_lan_init init_module +#endif + +int __init i2o_lan_init(void) +{ + struct net_device *dev; + int i; + + printk(KERN_INFO "I2O LAN OSM (C) 1999 University of Helsinki.\n"); + + /* Module params are used as global defaults for private values */ + + if (max_buckets_out > I2O_LAN_MAX_BUCKETS_OUT) + max_buckets_out = I2O_LAN_MAX_BUCKETS_OUT; + if (bucket_thresh > max_buckets_out) + bucket_thresh = max_buckets_out; + + /* Install handlers for incoming replies */ + + if (i2o_install_handler(&i2o_lan_send_handler) < 0) { + printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); + return -EINVAL; + } + lan_send_context = i2o_lan_send_handler.context; + + if (i2o_install_handler(&i2o_lan_receive_handler) < 0) { + printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); + return -EINVAL; + } + lan_receive_context = i2o_lan_receive_handler.context; + + if (i2o_install_handler(&i2o_lan_handler) < 0) { + printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); + return -EINVAL; + } + lan_context = i2o_lan_handler.context; + + for(i=0; i <= MAX_LAN_CARDS; i++) + i2o_landevs[i] = NULL; + + for (i=0; i < MAX_I2O_CONTROLLERS; i++) { + struct i2o_controller *iop = i2o_find_controller(i); + struct i2o_device *i2o_dev; + + if (iop==NULL) + continue; + + for (i2o_dev=iop->devices;i2o_dev != NULL;i2o_dev=i2o_dev->next) { + + if (i2o_dev->lct_data.class_id != I2O_CLASS_LAN) + continue; + + /* Make sure device not already claimed by an ISM */ + if (i2o_dev->lct_data.user_tid != 0xFFF) + continue; + + if (unit == MAX_LAN_CARDS) { + i2o_unlock_controller(iop); + printk(KERN_WARNING "i2o_lan: Too many I2O LAN devices.\n"); + return -EINVAL; + } + + dev = i2o_lan_register_device(i2o_dev); + if (dev == NULL) { + printk(KERN_ERR "i2o_lan: Unable to register I2O LAN device 0x%04x.\n", + i2o_dev->lct_data.sub_class); + continue; + } + + printk(KERN_INFO "%s: I2O LAN device registered, " + "subclass = 0x%04x, unit = %d, tid = %d.\n", + dev->name, i2o_dev->lct_data.sub_class, + ((struct i2o_lan_local *)dev->priv)->unit, + i2o_dev->lct_data.tid); + } + + i2o_unlock_controller(iop); + } + + dprintk(KERN_INFO "%d I2O LAN devices found and registered.\n", unit+1); + + return 0; +} + +#ifdef MODULE + +void cleanup_module(void) +{ + int i; + + for (i = 0; i <= unit; i++) { + struct net_device *dev = i2o_landevs[i]; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + + switch (i2o_dev->lct_data.sub_class) { + case I2O_LAN_ETHERNET: + unregister_netdev(dev); + break; +#ifdef CONFIG_FDDI + case I2O_LAN_FDDI: + unregister_netdevice(dev); + break; +#endif +#ifdef CONFIG_TR + case I2O_LAN_TR: + unregister_trdev(dev); + break; +#endif +#ifdef CONFIG_NET_FC + case I2O_LAN_FIBRE_CHANNEL: + unregister_fcdev(dev); + break; +#endif + default: + printk(KERN_WARNING "%s: Spurious I2O LAN subclass 0x%08x.\n", + dev->name, i2o_dev->lct_data.sub_class); + } + + dprintk(KERN_INFO "%s: I2O LAN device unregistered.\n", + dev->name); + kfree(dev); + } + + i2o_remove_handler(&i2o_lan_handler); + i2o_remove_handler(&i2o_lan_send_handler); + i2o_remove_handler(&i2o_lan_receive_handler); +} + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("University of Helsinki, Department of Computer Science"); +MODULE_DESCRIPTION("I2O Lan OSM"); + +MODULE_PARM(max_buckets_out, "1-" __MODULE_STRING(I2O_LAN_MAX_BUCKETS_OUT) "i"); +MODULE_PARM_DESC(max_buckets_out, "Total number of buckets to post (1-)"); +MODULE_PARM(bucket_thresh, "1-" __MODULE_STRING(I2O_LAN_MAX_BUCKETS_OUT) "i"); +MODULE_PARM_DESC(bucket_thresh, "Bucket post threshold (1-)"); +MODULE_PARM(rx_copybreak, "1-" "i"); +MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy only small frames (1-)"); +MODULE_PARM(tx_batch_mode, "0-2" "i"); +MODULE_PARM_DESC(tx_batch_mode, "0=Send immediatelly, 1=Send in batches, 2=Switch automatically"); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_lan.h linux.ac/drivers/message/i2o/i2o_lan.h --- linux.vanilla/drivers/message/i2o/i2o_lan.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_lan.h Sat May 26 17:58:41 2001 @@ -0,0 +1,159 @@ +/* + * i2o_lan.h I2O LAN Class definitions + * + * I2O LAN CLASS OSM May 26th 2000 + * + * (C) Copyright 1999, 2000 University of Helsinki, + * Department of Computer Science + * + * This code is still under development / test. + * + * Author: Auvo Häkkinen + * Juha Sievänen + * Taneli Vähäkangas + */ + +#ifndef _I2O_LAN_H +#define _I2O_LAN_H + +/* Default values for tunable parameters first */ + +#define I2O_LAN_MAX_BUCKETS_OUT 96 +#define I2O_LAN_BUCKET_THRESH 18 /* 9 buckets in one message */ +#define I2O_LAN_RX_COPYBREAK 200 +#define I2O_LAN_TX_TIMEOUT (1*HZ) +#define I2O_LAN_TX_BATCH_MODE 2 /* 2=automatic, 1=on, 0=off */ +#define I2O_LAN_EVENT_MASK 0 /* 0=None, 0xFFC00002=All */ + +/* LAN types */ +#define I2O_LAN_ETHERNET 0x0030 +#define I2O_LAN_100VG 0x0040 +#define I2O_LAN_TR 0x0050 +#define I2O_LAN_FDDI 0x0060 +#define I2O_LAN_FIBRE_CHANNEL 0x0070 +#define I2O_LAN_UNKNOWN 0x00000000 + +/* Connector types */ + +/* Ethernet */ +#define I2O_LAN_AUI (I2O_LAN_ETHERNET << 4) + 0x00000001 +#define I2O_LAN_10BASE5 (I2O_LAN_ETHERNET << 4) + 0x00000002 +#define I2O_LAN_FIORL (I2O_LAN_ETHERNET << 4) + 0x00000003 +#define I2O_LAN_10BASE2 (I2O_LAN_ETHERNET << 4) + 0x00000004 +#define I2O_LAN_10BROAD36 (I2O_LAN_ETHERNET << 4) + 0x00000005 +#define I2O_LAN_10BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000006 +#define I2O_LAN_10BASE_FP (I2O_LAN_ETHERNET << 4) + 0x00000007 +#define I2O_LAN_10BASE_FB (I2O_LAN_ETHERNET << 4) + 0x00000008 +#define I2O_LAN_10BASE_FL (I2O_LAN_ETHERNET << 4) + 0x00000009 +#define I2O_LAN_100BASE_TX (I2O_LAN_ETHERNET << 4) + 0x0000000A +#define I2O_LAN_100BASE_FX (I2O_LAN_ETHERNET << 4) + 0x0000000B +#define I2O_LAN_100BASE_T4 (I2O_LAN_ETHERNET << 4) + 0x0000000C +#define I2O_LAN_1000BASE_SX (I2O_LAN_ETHERNET << 4) + 0x0000000D +#define I2O_LAN_1000BASE_LX (I2O_LAN_ETHERNET << 4) + 0x0000000E +#define I2O_LAN_1000BASE_CX (I2O_LAN_ETHERNET << 4) + 0x0000000F +#define I2O_LAN_1000BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000010 + +/* AnyLAN */ +#define I2O_LAN_100VG_ETHERNET (I2O_LAN_100VG << 4) + 0x00000001 +#define I2O_LAN_100VG_TR (I2O_LAN_100VG << 4) + 0x00000002 + +/* Token Ring */ +#define I2O_LAN_4MBIT (I2O_LAN_TR << 4) + 0x00000001 +#define I2O_LAN_16MBIT (I2O_LAN_TR << 4) + 0x00000002 + +/* FDDI */ +#define I2O_LAN_125MBAUD (I2O_LAN_FDDI << 4) + 0x00000001 + +/* Fibre Channel */ +#define I2O_LAN_POINT_POINT (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000001 +#define I2O_LAN_ARB_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000002 +#define I2O_LAN_PUBLIC_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000003 +#define I2O_LAN_FABRIC (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000004 + +#define I2O_LAN_EMULATION 0x00000F00 +#define I2O_LAN_OTHER 0x00000F01 +#define I2O_LAN_DEFAULT 0xFFFFFFFF + +/* LAN class functions */ + +#define LAN_PACKET_SEND 0x3B +#define LAN_SDU_SEND 0x3D +#define LAN_RECEIVE_POST 0x3E +#define LAN_RESET 0x35 +#define LAN_SUSPEND 0x37 + +/* LAN DetailedStatusCode defines */ +#define I2O_LAN_DSC_SUCCESS 0x00 +#define I2O_LAN_DSC_DEVICE_FAILURE 0x01 +#define I2O_LAN_DSC_DESTINATION_NOT_FOUND 0x02 +#define I2O_LAN_DSC_TRANSMIT_ERROR 0x03 +#define I2O_LAN_DSC_TRANSMIT_ABORTED 0x04 +#define I2O_LAN_DSC_RECEIVE_ERROR 0x05 +#define I2O_LAN_DSC_RECEIVE_ABORTED 0x06 +#define I2O_LAN_DSC_DMA_ERROR 0x07 +#define I2O_LAN_DSC_BAD_PACKET_DETECTED 0x08 +#define I2O_LAN_DSC_OUT_OF_MEMORY 0x09 +#define I2O_LAN_DSC_BUCKET_OVERRUN 0x0A +#define I2O_LAN_DSC_IOP_INTERNAL_ERROR 0x0B +#define I2O_LAN_DSC_CANCELED 0x0C +#define I2O_LAN_DSC_INVALID_TRANSACTION_CONTEXT 0x0D +#define I2O_LAN_DSC_DEST_ADDRESS_DETECTED 0x0E +#define I2O_LAN_DSC_DEST_ADDRESS_OMITTED 0x0F +#define I2O_LAN_DSC_PARTIAL_PACKET_RETURNED 0x10 +#define I2O_LAN_DSC_SUSPENDED 0x11 + +struct i2o_packet_info { + u32 offset : 24; + u32 flags : 8; + u32 len : 24; + u32 status : 8; +}; + +struct i2o_bucket_descriptor { + u32 context; /* FIXME: 64bit support */ + struct i2o_packet_info packet_info[1]; +}; + +/* Event Indicator Mask Flags for LAN OSM */ + +#define I2O_LAN_EVT_LINK_DOWN 0x01 +#define I2O_LAN_EVT_LINK_UP 0x02 +#define I2O_LAN_EVT_MEDIA_CHANGE 0x04 + +#include +#include + +struct i2o_lan_local { + u8 unit; + struct i2o_device *i2o_dev; + + struct fddi_statistics stats; /* see also struct net_device_stats */ + unsigned short (*type_trans)(struct sk_buff *, struct net_device *); + atomic_t buckets_out; /* nbr of unused buckets on DDM */ + atomic_t tx_out; /* outstanding TXes */ + u8 tx_count; /* packets in one TX message frame */ + u16 tx_max_out; /* DDM's Tx queue len */ + u8 sgl_max; /* max SGLs in one message frame */ + u32 m; /* IOP address of the batch msg frame */ + + struct tq_struct i2o_batch_send_task; + int send_active; + struct sk_buff **i2o_fbl; /* Free bucket list (to reuse skbs) */ + int i2o_fbl_tail; + spinlock_t fbl_lock; + + spinlock_t tx_lock; + + u32 max_size_mc_table; /* max number of multicast addresses */ + + /* LAN OSM configurable parameters are here: */ + + u16 max_buckets_out; /* max nbr of buckets to send to DDM */ + u16 bucket_thresh; /* send more when this many used */ + u16 rx_copybreak; + + u8 tx_batch_mode; /* Set when using batch mode sends */ + u32 i2o_event_mask; /* To turn on interesting event flags */ +}; + +#endif /* _I2O_LAN_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_pci.c linux.ac/drivers/message/i2o/i2o_pci.c --- linux.vanilla/drivers/message/i2o/i2o_pci.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_pci.c Thu May 24 23:10:26 2001 @@ -0,0 +1,376 @@ +/* + * Find I2O capable controllers on the PCI bus, and register/install + * them with the I2O layer + * + * (C) Copyright 1999 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * Modified by Deepak Saxena + * Modified by Boji T Kannanthanam + * + * 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. + * + * TODO: + * Support polled I2O PCI controllers. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTRR +#include +#endif // CONFIG_MTRR + +#ifdef MODULE +/* + * Core function table + * See for an explanation + */ +static struct i2o_core_func_table *core; + +/* Core attach function */ +extern int i2o_pci_core_attach(struct i2o_core_func_table *); +extern void i2o_pci_core_detach(void); +#endif /* MODULE */ + +/* + * Free bus specific resources + */ +static void i2o_pci_dispose(struct i2o_controller *c) +{ + I2O_IRQ_WRITE32(c,0xFFFFFFFF); + if(c->bus.pci.irq > 0) + free_irq(c->bus.pci.irq, c); + iounmap(((u8 *)c->post_port)-0x40); + +#ifdef CONFIG_MTRR + if(c->bus.pci.mtrr_reg0 > 0) + mtrr_del(c->bus.pci.mtrr_reg0, 0, 0); + if(c->bus.pci.mtrr_reg1 > 0) + mtrr_del(c->bus.pci.mtrr_reg1, 0, 0); +#endif +} + +/* + * No real bus specific handling yet (note that later we will + * need to 'steal' PCI devices on i960 mainboards) + */ + +static int i2o_pci_bind(struct i2o_controller *c, struct i2o_device *dev) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int i2o_pci_unbind(struct i2o_controller *c, struct i2o_device *dev) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Bus specific enable/disable functions + */ +static void i2o_pci_enable(struct i2o_controller *c) +{ + I2O_IRQ_WRITE32(c, 0); + c->enabled = 1; +} + +static void i2o_pci_disable(struct i2o_controller *c) +{ + I2O_IRQ_WRITE32(c, 0xFFFFFFFF); + c->enabled = 0; +} + +/* + * Bus specific interrupt handler + */ + +static void i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r) +{ + struct i2o_controller *c = dev_id; +#ifdef MODULE + core->run_queue(c); +#else + i2o_run_queue(c); +#endif /* MODULE */ +} + +/* + * Install a PCI (or in theory AGP) i2o controller + * + * TODO: Add support for polled controllers + */ +int __init i2o_pci_install(struct pci_dev *dev) +{ + struct i2o_controller *c=kmalloc(sizeof(struct i2o_controller), + GFP_KERNEL); + u8 *mem; + u32 memptr = 0; + u32 size; + + int i; + + if(c==NULL) + { + printk(KERN_ERR "i2o: Insufficient memory to add controller.\n"); + return -ENOMEM; + } + memset(c, 0, sizeof(*c)); + + for(i=0; i<6; i++) + { + /* Skip I/O spaces */ + if(!(pci_resource_flags(dev, i) & IORESOURCE_IO)) + { + memptr = pci_resource_start(dev, i); + break; + } + } + + if(i==6) + { + printk(KERN_ERR "i2o: I2O controller has no memory regions defined.\n"); + kfree(c); + return -EINVAL; + } + + size = dev->resource[i].end-dev->resource[i].start+1; + /* Map the I2O controller */ + + printk(KERN_INFO "i2o: PCI I2O controller at 0x%08X size=%d\n", memptr, size); + mem = ioremap(memptr, size); + if(mem==NULL) + { + printk(KERN_ERR "i2o: Unable to map controller.\n"); + kfree(c); + return -EINVAL; + } + + c->bus.pci.irq = -1; + c->bus.pci.queue_buggy = 0; + c->bus.pci.dpt = 0; + c->bus.pci.short_req = 0; + + c->irq_mask = (volatile u32 *)(mem+0x34); + c->post_port = (volatile u32 *)(mem+0x40); + c->reply_port = (volatile u32 *)(mem+0x44); + + c->mem_phys = memptr; + c->mem_offset = (u32)mem; + c->destructor = i2o_pci_dispose; + + c->bind = i2o_pci_bind; + c->unbind = i2o_pci_unbind; + c->bus_enable = i2o_pci_enable; + c->bus_disable = i2o_pci_disable; + + c->type = I2O_TYPE_PCI; + + /* + * Cards that fall apart if you hit them with large I/O + * loads... + */ + + if(dev->vendor == PCI_VENDOR_ID_NCR && dev->device == 0x0630) + { + c->bus.pci.short_req=1; + printk(KERN_INFO "I2O: Symbios FC920 workarounds activated.\n"); + } + if(dev->subsystem_vendor == PCI_VENDOR_ID_PROMISE) + { + c->bus.pci.queue_buggy=1; + printk(KERN_INFO "I2O: Promise workarounds activated.\n"); + } + + /* + * Cards that go bananas if you quiesce them before you reset + * them + */ + + if(dev->vendor == PCI_VENDOR_ID_DPT) + c->bus.pci.dpt=1; + + /* + * Enable Write Combining MTRR for IOP's memory region + */ +#ifdef CONFIG_MTRR + c->bus.pci.mtrr_reg0 = + mtrr_add(c->mem_phys, size, MTRR_TYPE_WRCOMB, 1); +/* +* If it is an INTEL i960 I/O processor then set the first 64K to Uncacheable +* since the region contains the Messaging unit which shouldn't be cached. +*/ + c->bus.pci.mtrr_reg1 = -1; + if(dev->vendor == PCI_VENDOR_ID_INTEL || dev->vendor == PCI_VENDOR_ID_DPT) + { + printk(KERN_INFO "I2O: MTRR workaround for Intel i960 processor\n"); + c->bus.pci.mtrr_reg1 = mtrr_add(c->mem_phys, 65536, MTRR_TYPE_UNCACHABLE, 1); + if(c->bus.pci.mtrr_reg1< 0) + printk(KERN_INFO "i2o_pci: Error in setting MTRR_TYPE_UNCACHABLE\n"); + } + +#endif + + I2O_IRQ_WRITE32(c,0xFFFFFFFF); + +#ifdef MODULE + i = core->install(c); +#else + i = i2o_install_controller(c); +#endif /* MODULE */ + + if(i<0) + { + printk(KERN_ERR "i2o: Unable to install controller.\n"); + kfree(c); + iounmap(mem); + return i; + } + + c->bus.pci.irq = dev->irq; + if(c->bus.pci.irq) + { + i=request_irq(dev->irq, i2o_pci_interrupt, SA_SHIRQ, + c->name, c); + if(i<0) + { + printk(KERN_ERR "%s: unable to allocate interrupt %d.\n", + c->name, dev->irq); + c->bus.pci.irq = -1; +#ifdef MODULE + core->delete(c); +#else + i2o_delete_controller(c); +#endif /* MODULE */ + iounmap(mem); + return -EBUSY; + } + } + + printk(KERN_INFO "%s: Installed at IRQ%d\n", c->name, dev->irq); + I2O_IRQ_WRITE32(c,0x0); + c->enabled = 1; + return 0; +} + +int __init i2o_pci_scan(void) +{ + struct pci_dev *dev; + int count=0; + + printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n"); + + pci_for_each_dev(dev) + { + if((dev->class>>8)!=PCI_CLASS_INTELLIGENT_I2O) + continue; + if((dev->class&0xFF)>1) + { + printk(KERN_INFO "i2o: I2O Controller found but does not support I2O 1.5 (skipping).\n"); + continue; + } + if (pci_enable_device(dev)) + continue; + printk(KERN_INFO "i2o: I2O controller on bus %d at %d.\n", + dev->bus->number, dev->devfn); + pci_set_master(dev); + if(i2o_pci_install(dev)==0) + count++; + } + if(count) + printk(KERN_INFO "i2o: %d I2O controller%s found and installed.\n", count, + count==1?"":"s"); + return count?count:-ENODEV; +} + +#ifdef I2O_HOTPLUG_SUPPORT +/* + * Activate a newly found PCI I2O controller + * Not used now, but will be needed in future for + * hot plug PCI support + */ +static void i2o_pci_activate(i2o_controller * c) +{ + int i=0; + struct i2o_controller *c; + + if(c->type == I2O_TYPE_PCI) + { + I2O_IRQ_WRITE32(c,0); +#ifdef MODULE + if(core->activate(c)) +#else + if(i2o_activate_controller(c)) +#endif /* MODULE */ + { + printk("%s: Failed to initialize.\n", c->name); +#ifdef MODULE + core->unlock(c); + core->delete(c); +#else + i2o_unlock_controller(c); + i2o_delete_controller(c); +#endif + continue; + } + } +} +#endif // I2O_HOTPLUG_SUPPORT + +#ifdef MODULE + +int i2o_pci_core_attach(struct i2o_core_func_table *table) +{ + MOD_INC_USE_COUNT; + + core = table; + + return i2o_pci_scan(); +} + +void i2o_pci_core_detach(void) +{ + core = NULL; + + MOD_DEC_USE_COUNT; +} + +int init_module(void) +{ + printk(KERN_INFO "Linux I2O PCI support (c) 1999 Red Hat Software.\n"); + + core = NULL; + + return 0; + +} + +void cleanup_module(void) +{ +} + +EXPORT_SYMBOL(i2o_pci_core_attach); +EXPORT_SYMBOL(i2o_pci_core_detach); + +MODULE_AUTHOR("Red Hat Software"); +MODULE_DESCRIPTION("I2O PCI Interface"); + +#else +void __init i2o_pci_init(void) +{ + printk(KERN_INFO "Linux I2O PCI support (c) 1999 Red Hat Software.\n"); + i2o_pci_scan(); +} +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_proc.c linux.ac/drivers/message/i2o/i2o_proc.c --- linux.vanilla/drivers/message/i2o/i2o_proc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_proc.c Tue Apr 3 17:54:48 2001 @@ -0,0 +1,3372 @@ +/* + * procfs handler for Linux I2O subsystem + * + * (c) Copyright 1999 Deepak Saxena + * + * Originally written by Deepak Saxena(deepak@plexity.net) + * + * 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 is an initial test release. The code is based on the design + * of the ide procfs system (drivers/block/ide-proc.c). Some code + * taken from i2o-core module by Alan Cox. + * + * DISCLAIMER: This code is still under development/test and may cause + * your system to behave unpredictably. Use at your own discretion. + * + * LAN entries by Juha Sievänen (Juha.Sievanen@cs.Helsinki.FI), + * Auvo Häkkinen (Auvo.Hakkinen@cs.Helsinki.FI) + * University of Helsinki, Department of Computer Science + */ + +/* + * set tabstop=3 + */ + +/* + * TODO List + * + * - Add support for any version 2.0 spec changes once 2.0 IRTOS is + * is available to test with + * - Clean up code to use official structure definitions + */ + +// FIXME! +#define FMT_U64_HEX "0x%08x%08x" +#define U64_VAL(pu64) *((u32*)(pu64)+1), *((u32*)(pu64)) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "i2o_lan.h" + +/* + * Structure used to define /proc entries + */ +typedef struct _i2o_proc_entry_t +{ + char *name; /* entry name */ + mode_t mode; /* mode */ + read_proc_t *read_proc; /* read func */ + write_proc_t *write_proc; /* write func */ +} i2o_proc_entry; + +// #define DRIVERDEBUG + +static int i2o_proc_read_lct(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_hrt(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_status(char *, char **, off_t, int, int *, void *); + +static int i2o_proc_read_hw(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_ddm_table(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_driver_store(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_drivers_stored(char *, char **, off_t, int, int *, void *); + +static int i2o_proc_read_groups(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_phys_device(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_claimed(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_users(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_priv_msgs(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_authorized_users(char *, char **, off_t, int, int *, void *); + +static int i2o_proc_read_dev_name(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_dev_identity(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_ddm_identity(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_uinfo(char *, char **, off_t, int, int *, void *); +static int i2o_proc_read_sgl_limits(char *, char **, off_t, int, int *, void *); + +static int i2o_proc_read_sensors(char *, char **, off_t, int, int *, void *); + +static int print_serial_number(char *, int, u8 *, int); + +static int i2o_proc_create_entries(void *, i2o_proc_entry *, + struct proc_dir_entry *); +static void i2o_proc_remove_entries(i2o_proc_entry *, struct proc_dir_entry *); +static int i2o_proc_add_controller(struct i2o_controller *, + struct proc_dir_entry * ); +static void i2o_proc_remove_controller(struct i2o_controller *, + struct proc_dir_entry * ); +static void i2o_proc_add_device(struct i2o_device *, struct proc_dir_entry *); +static void i2o_proc_remove_device(struct i2o_device *); +static int create_i2o_procfs(void); +static int destroy_i2o_procfs(void); +static void i2o_proc_new_dev(struct i2o_controller *, struct i2o_device *); +static void i2o_proc_dev_del(struct i2o_controller *, struct i2o_device *); + +static int i2o_proc_read_lan_dev_info(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_mac_addr(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_mcast_addr(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_batch_control(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_operation(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_media_operation(char *, char **, off_t, int, + int *, void *); +static int i2o_proc_read_lan_alt_addr(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_tx_info(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_rx_info(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_hist_stats(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_eth_stats(char *, char **, off_t, int, + int *, void *); +static int i2o_proc_read_lan_tr_stats(char *, char **, off_t, int, int *, + void *); +static int i2o_proc_read_lan_fddi_stats(char *, char **, off_t, int, int *, + void *); + +static struct proc_dir_entry *i2o_proc_dir_root; + +/* + * I2O OSM descriptor + */ +static struct i2o_handler i2o_proc_handler = +{ + NULL, + i2o_proc_new_dev, + i2o_proc_dev_del, + NULL, + "I2O procfs Layer", + 0, + 0xffffffff // All classes +}; + +/* + * IOP specific entries...write field just in case someone + * ever wants one. + */ +static i2o_proc_entry generic_iop_entries[] = +{ + {"hrt", S_IFREG|S_IRUGO, i2o_proc_read_hrt, NULL}, + {"lct", S_IFREG|S_IRUGO, i2o_proc_read_lct, NULL}, + {"status", S_IFREG|S_IRUGO, i2o_proc_read_status, NULL}, + {"hw", S_IFREG|S_IRUGO, i2o_proc_read_hw, NULL}, + {"ddm_table", S_IFREG|S_IRUGO, i2o_proc_read_ddm_table, NULL}, + {"driver_store", S_IFREG|S_IRUGO, i2o_proc_read_driver_store, NULL}, + {"drivers_stored", S_IFREG|S_IRUGO, i2o_proc_read_drivers_stored, NULL}, + {NULL, 0, NULL, NULL} +}; + +/* + * Device specific entries + */ +static i2o_proc_entry generic_dev_entries[] = +{ + {"groups", S_IFREG|S_IRUGO, i2o_proc_read_groups, NULL}, + {"phys_dev", S_IFREG|S_IRUGO, i2o_proc_read_phys_device, NULL}, + {"claimed", S_IFREG|S_IRUGO, i2o_proc_read_claimed, NULL}, + {"users", S_IFREG|S_IRUGO, i2o_proc_read_users, NULL}, + {"priv_msgs", S_IFREG|S_IRUGO, i2o_proc_read_priv_msgs, NULL}, + {"authorized_users", S_IFREG|S_IRUGO, i2o_proc_read_authorized_users, NULL}, + {"dev_identity", S_IFREG|S_IRUGO, i2o_proc_read_dev_identity, NULL}, + {"ddm_identity", S_IFREG|S_IRUGO, i2o_proc_read_ddm_identity, NULL}, + {"user_info", S_IFREG|S_IRUGO, i2o_proc_read_uinfo, NULL}, + {"sgl_limits", S_IFREG|S_IRUGO, i2o_proc_read_sgl_limits, NULL}, + {"sensors", S_IFREG|S_IRUGO, i2o_proc_read_sensors, NULL}, + {NULL, 0, NULL, NULL} +}; + +/* + * Storage unit specific entries (SCSI Periph, BS) with device names + */ +static i2o_proc_entry rbs_dev_entries[] = +{ + {"dev_name", S_IFREG|S_IRUGO, i2o_proc_read_dev_name, NULL}, + {NULL, 0, NULL, NULL} +}; + +#define SCSI_TABLE_SIZE 13 +static char *scsi_devices[] = +{ + "Direct-Access Read/Write", + "Sequential-Access Storage", + "Printer", + "Processor", + "WORM Device", + "CD-ROM Device", + "Scanner Device", + "Optical Memory Device", + "Medium Changer Device", + "Communications Device", + "Graphics Art Pre-Press Device", + "Graphics Art Pre-Press Device", + "Array Controller Device" +}; + +/* private */ + +/* + * Generic LAN specific entries + * + * Should groups with r/w entries have their own subdirectory? + * + */ +static i2o_proc_entry lan_entries[] = +{ + {"lan_dev_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_dev_info, NULL}, + {"lan_mac_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_mac_addr, NULL}, + {"lan_mcast_addr", S_IFREG|S_IRUGO|S_IWUSR, + i2o_proc_read_lan_mcast_addr, NULL}, + {"lan_batch_ctrl", S_IFREG|S_IRUGO|S_IWUSR, + i2o_proc_read_lan_batch_control, NULL}, + {"lan_operation", S_IFREG|S_IRUGO, i2o_proc_read_lan_operation, NULL}, + {"lan_media_operation", S_IFREG|S_IRUGO, + i2o_proc_read_lan_media_operation, NULL}, + {"lan_alt_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_alt_addr, NULL}, + {"lan_tx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_tx_info, NULL}, + {"lan_rx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_rx_info, NULL}, + + {"lan_hist_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_hist_stats, NULL}, + {NULL, 0, NULL, NULL} +}; + +/* + * Port specific LAN entries + * + */ +static i2o_proc_entry lan_eth_entries[] = +{ + {"lan_eth_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_eth_stats, NULL}, + {NULL, 0, NULL, NULL} +}; + +static i2o_proc_entry lan_tr_entries[] = +{ + {"lan_tr_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_tr_stats, NULL}, + {NULL, 0, NULL, NULL} +}; + +static i2o_proc_entry lan_fddi_entries[] = +{ + {"lan_fddi_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_fddi_stats, NULL}, + {NULL, 0, NULL, NULL} +}; + + +static char *chtostr(u8 *chars, int n) +{ + char tmp[256]; + tmp[0] = 0; + return strncat(tmp, (char *)chars, n); +} + +static int i2o_report_query_status(char *buf, int block_status, char *group) +{ + switch (block_status) + { + case -ETIMEDOUT: + return sprintf(buf, "Timeout reading group %s.\n",group); + case -ENOMEM: + return sprintf(buf, "No free memory to read the table.\n"); + case -I2O_PARAMS_STATUS_INVALID_GROUP_ID: + return sprintf(buf, "Group %s not supported.\n", group); + default: + return sprintf(buf, "Error reading group %s. BlockStatus 0x%02X\n", + group, -block_status); + } +} + +static char* bus_strings[] = +{ + "Local Bus", + "ISA", + "EISA", + "MCA", + "PCI", + "PCMCIA", + "NUBUS", + "CARDBUS" +}; + +static spinlock_t i2o_proc_lock = SPIN_LOCK_UNLOCKED; + +int i2o_proc_read_hrt(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller *)data; + i2o_hrt *hrt = (i2o_hrt *)c->hrt; + u32 bus; + int count; + int i; + + spin_lock(&i2o_proc_lock); + + len = 0; + + if(hrt->hrt_version) + { + len += sprintf(buf+len, + "HRT table for controller is too new a version.\n"); + spin_unlock(&i2o_proc_lock); + return len; + } + + count = hrt->num_entries; + + if((count * hrt->entry_len + 8) > 2048) { + printk(KERN_WARNING "i2o_proc: HRT does not fit into buffer\n"); + len += sprintf(buf+len, + "HRT table too big to fit in buffer.\n"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "HRT has %d entries of %d bytes each.\n", + count, hrt->entry_len << 2); + + for(i = 0; i < count; i++) + { + len += sprintf(buf+len, "Entry %d:\n", i); + len += sprintf(buf+len, " Adapter ID: %0#10x\n", + hrt->hrt_entry[i].adapter_id); + len += sprintf(buf+len, " Controlling tid: %0#6x\n", + hrt->hrt_entry[i].parent_tid); + + if(hrt->hrt_entry[i].bus_type != 0x80) + { + bus = hrt->hrt_entry[i].bus_type; + len += sprintf(buf+len, " %s Information\n", bus_strings[bus]); + + switch(bus) + { + case I2O_BUS_LOCAL: + len += sprintf(buf+len, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.local_bus.LbBaseIOPort); + len += sprintf(buf+len, " MemoryBase: %0#10x\n", + hrt->hrt_entry[i].bus.local_bus.LbBaseMemoryAddress); + break; + + case I2O_BUS_ISA: + len += sprintf(buf+len, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.isa_bus.IsaBaseIOPort); + len += sprintf(buf+len, " MemoryBase: %0#10x,", + hrt->hrt_entry[i].bus.isa_bus.IsaBaseMemoryAddress); + len += sprintf(buf+len, " CSN: %0#4x,", + hrt->hrt_entry[i].bus.isa_bus.CSN); + break; + + case I2O_BUS_EISA: + len += sprintf(buf+len, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.eisa_bus.EisaBaseIOPort); + len += sprintf(buf+len, " MemoryBase: %0#10x,", + hrt->hrt_entry[i].bus.eisa_bus.EisaBaseMemoryAddress); + len += sprintf(buf+len, " Slot: %0#4x,", + hrt->hrt_entry[i].bus.eisa_bus.EisaSlotNumber); + break; + + case I2O_BUS_MCA: + len += sprintf(buf+len, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.mca_bus.McaBaseIOPort); + len += sprintf(buf+len, " MemoryBase: %0#10x,", + hrt->hrt_entry[i].bus.mca_bus.McaBaseMemoryAddress); + len += sprintf(buf+len, " Slot: %0#4x,", + hrt->hrt_entry[i].bus.mca_bus.McaSlotNumber); + break; + + case I2O_BUS_PCI: + len += sprintf(buf+len, " Bus: %0#4x", + hrt->hrt_entry[i].bus.pci_bus.PciBusNumber); + len += sprintf(buf+len, " Dev: %0#4x", + hrt->hrt_entry[i].bus.pci_bus.PciDeviceNumber); + len += sprintf(buf+len, " Func: %0#4x", + hrt->hrt_entry[i].bus.pci_bus.PciFunctionNumber); + len += sprintf(buf+len, " Vendor: %0#6x", + hrt->hrt_entry[i].bus.pci_bus.PciVendorID); + len += sprintf(buf+len, " Device: %0#6x\n", + hrt->hrt_entry[i].bus.pci_bus.PciDeviceID); + break; + + default: + len += sprintf(buf+len, " Unsupported Bus Type\n"); + } + } + else + len += sprintf(buf+len, " Unknown Bus Type\n"); + } + + spin_unlock(&i2o_proc_lock); + + return len; +} + +int i2o_proc_read_lct(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller*)data; + i2o_lct *lct = (i2o_lct *)c->lct; + int entries; + int i; + +#define BUS_TABLE_SIZE 3 + static char *bus_ports[] = + { + "Generic Bus", + "SCSI Bus", + "Fibre Channel Bus" + }; + + spin_lock(&i2o_proc_lock); + len = 0; + + entries = (lct->table_size - 3)/9; + + len += sprintf(buf, "LCT contains %d %s\n", entries, + entries == 1 ? "entry" : "entries"); + if(lct->boot_tid) + len += sprintf(buf+len, "Boot Device @ ID %d\n", lct->boot_tid); + + len += + sprintf(buf+len, "Current Change Indicator: %#10x\n", lct->change_ind); + + for(i = 0; i < entries; i++) + { + len += sprintf(buf+len, "Entry %d\n", i); + len += sprintf(buf+len, " Class, SubClass : %s", i2o_get_class_name(lct->lct_entry[i].class_id)); + + /* + * Classes which we'll print subclass info for + */ + switch(lct->lct_entry[i].class_id & 0xFFF) + { + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + switch(lct->lct_entry[i].sub_class) + { + case 0x00: + len += sprintf(buf+len, ", Direct-Access Read/Write"); + break; + + case 0x04: + len += sprintf(buf+len, ", WORM Drive"); + break; + + case 0x05: + len += sprintf(buf+len, ", CD-ROM Drive"); + break; + + case 0x07: + len += sprintf(buf+len, ", Optical Memory Device"); + break; + + default: + len += sprintf(buf+len, ", Unknown (0x%02x)", + lct->lct_entry[i].sub_class); + break; + } + break; + + case I2O_CLASS_LAN: + switch(lct->lct_entry[i].sub_class & 0xFF) + { + case 0x30: + len += sprintf(buf+len, ", Ethernet"); + break; + + case 0x40: + len += sprintf(buf+len, ", 100base VG"); + break; + + case 0x50: + len += sprintf(buf+len, ", IEEE 802.5/Token-Ring"); + break; + + case 0x60: + len += sprintf(buf+len, ", ANSI X3T9.5 FDDI"); + break; + + case 0x70: + len += sprintf(buf+len, ", Fibre Channel"); + break; + + default: + len += sprintf(buf+len, ", Unknown Sub-Class (0x%02x)", + lct->lct_entry[i].sub_class & 0xFF); + break; + } + break; + + case I2O_CLASS_SCSI_PERIPHERAL: + if(lct->lct_entry[i].sub_class < SCSI_TABLE_SIZE) + len += sprintf(buf+len, ", %s", + scsi_devices[lct->lct_entry[i].sub_class]); + else + len += sprintf(buf+len, ", Unknown Device Type"); + break; + + case I2O_CLASS_BUS_ADAPTER_PORT: + if(lct->lct_entry[i].sub_class < BUS_TABLE_SIZE) + len += sprintf(buf+len, ", %s", + bus_ports[lct->lct_entry[i].sub_class]); + else + len += sprintf(buf+len, ", Unknown Bus Type"); + break; + } + len += sprintf(buf+len, "\n"); + + len += sprintf(buf+len, " Local TID : 0x%03x\n", lct->lct_entry[i].tid); + len += sprintf(buf+len, " User TID : 0x%03x\n", lct->lct_entry[i].user_tid); + len += sprintf(buf+len, " Parent TID : 0x%03x\n", + lct->lct_entry[i].parent_tid); + len += sprintf(buf+len, " Identity Tag : 0x%x%x%x%x%x%x%x%x\n", + lct->lct_entry[i].identity_tag[0], + lct->lct_entry[i].identity_tag[1], + lct->lct_entry[i].identity_tag[2], + lct->lct_entry[i].identity_tag[3], + lct->lct_entry[i].identity_tag[4], + lct->lct_entry[i].identity_tag[5], + lct->lct_entry[i].identity_tag[6], + lct->lct_entry[i].identity_tag[7]); + len += sprintf(buf+len, " Change Indicator : %0#10x\n", + lct->lct_entry[i].change_ind); + len += sprintf(buf+len, " Event Capab Mask : %0#10x\n", + lct->lct_entry[i].device_flags); + } + + spin_unlock(&i2o_proc_lock); + return len; +} + +int i2o_proc_read_status(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller*)data; + char prodstr[25]; + int version; + + spin_lock(&i2o_proc_lock); + len = 0; + + i2o_status_get(c); // reread the status block + + len += sprintf(buf+len,"Organization ID : %0#6x\n", + c->status_block->org_id); + + version = c->status_block->i2o_version; + +/* FIXME for Spec 2.0 + if (version == 0x02) { + len += sprintf(buf+len,"Lowest I2O version supported: "); + switch(workspace[2]) { + case 0x00: + len += sprintf(buf+len,"1.0\n"); + break; + case 0x01: + len += sprintf(buf+len,"1.5\n"); + break; + case 0x02: + len += sprintf(buf+len,"2.0\n"); + break; + } + + len += sprintf(buf+len, "Highest I2O version supported: "); + switch(workspace[3]) { + case 0x00: + len += sprintf(buf+len,"1.0\n"); + break; + case 0x01: + len += sprintf(buf+len,"1.5\n"); + break; + case 0x02: + len += sprintf(buf+len,"2.0\n"); + break; + } + } +*/ + len += sprintf(buf+len,"IOP ID : %0#5x\n", + c->status_block->iop_id); + len += sprintf(buf+len,"Host Unit ID : %0#6x\n", + c->status_block->host_unit_id); + len += sprintf(buf+len,"Segment Number : %0#5x\n", + c->status_block->segment_number); + + len += sprintf(buf+len, "I2O version : "); + switch (version) { + case 0x00: + len += sprintf(buf+len,"1.0\n"); + break; + case 0x01: + len += sprintf(buf+len,"1.5\n"); + break; + case 0x02: + len += sprintf(buf+len,"2.0\n"); + break; + default: + len += sprintf(buf+len,"Unknown version\n"); + } + + len += sprintf(buf+len, "IOP State : "); + switch (c->status_block->iop_state) { + case 0x01: + len += sprintf(buf+len,"INIT\n"); + break; + + case 0x02: + len += sprintf(buf+len,"RESET\n"); + break; + + case 0x04: + len += sprintf(buf+len,"HOLD\n"); + break; + + case 0x05: + len += sprintf(buf+len,"READY\n"); + break; + + case 0x08: + len += sprintf(buf+len,"OPERATIONAL\n"); + break; + + case 0x10: + len += sprintf(buf+len,"FAILED\n"); + break; + + case 0x11: + len += sprintf(buf+len,"FAULTED\n"); + break; + + default: + len += sprintf(buf+len,"Unknown\n"); + break; + } + + len += sprintf(buf+len,"Messenger Type : "); + switch (c->status_block->msg_type) { + case 0x00: + len += sprintf(buf+len,"Memory mapped\n"); + break; + case 0x01: + len += sprintf(buf+len,"Memory mapped only\n"); + break; + case 0x02: + len += sprintf(buf+len,"Remote only\n"); + break; + case 0x03: + len += sprintf(buf+len,"Memory mapped and remote\n"); + break; + default: + len += sprintf(buf+len,"Unknown\n"); + } + + len += sprintf(buf+len,"Inbound Frame Size : %d bytes\n", + c->status_block->inbound_frame_size<<2); + len += sprintf(buf+len,"Max Inbound Frames : %d\n", + c->status_block->max_inbound_frames); + len += sprintf(buf+len,"Current Inbound Frames : %d\n", + c->status_block->cur_inbound_frames); + len += sprintf(buf+len,"Max Outbound Frames : %d\n", + c->status_block->max_outbound_frames); + + /* Spec doesn't say if NULL terminated or not... */ + memcpy(prodstr, c->status_block->product_id, 24); + prodstr[24] = '\0'; + len += sprintf(buf+len,"Product ID : %s\n", prodstr); + len += sprintf(buf+len,"Expected LCT Size : %d bytes\n", + c->status_block->expected_lct_size); + + len += sprintf(buf+len,"IOP Capabilities\n"); + len += sprintf(buf+len," Context Field Size Support : "); + switch (c->status_block->iop_capabilities & 0x0000003) { + case 0: + len += sprintf(buf+len,"Supports only 32-bit context fields\n"); + break; + case 1: + len += sprintf(buf+len,"Supports only 64-bit context fields\n"); + break; + case 2: + len += sprintf(buf+len,"Supports 32-bit and 64-bit context fields, " + "but not concurrently\n"); + break; + case 3: + len += sprintf(buf+len,"Supports 32-bit and 64-bit context fields " + "concurrently\n"); + break; + default: + len += sprintf(buf+len,"0x%08x\n",c->status_block->iop_capabilities); + } + len += sprintf(buf+len," Current Context Field Size : "); + switch (c->status_block->iop_capabilities & 0x0000000C) { + case 0: + len += sprintf(buf+len,"not configured\n"); + break; + case 4: + len += sprintf(buf+len,"Supports only 32-bit context fields\n"); + break; + case 8: + len += sprintf(buf+len,"Supports only 64-bit context fields\n"); + break; + case 12: + len += sprintf(buf+len,"Supports both 32-bit or 64-bit context fields " + "concurrently\n"); + break; + default: + len += sprintf(buf+len,"\n"); + } + len += sprintf(buf+len," Inbound Peer Support : %s\n", + (c->status_block->iop_capabilities & 0x00000010) ? "Supported" : "Not supported"); + len += sprintf(buf+len," Outbound Peer Support : %s\n", + (c->status_block->iop_capabilities & 0x00000020) ? "Supported" : "Not supported"); + len += sprintf(buf+len," Peer to Peer Support : %s\n", + (c->status_block->iop_capabilities & 0x00000040) ? "Supported" : "Not supported"); + + len += sprintf(buf+len, "Desired private memory size : %d kB\n", + c->status_block->desired_mem_size>>10); + len += sprintf(buf+len, "Allocated private memory size : %d kB\n", + c->status_block->current_mem_size>>10); + len += sprintf(buf+len, "Private memory base address : %0#10x\n", + c->status_block->current_mem_base); + len += sprintf(buf+len, "Desired private I/O size : %d kB\n", + c->status_block->desired_io_size>>10); + len += sprintf(buf+len, "Allocated private I/O size : %d kB\n", + c->status_block->current_io_size>>10); + len += sprintf(buf+len, "Private I/O base address : %0#10x\n", + c->status_block->current_io_base); + + spin_unlock(&i2o_proc_lock); + + return len; +} + +int i2o_proc_read_hw(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller*)data; + static u32 work32[5]; + static u8 *work8 = (u8*)work32; + static u16 *work16 = (u16*)work32; + int token; + u32 hwcap; + + static char *cpu_table[] = + { + "Intel 80960 series", + "AMD2900 series", + "Motorola 68000 series", + "ARM series", + "MIPS series", + "Sparc series", + "PowerPC series", + "Intel x86 series" + }; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_scalar(c, ADAPTER_TID, 0x0000, -1, &work32, sizeof(work32)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0000 IOP Hardware"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "I2O Vendor ID : %0#6x\n", work16[0]); + len += sprintf(buf+len, "Product ID : %0#6x\n", work16[1]); + len += sprintf(buf+len, "CPU : "); + if(work8[16] > 8) + len += sprintf(buf+len, "Unknown\n"); + else + len += sprintf(buf+len, "%s\n", cpu_table[work8[16]]); + /* Anyone using ProcessorVersion? */ + + len += sprintf(buf+len, "RAM : %dkB\n", work32[1]>>10); + len += sprintf(buf+len, "Non-Volatile Mem : %dkB\n", work32[2]>>10); + + hwcap = work32[3]; + len += sprintf(buf+len, "Capabilities : 0x%08x\n", hwcap); + len += sprintf(buf+len, " [%s] Self booting\n", + (hwcap&0x00000001) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Upgradable IRTOS\n", + (hwcap&0x00000002) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Supports downloading DDMs\n", + (hwcap&0x00000004) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Supports installing DDMs\n", + (hwcap&0x00000008) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Battery-backed RAM\n", + (hwcap&0x00000010) ? "+" : "-"); + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +/* Executive group 0003h - Executing DDM List (table) */ +int i2o_proc_read_ddm_table(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller*)data; + int token; + int i; + + typedef struct _i2o_exec_execute_ddm_table { + u16 ddm_tid; + u8 module_type; + u8 reserved; + u16 i2o_vendor_id; + u16 module_id; + u8 module_name_version[28]; + u32 data_size; + u32 code_size; + } i2o_exec_execute_ddm_table; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + i2o_exec_execute_ddm_table ddm_table[MAX_I2O_MODULES]; + } result; + + i2o_exec_execute_ddm_table ddm_table; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + c, ADAPTER_TID, + 0x0003, -1, + NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0003 Executing DDM List"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Tid Module_type Vendor Mod_id Module_name Vrs Data_size Code_size\n"); + ddm_table=result.ddm_table[0]; + + for(i=0; i < result.row_count; ddm_table=result.ddm_table[++i]) + { + len += sprintf(buf+len, "0x%03x ", ddm_table.ddm_tid & 0xFFF); + + switch(ddm_table.module_type) + { + case 0x01: + len += sprintf(buf+len, "Downloaded DDM "); + break; + case 0x22: + len += sprintf(buf+len, "Embedded DDM "); + break; + default: + len += sprintf(buf+len, " "); + } + + len += sprintf(buf+len, "%-#7x", ddm_table.i2o_vendor_id); + len += sprintf(buf+len, "%-#8x", ddm_table.module_id); + len += sprintf(buf+len, "%-29s", chtostr(ddm_table.module_name_version, 28)); + len += sprintf(buf+len, "%9d ", ddm_table.data_size); + len += sprintf(buf+len, "%8d", ddm_table.code_size); + + len += sprintf(buf+len, "\n"); + } + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +/* Executive group 0004h - Driver Store (scalar) */ +int i2o_proc_read_driver_store(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_controller *c = (struct i2o_controller*)data; + u32 work32[8]; + int token; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_scalar(c, ADAPTER_TID, 0x0004, -1, &work32, sizeof(work32)); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0004 Driver Store"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Module limit : %d\n" + "Module count : %d\n" + "Current space : %d kB\n" + "Free space : %d kB\n", + work32[0], work32[1], work32[2]>>10, work32[3]>>10); + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +/* Executive group 0005h - Driver Store Table (table) */ +int i2o_proc_read_drivers_stored(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + typedef struct _i2o_driver_store { + u16 stored_ddm_index; + u8 module_type; + u8 reserved; + u16 i2o_vendor_id; + u16 module_id; + u8 module_name_version[28]; + u8 date[8]; + u32 module_size; + u32 mpb_size; + u32 module_flags; + } i2o_driver_store_table; + + struct i2o_controller *c = (struct i2o_controller*)data; + int token; + int i; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + i2o_driver_store_table dst[MAX_I2O_MODULES]; + } result; + + i2o_driver_store_table dst; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + c, ADAPTER_TID, 0x0005, -1, NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0005 DRIVER STORE TABLE"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "# Module_type Vendor Mod_id Module_name Vrs" + "Date Mod_size Par_size Flags\n"); + for(i=0, dst=result.dst[0]; i < result.row_count; dst=result.dst[++i]) + { + len += sprintf(buf+len, "%-3d", dst.stored_ddm_index); + switch(dst.module_type) + { + case 0x01: + len += sprintf(buf+len, "Downloaded DDM "); + break; + case 0x22: + len += sprintf(buf+len, "Embedded DDM "); + break; + default: + len += sprintf(buf+len, " "); + } + +#if 0 + if(c->i2oversion == 0x02) + len += sprintf(buf+len, "%-d", dst.module_state); +#endif + + len += sprintf(buf+len, "%-#7x", dst.i2o_vendor_id); + len += sprintf(buf+len, "%-#8x", dst.module_id); + len += sprintf(buf+len, "%-29s", chtostr(dst.module_name_version,28)); + len += sprintf(buf+len, "%-9s", chtostr(dst.date,8)); + len += sprintf(buf+len, "%8d ", dst.module_size); + len += sprintf(buf+len, "%8d ", dst.mpb_size); + len += sprintf(buf+len, "0x%04x", dst.module_flags); +#if 0 + if(c->i2oversion == 0x02) + len += sprintf(buf+len, "%d", + dst.notification_level); +#endif + len += sprintf(buf+len, "\n"); + } + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +/* Generic group F000h - Params Descriptor (table) */ +int i2o_proc_read_groups(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + u8 properties; + + typedef struct _i2o_group_info + { + u16 group_number; + u16 field_count; + u16 row_count; + u8 properties; + u8 reserved; + } i2o_group_info; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + i2o_group_info group[256]; + } result; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, 0xF000, -1, NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len = i2o_report_query_status(buf+len, token, "0xF000 Params Descriptor"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "# Group FieldCount RowCount Type Add Del Clear\n"); + + for (i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%-3d", i); + len += sprintf(buf+len, "0x%04X ", result.group[i].group_number); + len += sprintf(buf+len, "%10d ", result.group[i].field_count); + len += sprintf(buf+len, "%8d ", result.group[i].row_count); + + properties = result.group[i].properties; + if (properties & 0x1) len += sprintf(buf+len, "Table "); + else len += sprintf(buf+len, "Scalar "); + if (properties & 0x2) len += sprintf(buf+len, " + "); + else len += sprintf(buf+len, " - "); + if (properties & 0x4) len += sprintf(buf+len, " + "); + else len += sprintf(buf+len, " - "); + if (properties & 0x8) len += sprintf(buf+len, " + "); + else len += sprintf(buf+len, " - "); + + len += sprintf(buf+len, "\n"); + } + + if (result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +/* Generic group F001h - Physical Device Table (table) */ +int i2o_proc_read_phys_device(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + u32 adapter_id[64]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0xF001, -1, NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF001 Physical Device Table"); + spin_unlock(&i2o_proc_lock); + return len; + } + + if (result.row_count) + len += sprintf(buf+len, "# AdapterId\n"); + + for (i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%-2d", i); + len += sprintf(buf+len, "%#7x\n", result.adapter_id[i]); + } + + if (result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* Generic group F002h - Claimed Table (table) */ +int i2o_proc_read_claimed(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + + struct { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + u16 claimed_tid[64]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0xF002, -1, NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF002 Claimed Table"); + spin_unlock(&i2o_proc_lock); + return len; + } + + if (result.row_count) + len += sprintf(buf+len, "# ClaimedTid\n"); + + for (i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%-2d", i); + len += sprintf(buf+len, "%#7x\n", result.claimed_tid[i]); + } + + if (result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* Generic group F003h - User Table (table) */ +int i2o_proc_read_users(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + + typedef struct _i2o_user_table + { + u16 instance; + u16 user_tid; + u8 claim_type; + u8 reserved1; + u16 reserved2; + } i2o_user_table; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + i2o_user_table user[64]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0xF003, -1, NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF003 User Table"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "# Instance UserTid ClaimType\n"); + + for(i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%-3d", i); + len += sprintf(buf+len, "%#8x ", result.user[i].instance); + len += sprintf(buf+len, "%#7x ", result.user[i].user_tid); + len += sprintf(buf+len, "%#9x\n", result.user[i].claim_type); + } + + if (result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* Generic group F005h - Private message extensions (table) (optional) */ +int i2o_proc_read_priv_msgs(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + + typedef struct _i2o_private + { + u16 ext_instance; + u16 organization_id; + u16 x_function_code; + } i2o_private; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + i2o_private extension[64]; + } result; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0xF000, -1, + NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF005 Private Message Extensions (optional)"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Instance# OrgId FunctionCode\n"); + + for(i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%0#9x ", result.extension[i].ext_instance); + len += sprintf(buf+len, "%0#6x ", result.extension[i].organization_id); + len += sprintf(buf+len, "%0#6x", result.extension[i].x_function_code); + + len += sprintf(buf+len, "\n"); + } + + if(result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +/* Generic group F006h - Authorized User Table (table) */ +int i2o_proc_read_authorized_users(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + u32 alternate_tid[64]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0xF006, -1, + NULL, 0, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF006 Autohorized User Table"); + spin_unlock(&i2o_proc_lock); + return len; + } + + if (result.row_count) + len += sprintf(buf+len, "# AlternateTid\n"); + + for(i=0; i < result.row_count; i++) + { + len += sprintf(buf+len, "%-2d", i); + len += sprintf(buf+len, "%#7x ", result.alternate_tid[i]); + } + + if (result.more_flag) + len += sprintf(buf+len, "There is more...\n"); + + spin_unlock(&i2o_proc_lock); + return len; +} + + +/* Generic group F100h - Device Identity (scalar) */ +int i2o_proc_read_dev_identity(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[128]; // allow for "stuff" + up to 256 byte (max) serial number + // == (allow) 512d bytes (max) + static u16 *work16 = (u16*)work32; + int token; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0xF100, -1, + &work32, sizeof(work32)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token ,"0xF100 Device Identity"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Device Class : %s\n", i2o_get_class_name(work16[0])); + len += sprintf(buf+len, "Owner TID : %0#5x\n", work16[2]); + len += sprintf(buf+len, "Parent TID : %0#5x\n", work16[3]); + len += sprintf(buf+len, "Vendor info : %s\n", chtostr((u8 *)(work32+2), 16)); + len += sprintf(buf+len, "Product info : %s\n", chtostr((u8 *)(work32+6), 16)); + len += sprintf(buf+len, "Description : %s\n", chtostr((u8 *)(work32+10), 16)); + len += sprintf(buf+len, "Product rev. : %s\n", chtostr((u8 *)(work32+14), 8)); + + len += sprintf(buf+len, "Serial number : "); + len = print_serial_number(buf, len, + (u8*)(work32+16), + /* allow for SNLen plus + * possible trailing '\0' + */ + sizeof(work32)-(16*sizeof(u32))-2 + ); + len += sprintf(buf+len, "\n"); + + spin_unlock(&i2o_proc_lock); + + return len; +} + + +int i2o_proc_read_dev_name(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + + if ( d->dev_name[0] == '\0' ) + return 0; + + len = sprintf(buf, "%s\n", d->dev_name); + + return len; +} + + +/* Generic group F101h - DDM Identity (scalar) */ +int i2o_proc_read_ddm_identity(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u16 ddm_tid; + u8 module_name[24]; + u8 module_rev[8]; + u8 sn_format; + u8 serial_number[12]; + u8 pad[256]; // allow up to 256 byte (max) serial number + } result; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0xF101, -1, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF101 DDM Identity"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Registering DDM TID : 0x%03x\n", result.ddm_tid); + len += sprintf(buf+len, "Module name : %s\n", chtostr(result.module_name, 24)); + len += sprintf(buf+len, "Module revision : %s\n", chtostr(result.module_rev, 8)); + + len += sprintf(buf+len, "Serial number : "); + len = print_serial_number(buf, len, result.serial_number, sizeof(result)-36); + /* allow for SNLen plus possible trailing '\0' */ + + len += sprintf(buf+len, "\n"); + + spin_unlock(&i2o_proc_lock); + + return len; +} + +/* Generic group F102h - User Information (scalar) */ +int i2o_proc_read_uinfo(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u8 device_name[64]; + u8 service_name[64]; + u8 physical_location[64]; + u8 instance_number[4]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0xF102, -1, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF102 User Information"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Device name : %s\n", chtostr(result.device_name, 64)); + len += sprintf(buf+len, "Service name : %s\n", chtostr(result.service_name, 64)); + len += sprintf(buf+len, "Physical name : %s\n", chtostr(result.physical_location, 64)); + len += sprintf(buf+len, "Instance number : %s\n", chtostr(result.instance_number, 4)); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* Generic group F103h - SGL Operating Limits (scalar) */ +int i2o_proc_read_sgl_limits(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[12]; + static u16 *work16 = (u16 *)work32; + static u8 *work8 = (u8 *)work32; + int token; + + spin_lock(&i2o_proc_lock); + + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0xF103, -1, + &work32, sizeof(work32)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF103 SGL Operating Limits"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "SGL chain size : %d\n", work32[0]); + len += sprintf(buf+len, "Max SGL chain size : %d\n", work32[1]); + len += sprintf(buf+len, "SGL chain size target : %d\n", work32[2]); + len += sprintf(buf+len, "SGL frag count : %d\n", work16[6]); + len += sprintf(buf+len, "Max SGL frag count : %d\n", work16[7]); + len += sprintf(buf+len, "SGL frag count target : %d\n", work16[8]); + + if (d->i2oversion == 0x02) + { + len += sprintf(buf+len, "SGL data alignment : %d\n", work16[8]); + len += sprintf(buf+len, "SGL addr limit : %d\n", work8[20]); + len += sprintf(buf+len, "SGL addr sizes supported : "); + if (work8[21] & 0x01) + len += sprintf(buf+len, "32 bit "); + if (work8[21] & 0x02) + len += sprintf(buf+len, "64 bit "); + if (work8[21] & 0x04) + len += sprintf(buf+len, "96 bit "); + if (work8[21] & 0x08) + len += sprintf(buf+len, "128 bit "); + len += sprintf(buf+len, "\n"); + } + + spin_unlock(&i2o_proc_lock); + + return len; +} + +/* Generic group F200h - Sensors (scalar) */ +int i2o_proc_read_sensors(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u16 sensor_instance; + u8 component; + u16 component_instance; + u8 sensor_class; + u8 sensor_type; + u8 scaling_exponent; + u32 actual_reading; + u32 minimum_reading; + u32 low2lowcat_treshold; + u32 lowcat2low_treshold; + u32 lowwarn2low_treshold; + u32 low2lowwarn_treshold; + u32 norm2lowwarn_treshold; + u32 lowwarn2norm_treshold; + u32 nominal_reading; + u32 hiwarn2norm_treshold; + u32 norm2hiwarn_treshold; + u32 high2hiwarn_treshold; + u32 hiwarn2high_treshold; + u32 hicat2high_treshold; + u32 hi2hicat_treshold; + u32 maximum_reading; + u8 sensor_state; + u16 event_enable; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0xF200, -1, + &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0xF200 Sensors (optional)"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Sensor instance : %d\n", result.sensor_instance); + + len += sprintf(buf+len, "Component : %d = ", result.component); + switch (result.component) + { + case 0: len += sprintf(buf+len, "Other"); + break; + case 1: len += sprintf(buf+len, "Planar logic Board"); + break; + case 2: len += sprintf(buf+len, "CPU"); + break; + case 3: len += sprintf(buf+len, "Chassis"); + break; + case 4: len += sprintf(buf+len, "Power Supply"); + break; + case 5: len += sprintf(buf+len, "Storage"); + break; + case 6: len += sprintf(buf+len, "External"); + break; + } + len += sprintf(buf+len,"\n"); + + len += sprintf(buf+len, "Component instance : %d\n", result.component_instance); + len += sprintf(buf+len, "Sensor class : %s\n", + result.sensor_class ? "Analog" : "Digital"); + + len += sprintf(buf+len, "Sensor type : %d = ",result.sensor_type); + switch (result.sensor_type) + { + case 0: len += sprintf(buf+len, "Other\n"); + break; + case 1: len += sprintf(buf+len, "Thermal\n"); + break; + case 2: len += sprintf(buf+len, "DC voltage (DC volts)\n"); + break; + case 3: len += sprintf(buf+len, "AC voltage (AC volts)\n"); + break; + case 4: len += sprintf(buf+len, "DC current (DC amps)\n"); + break; + case 5: len += sprintf(buf+len, "AC current (AC volts)\n"); + break; + case 6: len += sprintf(buf+len, "Door open\n"); + break; + case 7: len += sprintf(buf+len, "Fan operational\n"); + break; + } + + len += sprintf(buf+len, "Scaling exponent : %d\n", result.scaling_exponent); + len += sprintf(buf+len, "Actual reading : %d\n", result.actual_reading); + len += sprintf(buf+len, "Minimum reading : %d\n", result.minimum_reading); + len += sprintf(buf+len, "Low2LowCat treshold : %d\n", result.low2lowcat_treshold); + len += sprintf(buf+len, "LowCat2Low treshold : %d\n", result.lowcat2low_treshold); + len += sprintf(buf+len, "LowWarn2Low treshold : %d\n", result.lowwarn2low_treshold); + len += sprintf(buf+len, "Low2LowWarn treshold : %d\n", result.low2lowwarn_treshold); + len += sprintf(buf+len, "Norm2LowWarn treshold : %d\n", result.norm2lowwarn_treshold); + len += sprintf(buf+len, "LowWarn2Norm treshold : %d\n", result.lowwarn2norm_treshold); + len += sprintf(buf+len, "Nominal reading : %d\n", result.nominal_reading); + len += sprintf(buf+len, "HiWarn2Norm treshold : %d\n", result.hiwarn2norm_treshold); + len += sprintf(buf+len, "Norm2HiWarn treshold : %d\n", result.norm2hiwarn_treshold); + len += sprintf(buf+len, "High2HiWarn treshold : %d\n", result.high2hiwarn_treshold); + len += sprintf(buf+len, "HiWarn2High treshold : %d\n", result.hiwarn2high_treshold); + len += sprintf(buf+len, "HiCat2High treshold : %d\n", result.hicat2high_treshold); + len += sprintf(buf+len, "High2HiCat treshold : %d\n", result.hi2hicat_treshold); + len += sprintf(buf+len, "Maximum reading : %d\n", result.maximum_reading); + + len += sprintf(buf+len, "Sensor state : %d = ", result.sensor_state); + switch (result.sensor_state) + { + case 0: len += sprintf(buf+len, "Normal\n"); + break; + case 1: len += sprintf(buf+len, "Abnormal\n"); + break; + case 2: len += sprintf(buf+len, "Unknown\n"); + break; + case 3: len += sprintf(buf+len, "Low Catastrophic (LoCat)\n"); + break; + case 4: len += sprintf(buf+len, "Low (Low)\n"); + break; + case 5: len += sprintf(buf+len, "Low Warning (LoWarn)\n"); + break; + case 6: len += sprintf(buf+len, "High Warning (HiWarn)\n"); + break; + case 7: len += sprintf(buf+len, "High (High)\n"); + break; + case 8: len += sprintf(buf+len, "High Catastrophic (HiCat)\n"); + break; + } + + len += sprintf(buf+len, "Event_enable : 0x%02X\n", result.event_enable); + len += sprintf(buf+len, " [%s] Operational state change. \n", + (result.event_enable & 0x01) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] Low catastrophic. \n", + (result.event_enable & 0x02) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] Low reading. \n", + (result.event_enable & 0x04) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] Low warning. \n", + (result.event_enable & 0x08) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] Change back to normal from out of range state. \n", + (result.event_enable & 0x10) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] High warning. \n", + (result.event_enable & 0x20) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] High reading. \n", + (result.event_enable & 0x40) ? "+" : "-" ); + len += sprintf(buf+len, " [%s] High catastrophic. \n", + (result.event_enable & 0x80) ? "+" : "-" ); + + spin_unlock(&i2o_proc_lock); + return len; +} + + +static int print_serial_number(char *buff, int pos, u8 *serialno, int max_len) +{ + int i; + + /* 19990419 -sralston + * The I2O v1.5 (and v2.0 so far) "official specification" + * got serial numbers WRONG! + * Apparently, and despite what Section 3.4.4 says and + * Figure 3-35 shows (pg 3-39 in the pdf doc), + * the convention / consensus seems to be: + * + First byte is SNFormat + * + Second byte is SNLen (but only if SNFormat==7 (?)) + * + (v2.0) SCSI+BS may use IEEE Registered (64 or 128 bit) format + */ + switch(serialno[0]) + { + case I2O_SNFORMAT_BINARY: /* Binary */ + pos += sprintf(buff+pos, "0x"); + for(i = 0; i < serialno[1]; i++) + { + pos += sprintf(buff+pos, "%02X", serialno[2+i]); + } + break; + + case I2O_SNFORMAT_ASCII: /* ASCII */ + if ( serialno[1] < ' ' ) /* printable or SNLen? */ + { + /* sanity */ + max_len = (max_len < serialno[1]) ? max_len : serialno[1]; + serialno[1+max_len] = '\0'; + + /* just print it */ + pos += sprintf(buff+pos, "%s", &serialno[2]); + } + else + { + /* print chars for specified length */ + for(i = 0; i < serialno[1]; i++) + { + pos += sprintf(buff+pos, "%c", serialno[2+i]); + } + } + break; + + case I2O_SNFORMAT_UNICODE: /* UNICODE */ + pos += sprintf(buff+pos, "UNICODE Format. Can't Display\n"); + break; + + case I2O_SNFORMAT_LAN48_MAC: /* LAN-48 MAC Address */ + pos += sprintf(buff+pos, + "LAN-48 MAC address @ %02X:%02X:%02X:%02X:%02X:%02X", + serialno[2], serialno[3], + serialno[4], serialno[5], + serialno[6], serialno[7]); + break; + + case I2O_SNFORMAT_WAN: /* WAN MAC Address */ + /* FIXME: Figure out what a WAN access address looks like?? */ + pos += sprintf(buff+pos, "WAN Access Address"); + break; + +/* plus new in v2.0 */ + case I2O_SNFORMAT_LAN64_MAC: /* LAN-64 MAC Address */ + /* FIXME: Figure out what a LAN-64 address really looks like?? */ + pos += sprintf(buff+pos, + "LAN-64 MAC address @ [?:%02X:%02X:?] %02X:%02X:%02X:%02X:%02X:%02X", + serialno[8], serialno[9], + serialno[2], serialno[3], + serialno[4], serialno[5], + serialno[6], serialno[7]); + break; + + + case I2O_SNFORMAT_DDM: /* I2O DDM */ + pos += sprintf(buff+pos, + "DDM: Tid=%03Xh, Rsvd=%04Xh, OrgId=%04Xh", + *(u16*)&serialno[2], + *(u16*)&serialno[4], + *(u16*)&serialno[6]); + break; + + case I2O_SNFORMAT_IEEE_REG64: /* IEEE Registered (64-bit) */ + case I2O_SNFORMAT_IEEE_REG128: /* IEEE Registered (128-bit) */ + /* FIXME: Figure if this is even close?? */ + pos += sprintf(buff+pos, + "IEEE NodeName(hi,lo)=(%08Xh:%08Xh), PortName(hi,lo)=(%08Xh:%08Xh)\n", + *(u32*)&serialno[2], + *(u32*)&serialno[6], + *(u32*)&serialno[10], + *(u32*)&serialno[14]); + break; + + + case I2O_SNFORMAT_UNKNOWN: /* Unknown 0 */ + case I2O_SNFORMAT_UNKNOWN2: /* Unknown 0xff */ + default: + pos += sprintf(buff+pos, "Unknown data format (0x%02x)", + serialno[0]); + break; + } + + return pos; +} + +const char * i2o_get_connector_type(int conn) +{ + int idx = 16; + static char *i2o_connector_type[] = { + "OTHER", + "UNKNOWN", + "AUI", + "UTP", + "BNC", + "RJ45", + "STP DB9", + "FIBER MIC", + "APPLE AUI", + "MII", + "DB9", + "HSSDC", + "DUPLEX SC FIBER", + "DUPLEX ST FIBER", + "TNC/BNC", + "HW DEFAULT" + }; + + switch(conn) + { + case 0x00000000: + idx = 0; + break; + case 0x00000001: + idx = 1; + break; + case 0x00000002: + idx = 2; + break; + case 0x00000003: + idx = 3; + break; + case 0x00000004: + idx = 4; + break; + case 0x00000005: + idx = 5; + break; + case 0x00000006: + idx = 6; + break; + case 0x00000007: + idx = 7; + break; + case 0x00000008: + idx = 8; + break; + case 0x00000009: + idx = 9; + break; + case 0x0000000A: + idx = 10; + break; + case 0x0000000B: + idx = 11; + break; + case 0x0000000C: + idx = 12; + break; + case 0x0000000D: + idx = 13; + break; + case 0x0000000E: + idx = 14; + break; + case 0xFFFFFFFF: + idx = 15; + break; + } + + return i2o_connector_type[idx]; +} + + +const char * i2o_get_connection_type(int conn) +{ + int idx = 0; + static char *i2o_connection_type[] = { + "Unknown", + "AUI", + "10BASE5", + "FIORL", + "10BASE2", + "10BROAD36", + "10BASE-T", + "10BASE-FP", + "10BASE-FB", + "10BASE-FL", + "100BASE-TX", + "100BASE-FX", + "100BASE-T4", + "1000BASE-SX", + "1000BASE-LX", + "1000BASE-CX", + "1000BASE-T", + "100VG-ETHERNET", + "100VG-TOKEN RING", + "4MBIT TOKEN RING", + "16 Mb Token Ring", + "125 MBAUD FDDI", + "Point-to-point", + "Arbitrated loop", + "Public loop", + "Fabric", + "Emulation", + "Other", + "HW default" + }; + + switch(conn) + { + case I2O_LAN_UNKNOWN: + idx = 0; + break; + case I2O_LAN_AUI: + idx = 1; + break; + case I2O_LAN_10BASE5: + idx = 2; + break; + case I2O_LAN_FIORL: + idx = 3; + break; + case I2O_LAN_10BASE2: + idx = 4; + break; + case I2O_LAN_10BROAD36: + idx = 5; + break; + case I2O_LAN_10BASE_T: + idx = 6; + break; + case I2O_LAN_10BASE_FP: + idx = 7; + break; + case I2O_LAN_10BASE_FB: + idx = 8; + break; + case I2O_LAN_10BASE_FL: + idx = 9; + break; + case I2O_LAN_100BASE_TX: + idx = 10; + break; + case I2O_LAN_100BASE_FX: + idx = 11; + break; + case I2O_LAN_100BASE_T4: + idx = 12; + break; + case I2O_LAN_1000BASE_SX: + idx = 13; + break; + case I2O_LAN_1000BASE_LX: + idx = 14; + break; + case I2O_LAN_1000BASE_CX: + idx = 15; + break; + case I2O_LAN_1000BASE_T: + idx = 16; + break; + case I2O_LAN_100VG_ETHERNET: + idx = 17; + break; + case I2O_LAN_100VG_TR: + idx = 18; + break; + case I2O_LAN_4MBIT: + idx = 19; + break; + case I2O_LAN_16MBIT: + idx = 20; + break; + case I2O_LAN_125MBAUD: + idx = 21; + break; + case I2O_LAN_POINT_POINT: + idx = 22; + break; + case I2O_LAN_ARB_LOOP: + idx = 23; + break; + case I2O_LAN_PUBLIC_LOOP: + idx = 24; + break; + case I2O_LAN_FABRIC: + idx = 25; + break; + case I2O_LAN_EMULATION: + idx = 26; + break; + case I2O_LAN_OTHER: + idx = 27; + break; + case I2O_LAN_DEFAULT: + idx = 28; + break; + } + + return i2o_connection_type[idx]; +} + + +/* LAN group 0000h - Device info (scalar) */ +int i2o_proc_read_lan_dev_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[56]; + static u8 *work8 = (u8*)work32; + static u16 *work16 = (u16*)work32; + static u64 *work64 = (u64*)work32; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0000, -1, &work32, 56*4); + if (token < 0) { + len += i2o_report_query_status(buf+len, token, "0x0000 LAN Device Info"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "LAN Type : "); + switch (work16[0]) + { + case 0x0030: + len += sprintf(buf+len, "Ethernet, "); + break; + case 0x0040: + len += sprintf(buf+len, "100Base VG, "); + break; + case 0x0050: + len += sprintf(buf+len, "Token Ring, "); + break; + case 0x0060: + len += sprintf(buf+len, "FDDI, "); + break; + case 0x0070: + len += sprintf(buf+len, "Fibre Channel, "); + break; + default: + len += sprintf(buf+len, "Unknown type (0x%04x), ", work16[0]); + break; + } + + if (work16[1]&0x00000001) + len += sprintf(buf+len, "emulated LAN, "); + else + len += sprintf(buf+len, "physical LAN port, "); + + if (work16[1]&0x00000002) + len += sprintf(buf+len, "full duplex\n"); + else + len += sprintf(buf+len, "simplex\n"); + + len += sprintf(buf+len, "Address format : "); + switch(work8[4]) { + case 0x00: + len += sprintf(buf+len, "IEEE 48bit\n"); + break; + case 0x01: + len += sprintf(buf+len, "FC IEEE\n"); + break; + default: + len += sprintf(buf+len, "Unknown (0x%02x)\n", work8[4]); + break; + } + + len += sprintf(buf+len, "State : "); + switch(work8[5]) + { + case 0x00: + len += sprintf(buf+len, "Unknown\n"); + break; + case 0x01: + len += sprintf(buf+len, "Unclaimed\n"); + break; + case 0x02: + len += sprintf(buf+len, "Operational\n"); + break; + case 0x03: + len += sprintf(buf+len, "Suspended\n"); + break; + case 0x04: + len += sprintf(buf+len, "Resetting\n"); + break; + case 0x05: + len += sprintf(buf+len, "ERROR: "); + if(work16[3]&0x0001) + len += sprintf(buf+len, "TxCU inoperative "); + if(work16[3]&0x0002) + len += sprintf(buf+len, "RxCU inoperative "); + if(work16[3]&0x0004) + len += sprintf(buf+len, "Local mem alloc "); + len += sprintf(buf+len, "\n"); + break; + case 0x06: + len += sprintf(buf+len, "Operational no Rx\n"); + break; + case 0x07: + len += sprintf(buf+len, "Suspended no Rx\n"); + break; + default: + len += sprintf(buf+len, "Unspecified\n"); + break; + } + + len += sprintf(buf+len, "Min packet size : %d\n", work32[2]); + len += sprintf(buf+len, "Max packet size : %d\n", work32[3]); + len += sprintf(buf+len, "HW address : " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + work8[16],work8[17],work8[18],work8[19], + work8[20],work8[21],work8[22],work8[23]); + + len += sprintf(buf+len, "Max Tx wire speed : %d bps\n", (int)work64[3]); + len += sprintf(buf+len, "Max Rx wire speed : %d bps\n", (int)work64[4]); + + len += sprintf(buf+len, "Min SDU packet size : 0x%08x\n", work32[10]); + len += sprintf(buf+len, "Max SDU packet size : 0x%08x\n", work32[11]); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0001h - MAC address table (scalar) */ +int i2o_proc_read_lan_mac_addr(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[48]; + static u8 *work8 = (u8*)work32; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0001, -1, &work32, 48*4); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0001 LAN MAC Address"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Active address : " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + work8[0],work8[1],work8[2],work8[3], + work8[4],work8[5],work8[6],work8[7]); + len += sprintf(buf+len, "Current address : " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + work8[8],work8[9],work8[10],work8[11], + work8[12],work8[13],work8[14],work8[15]); + len += sprintf(buf+len, "Functional address mask : " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + work8[16],work8[17],work8[18],work8[19], + work8[20],work8[21],work8[22],work8[23]); + + len += sprintf(buf+len,"HW/DDM capabilities : 0x%08x\n", work32[7]); + len += sprintf(buf+len," [%s] Unicast packets supported\n", + (work32[7]&0x00000001)?"+":"-"); + len += sprintf(buf+len," [%s] Promiscuous mode supported\n", + (work32[7]&0x00000002)?"+":"-"); + len += sprintf(buf+len," [%s] Promiscuous multicast mode supported\n", + (work32[7]&0x00000004)?"+":"-"); + len += sprintf(buf+len," [%s] Broadcast reception disabling supported\n", + (work32[7]&0x00000100)?"+":"-"); + len += sprintf(buf+len," [%s] Multicast reception disabling supported\n", + (work32[7]&0x00000200)?"+":"-"); + len += sprintf(buf+len," [%s] Functional address disabling supported\n", + (work32[7]&0x00000400)?"+":"-"); + len += sprintf(buf+len," [%s] MAC reporting supported\n", + (work32[7]&0x00000800)?"+":"-"); + + len += sprintf(buf+len,"Filter mask : 0x%08x\n", work32[6]); + len += sprintf(buf+len," [%s] Unicast packets disable\n", + (work32[6]&0x00000001)?"+":"-"); + len += sprintf(buf+len," [%s] Promiscuous mode enable\n", + (work32[6]&0x00000002)?"+":"-"); + len += sprintf(buf+len," [%s] Promiscuous multicast mode enable\n", + (work32[6]&0x00000004)?"+":"-"); + len += sprintf(buf+len," [%s] Broadcast packets disable\n", + (work32[6]&0x00000100)?"+":"-"); + len += sprintf(buf+len," [%s] Multicast packets disable\n", + (work32[6]&0x00000200)?"+":"-"); + len += sprintf(buf+len," [%s] Functional address disable\n", + (work32[6]&0x00000400)?"+":"-"); + + if (work32[7]&0x00000800) { + len += sprintf(buf+len, " MAC reporting mode : "); + if (work32[6]&0x00000800) + len += sprintf(buf+len, "Pass only priority MAC packets to user\n"); + else if (work32[6]&0x00001000) + len += sprintf(buf+len, "Pass all MAC packets to user\n"); + else if (work32[6]&0x00001800) + len += sprintf(buf+len, "Pass all MAC packets (promiscuous) to user\n"); + else + len += sprintf(buf+len, "Do not pass MAC packets to user\n"); + } + len += sprintf(buf+len, "Number of multicast addresses : %d\n", work32[8]); + len += sprintf(buf+len, "Perfect filtering for max %d multicast addresses\n", + work32[9]); + len += sprintf(buf+len, "Imperfect filtering for max %d multicast addresses\n", + work32[10]); + + spin_unlock(&i2o_proc_lock); + + return len; +} + +/* LAN group 0002h - Multicast MAC address table (table) */ +int i2o_proc_read_lan_mcast_addr(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + u8 mc_addr[8]; + + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + u8 mc_addr[256][8]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, 0x0002, -1, + NULL, 0, &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x002 LAN Multicast MAC Address"); + spin_unlock(&i2o_proc_lock); + return len; + } + + for (i = 0; i < result.row_count; i++) + { + memcpy(mc_addr, result.mc_addr[i], 8); + + len += sprintf(buf+len, "MC MAC address[%d]: " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + i, mc_addr[0], mc_addr[1], mc_addr[2], + mc_addr[3], mc_addr[4], mc_addr[5], + mc_addr[6], mc_addr[7]); + } + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0003h - Batch Control (scalar) */ +int i2o_proc_read_lan_batch_control(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[9]; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0003, -1, &work32, 9*4); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0003 LAN Batch Control"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Batch mode "); + if (work32[0]&0x00000001) + len += sprintf(buf+len, "disabled"); + else + len += sprintf(buf+len, "enabled"); + if (work32[0]&0x00000002) + len += sprintf(buf+len, " (current setting)"); + if (work32[0]&0x00000004) + len += sprintf(buf+len, ", forced"); + else + len += sprintf(buf+len, ", toggle"); + len += sprintf(buf+len, "\n"); + + len += sprintf(buf+len, "Max Rx batch count : %d\n", work32[5]); + len += sprintf(buf+len, "Max Rx batch delay : %d\n", work32[6]); + len += sprintf(buf+len, "Max Tx batch delay : %d\n", work32[7]); + len += sprintf(buf+len, "Max Tx batch count : %d\n", work32[8]); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0004h - LAN Operation (scalar) */ +int i2o_proc_read_lan_operation(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[5]; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0004, -1, &work32, 20); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0004 LAN Operation"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Packet prepadding (32b words) : %d\n", work32[0]); + len += sprintf(buf+len, "Transmission error reporting : %s\n", + (work32[1]&1)?"on":"off"); + len += sprintf(buf+len, "Bad packet handling : %s\n", + (work32[1]&0x2)?"by host":"by DDM"); + len += sprintf(buf+len, "Packet orphan limit : %d\n", work32[2]); + + len += sprintf(buf+len, "Tx modes : 0x%08x\n", work32[3]); + len += sprintf(buf+len, " [%s] HW CRC suppression\n", + (work32[3]&0x00000004) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW IPv4 checksum\n", + (work32[3]&0x00000100) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW TCP checksum\n", + (work32[3]&0x00000200) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW UDP checksum\n", + (work32[3]&0x00000400) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW RSVP checksum\n", + (work32[3]&0x00000800) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW ICMP checksum\n", + (work32[3]&0x00001000) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Loopback suppression enable\n", + (work32[3]&0x00002000) ? "+" : "-"); + + len += sprintf(buf+len, "Rx modes : 0x%08x\n", work32[4]); + len += sprintf(buf+len, " [%s] FCS in payload\n", + (work32[4]&0x00000004) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW IPv4 checksum validation\n", + (work32[4]&0x00000100) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW TCP checksum validation\n", + (work32[4]&0x00000200) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW UDP checksum validation\n", + (work32[4]&0x00000400) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW RSVP checksum validation\n", + (work32[4]&0x00000800) ? "+" : "-"); + len += sprintf(buf+len, " [%s] HW ICMP checksum validation\n", + (work32[4]&0x00001000) ? "+" : "-"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0005h - Media operation (scalar) */ +int i2o_proc_read_lan_media_operation(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u32 connector_type; + u32 connection_type; + u64 current_tx_wire_speed; + u64 current_rx_wire_speed; + u8 duplex_mode; + u8 link_status; + u8 reserved; + u8 duplex_mode_target; + u32 connector_type_target; + u32 connection_type_target; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0005, -1, &result, sizeof(result)); + if (token < 0) { + len += i2o_report_query_status(buf+len, token, "0x0005 LAN Media Operation"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Connector type : %s\n", + i2o_get_connector_type(result.connector_type)); + len += sprintf(buf+len, "Connection type : %s\n", + i2o_get_connection_type(result.connection_type)); + + len += sprintf(buf+len, "Current Tx wire speed : %d bps\n", (int)result.current_tx_wire_speed); + len += sprintf(buf+len, "Current Rx wire speed : %d bps\n", (int)result.current_rx_wire_speed); + len += sprintf(buf+len, "Duplex mode : %s duplex\n", + (result.duplex_mode)?"Full":"Half"); + + len += sprintf(buf+len, "Link status : "); + switch (result.link_status) + { + case 0x00: + len += sprintf(buf+len, "Unknown\n"); + break; + case 0x01: + len += sprintf(buf+len, "Normal\n"); + break; + case 0x02: + len += sprintf(buf+len, "Failure\n"); + break; + case 0x03: + len += sprintf(buf+len, "Reset\n"); + break; + default: + len += sprintf(buf+len, "Unspecified\n"); + } + + len += sprintf(buf+len, "Duplex mode target : "); + switch (result.duplex_mode_target){ + case 0: + len += sprintf(buf+len, "Half duplex\n"); + break; + case 1: + len += sprintf(buf+len, "Full duplex\n"); + break; + default: + len += sprintf(buf+len, "\n"); + } + + len += sprintf(buf+len, "Connector type target : %s\n", + i2o_get_connector_type(result.connector_type_target)); + len += sprintf(buf+len, "Connection type target : %s\n", + i2o_get_connection_type(result.connection_type_target)); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0006h - Alternate address (table) (optional) */ +int i2o_proc_read_lan_alt_addr(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + int i; + u8 alt_addr[8]; + struct + { + u16 result_count; + u16 pad; + u16 block_size; + u8 block_status; + u8 error_info_size; + u16 row_count; + u16 more_flag; + u8 alt_addr[256][8]; + } result; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_table(I2O_PARAMS_TABLE_GET, + d->controller, d->lct_data.tid, + 0x0006, -1, NULL, 0, &result, sizeof(result)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token, "0x0006 LAN Alternate Address (optional)"); + spin_unlock(&i2o_proc_lock); + return len; + } + + for (i=0; i < result.row_count; i++) + { + memcpy(alt_addr,result.alt_addr[i],8); + len += sprintf(buf+len, "Alternate address[%d]: " + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + i, alt_addr[0], alt_addr[1], alt_addr[2], + alt_addr[3], alt_addr[4], alt_addr[5], + alt_addr[6], alt_addr[7]); + } + + spin_unlock(&i2o_proc_lock); + return len; +} + + +/* LAN group 0007h - Transmit info (scalar) */ +int i2o_proc_read_lan_tx_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[8]; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0007, -1, &work32, 8*4); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0007 LAN Transmit Info"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "Tx Max SG elements per packet : %d\n", work32[0]); + len += sprintf(buf+len, "Tx Max SG elements per chain : %d\n", work32[1]); + len += sprintf(buf+len, "Tx Max outstanding packets : %d\n", work32[2]); + len += sprintf(buf+len, "Tx Max packets per request : %d\n", work32[3]); + + len += sprintf(buf+len, "Tx modes : 0x%08x\n", work32[4]); + len += sprintf(buf+len, " [%s] No DA in SGL\n", + (work32[4]&0x00000002) ? "+" : "-"); + len += sprintf(buf+len, " [%s] CRC suppression\n", + (work32[4]&0x00000004) ? "+" : "-"); + len += sprintf(buf+len, " [%s] MAC insertion\n", + (work32[4]&0x00000010) ? "+" : "-"); + len += sprintf(buf+len, " [%s] RIF insertion\n", + (work32[4]&0x00000020) ? "+" : "-"); + len += sprintf(buf+len, " [%s] IPv4 checksum generation\n", + (work32[4]&0x00000100) ? "+" : "-"); + len += sprintf(buf+len, " [%s] TCP checksum generation\n", + (work32[4]&0x00000200) ? "+" : "-"); + len += sprintf(buf+len, " [%s] UDP checksum generation\n", + (work32[4]&0x00000400) ? "+" : "-"); + len += sprintf(buf+len, " [%s] RSVP checksum generation\n", + (work32[4]&0x00000800) ? "+" : "-"); + len += sprintf(buf+len, " [%s] ICMP checksum generation\n", + (work32[4]&0x00001000) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Loopback enabled\n", + (work32[4]&0x00010000) ? "+" : "-"); + len += sprintf(buf+len, " [%s] Loopback suppression enabled\n", + (work32[4]&0x00020000) ? "+" : "-"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0008h - Receive info (scalar) */ +int i2o_proc_read_lan_rx_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u32 work32[8]; + int token; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0008, -1, &work32, 8*4); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0008 LAN Receive Info"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf ,"Rx Max size of chain element : %d\n", work32[0]); + len += sprintf(buf+len, "Rx Max Buckets : %d\n", work32[1]); + len += sprintf(buf+len, "Rx Max Buckets in Reply : %d\n", work32[3]); + len += sprintf(buf+len, "Rx Max Packets in Bucket : %d\n", work32[4]); + len += sprintf(buf+len, "Rx Max Buckets in Post : %d\n", work32[5]); + + len += sprintf(buf+len, "Rx Modes : 0x%08x\n", work32[2]); + len += sprintf(buf+len, " [%s] FCS reception\n", + (work32[2]&0x00000004) ? "+" : "-"); + len += sprintf(buf+len, " [%s] IPv4 checksum validation \n", + (work32[2]&0x00000100) ? "+" : "-"); + len += sprintf(buf+len, " [%s] TCP checksum validation \n", + (work32[2]&0x00000200) ? "+" : "-"); + len += sprintf(buf+len, " [%s] UDP checksum validation \n", + (work32[2]&0x00000400) ? "+" : "-"); + len += sprintf(buf+len, " [%s] RSVP checksum validation \n", + (work32[2]&0x00000800) ? "+" : "-"); + len += sprintf(buf+len, " [%s] ICMP checksum validation \n", + (work32[2]&0x00001000) ? "+" : "-"); + + spin_unlock(&i2o_proc_lock); + return len; +} + +static int i2o_report_opt_field(char *buf, char *field_name, + int field_nbr, int supp_fields, u64 *value) +{ + if (supp_fields & (1 << field_nbr)) + return sprintf(buf, "%-24s : " FMT_U64_HEX "\n", field_name, U64_VAL(value)); + else + return sprintf(buf, "%-24s : Not supported\n", field_name); +} + +/* LAN group 0100h - LAN Historical statistics (scalar) */ +/* LAN group 0180h - Supported Optional Historical Statistics (scalar) */ +/* LAN group 0182h - Optional Non Media Specific Transmit Historical Statistics (scalar) */ +/* LAN group 0183h - Optional Non Media Specific Receive Historical Statistics (scalar) */ + +int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u64 tx_packets; + u64 tx_bytes; + u64 rx_packets; + u64 rx_bytes; + u64 tx_errors; + u64 rx_errors; + u64 rx_dropped; + u64 adapter_resets; + u64 adapter_suspends; + } stats; // 0x0100 + + static u64 supp_groups[4]; // 0x0180 + + struct + { + u64 tx_retries; + u64 tx_directed_bytes; + u64 tx_directed_packets; + u64 tx_multicast_bytes; + u64 tx_multicast_packets; + u64 tx_broadcast_bytes; + u64 tx_broadcast_packets; + u64 tx_group_addr_packets; + u64 tx_short_packets; + } tx_stats; // 0x0182 + + struct + { + u64 rx_crc_errors; + u64 rx_directed_bytes; + u64 rx_directed_packets; + u64 rx_multicast_bytes; + u64 rx_multicast_packets; + u64 rx_broadcast_bytes; + u64 rx_broadcast_packets; + u64 rx_group_addr_packets; + u64 rx_short_packets; + u64 rx_long_packets; + u64 rx_runt_packets; + } rx_stats; // 0x0183 + + struct + { + u64 ipv4_generate; + u64 ipv4_validate_success; + u64 ipv4_validate_errors; + u64 tcp_generate; + u64 tcp_validate_success; + u64 tcp_validate_errors; + u64 udp_generate; + u64 udp_validate_success; + u64 udp_validate_errors; + u64 rsvp_generate; + u64 rsvp_validate_success; + u64 rsvp_validate_errors; + u64 icmp_generate; + u64 icmp_validate_success; + u64 icmp_validate_errors; + } chksum_stats; // 0x0184 + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0100, -1, &stats, sizeof(stats)); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x100 LAN Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Tx packets : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_packets)); + len += sprintf(buf+len, "Tx bytes : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_bytes)); + len += sprintf(buf+len, "Rx packets : " FMT_U64_HEX "\n", + U64_VAL(&stats.rx_packets)); + len += sprintf(buf+len, "Rx bytes : " FMT_U64_HEX "\n", + U64_VAL(&stats.rx_bytes)); + len += sprintf(buf+len, "Tx errors : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_errors)); + len += sprintf(buf+len, "Rx errors : " FMT_U64_HEX "\n", + U64_VAL(&stats.rx_errors)); + len += sprintf(buf+len, "Rx dropped : " FMT_U64_HEX "\n", + U64_VAL(&stats.rx_dropped)); + len += sprintf(buf+len, "Adapter resets : " FMT_U64_HEX "\n", + U64_VAL(&stats.adapter_resets)); + len += sprintf(buf+len, "Adapter suspends : " FMT_U64_HEX "\n", + U64_VAL(&stats.adapter_suspends)); + + /* Optional statistics follows */ + /* Get 0x0180 to see which optional groups/fields are supported */ + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0180, -1, &supp_groups, sizeof(supp_groups)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token, "0x180 LAN Supported Optional Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + if (supp_groups[1]) /* 0x0182 */ + { + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0182, -1, &tx_stats, sizeof(tx_stats)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x182 LAN Optional Tx Historical Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "==== Optional TX statistics (group 0182h)\n"); + + len += i2o_report_opt_field(buf+len, "Tx RetryCount", + 0, supp_groups[1], &tx_stats.tx_retries); + len += i2o_report_opt_field(buf+len, "Tx DirectedBytes", + 1, supp_groups[1], &tx_stats.tx_directed_bytes); + len += i2o_report_opt_field(buf+len, "Tx DirectedPackets", + 2, supp_groups[1], &tx_stats.tx_directed_packets); + len += i2o_report_opt_field(buf+len, "Tx MulticastBytes", + 3, supp_groups[1], &tx_stats.tx_multicast_bytes); + len += i2o_report_opt_field(buf+len, "Tx MulticastPackets", + 4, supp_groups[1], &tx_stats.tx_multicast_packets); + len += i2o_report_opt_field(buf+len, "Tx BroadcastBytes", + 5, supp_groups[1], &tx_stats.tx_broadcast_bytes); + len += i2o_report_opt_field(buf+len, "Tx BroadcastPackets", + 6, supp_groups[1], &tx_stats.tx_broadcast_packets); + len += i2o_report_opt_field(buf+len, "Tx TotalGroupAddrPackets", + 7, supp_groups[1], &tx_stats.tx_group_addr_packets); + len += i2o_report_opt_field(buf+len, "Tx TotalPacketsTooShort", + 8, supp_groups[1], &tx_stats.tx_short_packets); + } + + if (supp_groups[2]) /* 0x0183 */ + { + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0183, -1, &rx_stats, sizeof(rx_stats)); + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x183 LAN Optional Rx Historical Stats"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "==== Optional RX statistics (group 0183h)\n"); + + len += i2o_report_opt_field(buf+len, "Rx CRCErrorCount", + 0, supp_groups[2], &rx_stats.rx_crc_errors); + len += i2o_report_opt_field(buf+len, "Rx DirectedBytes", + 1, supp_groups[2], &rx_stats.rx_directed_bytes); + len += i2o_report_opt_field(buf+len, "Rx DirectedPackets", + 2, supp_groups[2], &rx_stats.rx_directed_packets); + len += i2o_report_opt_field(buf+len, "Rx MulticastBytes", + 3, supp_groups[2], &rx_stats.rx_multicast_bytes); + len += i2o_report_opt_field(buf+len, "Rx MulticastPackets", + 4, supp_groups[2], &rx_stats.rx_multicast_packets); + len += i2o_report_opt_field(buf+len, "Rx BroadcastBytes", + 5, supp_groups[2], &rx_stats.rx_broadcast_bytes); + len += i2o_report_opt_field(buf+len, "Rx BroadcastPackets", + 6, supp_groups[2], &rx_stats.rx_broadcast_packets); + len += i2o_report_opt_field(buf+len, "Rx TotalGroupAddrPackets", + 7, supp_groups[2], &rx_stats.rx_group_addr_packets); + len += i2o_report_opt_field(buf+len, "Rx TotalPacketsTooShort", + 8, supp_groups[2], &rx_stats.rx_short_packets); + len += i2o_report_opt_field(buf+len, "Rx TotalPacketsTooLong", + 9, supp_groups[2], &rx_stats.rx_long_packets); + len += i2o_report_opt_field(buf+len, "Rx TotalPacketsRunt", + 10, supp_groups[2], &rx_stats.rx_runt_packets); + } + + if (supp_groups[3]) /* 0x0184 */ + { + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0184, -1, &chksum_stats, sizeof(chksum_stats)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x184 LAN Optional Chksum Historical Stats"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "==== Optional CHKSUM statistics (group 0x0184)\n"); + + len += i2o_report_opt_field(buf+len, "IPv4 Generate", + 0, supp_groups[3], &chksum_stats.ipv4_generate); + len += i2o_report_opt_field(buf+len, "IPv4 ValidateSuccess", + 1, supp_groups[3], &chksum_stats.ipv4_validate_success); + len += i2o_report_opt_field(buf+len, "IPv4 ValidateError", + 2, supp_groups[3], &chksum_stats.ipv4_validate_errors); + len += i2o_report_opt_field(buf+len, "TCP Generate", + 3, supp_groups[3], &chksum_stats.tcp_generate); + len += i2o_report_opt_field(buf+len, "TCP ValidateSuccess", + 4, supp_groups[3], &chksum_stats.tcp_validate_success); + len += i2o_report_opt_field(buf+len, "TCP ValidateError", + 5, supp_groups[3], &chksum_stats.tcp_validate_errors); + len += i2o_report_opt_field(buf+len, "UDP Generate", + 6, supp_groups[3], &chksum_stats.udp_generate); + len += i2o_report_opt_field(buf+len, "UDP ValidateSuccess", + 7, supp_groups[3], &chksum_stats.udp_validate_success); + len += i2o_report_opt_field(buf+len, "UDP ValidateError", + 8, supp_groups[3], &chksum_stats.udp_validate_errors); + len += i2o_report_opt_field(buf+len, "RSVP Generate", + 9, supp_groups[3], &chksum_stats.rsvp_generate); + len += i2o_report_opt_field(buf+len, "RSVP ValidateSuccess", + 10, supp_groups[3], &chksum_stats.rsvp_validate_success); + len += i2o_report_opt_field(buf+len, "RSVP ValidateError", + 11, supp_groups[3], &chksum_stats.rsvp_validate_errors); + len += i2o_report_opt_field(buf+len, "ICMP Generate", + 12, supp_groups[3], &chksum_stats.icmp_generate); + len += i2o_report_opt_field(buf+len, "ICMP ValidateSuccess", + 13, supp_groups[3], &chksum_stats.icmp_validate_success); + len += i2o_report_opt_field(buf+len, "ICMP ValidateError", + 14, supp_groups[3], &chksum_stats.icmp_validate_errors); + } + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0200h - Required Ethernet Statistics (scalar) */ +/* LAN group 0280h - Optional Ethernet Statistics Supported (scalar) */ +/* LAN group 0281h - Optional Ethernet Historical Statistics (scalar) */ +int i2o_proc_read_lan_eth_stats(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + int token; + + struct + { + u64 rx_align_errors; + u64 tx_one_collisions; + u64 tx_multiple_collisions; + u64 tx_deferred; + u64 tx_late_collisions; + u64 tx_max_collisions; + u64 tx_carrier_lost; + u64 tx_excessive_deferrals; + } stats; + + static u64 supp_fields; + struct + { + u64 rx_overrun; + u64 tx_underrun; + u64 tx_heartbeat_failure; + } hist_stats; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0200, -1, &stats, sizeof(stats)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0200 LAN Ethernet Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "Rx alignment errors : " FMT_U64_HEX "\n", + U64_VAL(&stats.rx_align_errors)); + len += sprintf(buf+len, "Tx one collisions : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_one_collisions)); + len += sprintf(buf+len, "Tx multicollisions : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_multiple_collisions)); + len += sprintf(buf+len, "Tx deferred : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_deferred)); + len += sprintf(buf+len, "Tx late collisions : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_late_collisions)); + len += sprintf(buf+len, "Tx max collisions : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_max_collisions)); + len += sprintf(buf+len, "Tx carrier lost : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_carrier_lost)); + len += sprintf(buf+len, "Tx excessive deferrals : " FMT_U64_HEX "\n", + U64_VAL(&stats.tx_excessive_deferrals)); + + /* Optional Ethernet statistics follows */ + /* Get 0x0280 to see which optional fields are supported */ + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0280, -1, &supp_fields, sizeof(supp_fields)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0280 LAN Supported Optional Ethernet Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + if (supp_fields) /* 0x0281 */ + { + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0281, -1, &stats, sizeof(stats)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0281 LAN Optional Ethernet Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "==== Optional ETHERNET statistics (group 0x0281)\n"); + + len += i2o_report_opt_field(buf+len, "Rx Overrun", + 0, supp_fields, &hist_stats.rx_overrun); + len += i2o_report_opt_field(buf+len, "Tx Underrun", + 1, supp_fields, &hist_stats.tx_underrun); + len += i2o_report_opt_field(buf+len, "Tx HeartbeatFailure", + 2, supp_fields, &hist_stats.tx_heartbeat_failure); + } + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0300h - Required Token Ring Statistics (scalar) */ +/* LAN group 0380h, 0381h - Optional Statistics not yet defined (TODO) */ +int i2o_proc_read_lan_tr_stats(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u64 work64[13]; + int token; + + static char *ring_status[] = + { + "", + "", + "", + "", + "", + "Ring Recovery", + "Single Station", + "Counter Overflow", + "Remove Received", + "", + "Auto-Removal Error 1", + "Lobe Wire Fault", + "Transmit Beacon", + "Soft Error", + "Hard Error", + "Signal Loss" + }; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0300, -1, &work64, sizeof(work64)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0300 Token Ring Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf, "LineErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[0])); + len += sprintf(buf+len, "LostFrames : " FMT_U64_HEX "\n", + U64_VAL(&work64[1])); + len += sprintf(buf+len, "ACError : " FMT_U64_HEX "\n", + U64_VAL(&work64[2])); + len += sprintf(buf+len, "TxAbortDelimiter : " FMT_U64_HEX "\n", + U64_VAL(&work64[3])); + len += sprintf(buf+len, "BursErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[4])); + len += sprintf(buf+len, "FrameCopiedErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[5])); + len += sprintf(buf+len, "FrequencyErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[6])); + len += sprintf(buf+len, "InternalErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[7])); + len += sprintf(buf+len, "LastRingStatus : %s\n", ring_status[work64[8]]); + len += sprintf(buf+len, "TokenError : " FMT_U64_HEX "\n", + U64_VAL(&work64[9])); + len += sprintf(buf+len, "UpstreamNodeAddress : " FMT_U64_HEX "\n", + U64_VAL(&work64[10])); + len += sprintf(buf+len, "LastRingID : " FMT_U64_HEX "\n", + U64_VAL(&work64[11])); + len += sprintf(buf+len, "LastBeaconType : " FMT_U64_HEX "\n", + U64_VAL(&work64[12])); + + spin_unlock(&i2o_proc_lock); + return len; +} + +/* LAN group 0400h - Required FDDI Statistics (scalar) */ +/* LAN group 0480h, 0481h - Optional Statistics, not yet defined (TODO) */ +int i2o_proc_read_lan_fddi_stats(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct i2o_device *d = (struct i2o_device*)data; + static u64 work64[11]; + int token; + + static char *conf_state[] = + { + "Isolated", + "Local a", + "Local b", + "Local ab", + "Local s", + "Wrap a", + "Wrap b", + "Wrap ab", + "Wrap s", + "C-Wrap a", + "C-Wrap b", + "C-Wrap s", + "Through", + }; + + static char *ring_state[] = + { + "Isolated", + "Non-op", + "Rind-op", + "Detect", + "Non-op-Dup", + "Ring-op-Dup", + "Directed", + "Trace" + }; + + static char *link_state[] = + { + "Off", + "Break", + "Trace", + "Connect", + "Next", + "Signal", + "Join", + "Verify", + "Active", + "Maintenance" + }; + + spin_lock(&i2o_proc_lock); + len = 0; + + token = i2o_query_scalar(d->controller, d->lct_data.tid, + 0x0400, -1, &work64, sizeof(work64)); + + if (token < 0) { + len += i2o_report_query_status(buf+len, token,"0x0400 FDDI Required Statistics"); + spin_unlock(&i2o_proc_lock); + return len; + } + + len += sprintf(buf+len, "ConfigurationState : %s\n", conf_state[work64[0]]); + len += sprintf(buf+len, "UpstreamNode : " FMT_U64_HEX "\n", + U64_VAL(&work64[1])); + len += sprintf(buf+len, "DownStreamNode : " FMT_U64_HEX "\n", + U64_VAL(&work64[2])); + len += sprintf(buf+len, "FrameErrors : " FMT_U64_HEX "\n", + U64_VAL(&work64[3])); + len += sprintf(buf+len, "FramesLost : " FMT_U64_HEX "\n", + U64_VAL(&work64[4])); + len += sprintf(buf+len, "RingMgmtState : %s\n", ring_state[work64[5]]); + len += sprintf(buf+len, "LCTFailures : " FMT_U64_HEX "\n", + U64_VAL(&work64[6])); + len += sprintf(buf+len, "LEMRejects : " FMT_U64_HEX "\n", + U64_VAL(&work64[7])); + len += sprintf(buf+len, "LEMCount : " FMT_U64_HEX "\n", + U64_VAL(&work64[8])); + len += sprintf(buf+len, "LConnectionState : %s\n", + link_state[work64[9]]); + + spin_unlock(&i2o_proc_lock); + return len; +} + +static int i2o_proc_create_entries(void *data, i2o_proc_entry *pentry, + struct proc_dir_entry *parent) +{ + struct proc_dir_entry *ent; + + while(pentry->name != NULL) + { + ent = create_proc_entry(pentry->name, pentry->mode, parent); + if(!ent) return -1; + + ent->data = data; + ent->read_proc = pentry->read_proc; + ent->write_proc = pentry->write_proc; + ent->nlink = 1; + + pentry++; + } + + return 0; +} + +static void i2o_proc_remove_entries(i2o_proc_entry *pentry, + struct proc_dir_entry *parent) +{ + while(pentry->name != NULL) + { + remove_proc_entry(pentry->name, parent); + pentry++; + } +} + +static int i2o_proc_add_controller(struct i2o_controller *pctrl, + struct proc_dir_entry *root ) +{ + struct proc_dir_entry *dir, *dir1; + struct i2o_device *dev; + char buff[10]; + + sprintf(buff, "iop%d", pctrl->unit); + + dir = proc_mkdir(buff, root); + if(!dir) + return -1; + + pctrl->proc_entry = dir; + + i2o_proc_create_entries(pctrl, generic_iop_entries, dir); + + for(dev = pctrl->devices; dev; dev = dev->next) + { + sprintf(buff, "%0#5x", dev->lct_data.tid); + + dir1 = proc_mkdir(buff, dir); + dev->proc_entry = dir1; + + if(!dir1) + printk(KERN_INFO "i2o_proc: Could not allocate proc dir\n"); + + i2o_proc_add_device(dev, dir1); + } + + return 0; +} + +void i2o_proc_new_dev(struct i2o_controller *c, struct i2o_device *d) +{ + char buff[10]; + +#ifdef DRIVERDEBUG + printk(KERN_INFO "Adding new device to /proc/i2o/iop%d\n", c->unit); +#endif + sprintf(buff, "%0#5x", d->lct_data.tid); + + d->proc_entry = proc_mkdir(buff, c->proc_entry); + + if(!d->proc_entry) + { + printk(KERN_WARNING "i2o: Could not allocate procdir!\n"); + return; + } + + i2o_proc_add_device(d, d->proc_entry); +} + +void i2o_proc_add_device(struct i2o_device *dev, struct proc_dir_entry *dir) +{ + i2o_proc_create_entries(dev, generic_dev_entries, dir); + + /* Inform core that we want updates about this device's status */ + i2o_device_notify_on(dev, &i2o_proc_handler); + switch(dev->lct_data.class_id) + { + case I2O_CLASS_SCSI_PERIPHERAL: + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + i2o_proc_create_entries(dev, rbs_dev_entries, dir); + break; + case I2O_CLASS_LAN: + i2o_proc_create_entries(dev, lan_entries, dir); + switch(dev->lct_data.sub_class) + { + case I2O_LAN_ETHERNET: + i2o_proc_create_entries(dev, lan_eth_entries, dir); + break; + case I2O_LAN_FDDI: + i2o_proc_create_entries(dev, lan_fddi_entries, dir); + break; + case I2O_LAN_TR: + i2o_proc_create_entries(dev, lan_tr_entries, dir); + break; + default: + break; + } + break; + default: + break; + } +} + +static void i2o_proc_remove_controller(struct i2o_controller *pctrl, + struct proc_dir_entry *parent) +{ + char buff[10]; + struct i2o_device *dev; + + /* Remove unused device entries */ + for(dev=pctrl->devices; dev; dev=dev->next) + i2o_proc_remove_device(dev); + + if(!atomic_read(&pctrl->proc_entry->count)) + { + sprintf(buff, "iop%d", pctrl->unit); + + i2o_proc_remove_entries(generic_iop_entries, pctrl->proc_entry); + + remove_proc_entry(buff, parent); + pctrl->proc_entry = NULL; + } +} + +void i2o_proc_remove_device(struct i2o_device *dev) +{ + struct proc_dir_entry *de=dev->proc_entry; + char dev_id[10]; + + sprintf(dev_id, "%0#5x", dev->lct_data.tid); + + i2o_device_notify_off(dev, &i2o_proc_handler); + /* Would it be safe to remove _files_ even if they are in use? */ + if((de) && (!atomic_read(&de->count))) + { + i2o_proc_remove_entries(generic_dev_entries, de); + switch(dev->lct_data.class_id) + { + case I2O_CLASS_SCSI_PERIPHERAL: + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + i2o_proc_remove_entries(rbs_dev_entries, de); + break; + case I2O_CLASS_LAN: + { + i2o_proc_remove_entries(lan_entries, de); + switch(dev->lct_data.sub_class) + { + case I2O_LAN_ETHERNET: + i2o_proc_remove_entries(lan_eth_entries, de); + break; + case I2O_LAN_FDDI: + i2o_proc_remove_entries(lan_fddi_entries, de); + break; + case I2O_LAN_TR: + i2o_proc_remove_entries(lan_tr_entries, de); + break; + } + } + remove_proc_entry(dev_id, dev->controller->proc_entry); + } + } +} + +void i2o_proc_dev_del(struct i2o_controller *c, struct i2o_device *d) +{ +#ifdef DRIVERDEBUG + printk(KERN_INFO, "Deleting device %d from iop%d\n", + d->lct_data.tid, c->unit); +#endif + + i2o_proc_remove_device(d); +} + +static int create_i2o_procfs(void) +{ + struct i2o_controller *pctrl = NULL; + int i; + + i2o_proc_dir_root = proc_mkdir("i2o", 0); + if(!i2o_proc_dir_root) + return -1; + + for(i = 0; i < MAX_I2O_CONTROLLERS; i++) + { + pctrl = i2o_find_controller(i); + if(pctrl) + { + i2o_proc_add_controller(pctrl, i2o_proc_dir_root); + i2o_unlock_controller(pctrl); + } + }; + + return 0; +} + +static int __exit destroy_i2o_procfs(void) +{ + struct i2o_controller *pctrl = NULL; + int i; + + for(i = 0; i < MAX_I2O_CONTROLLERS; i++) + { + pctrl = i2o_find_controller(i); + if(pctrl) + { + i2o_proc_remove_controller(pctrl, i2o_proc_dir_root); + i2o_unlock_controller(pctrl); + } + } + + if(!atomic_read(&i2o_proc_dir_root->count)) + remove_proc_entry("i2o", 0); + else + return -1; + + return 0; +} + +int __init i2o_proc_init(void) +{ + if (i2o_install_handler(&i2o_proc_handler) < 0) + { + printk(KERN_ERR "i2o_proc: Unable to install PROC handler.\n"); + return 0; + } + + if(create_i2o_procfs()) + return -EBUSY; + + return 0; +} + +MODULE_AUTHOR("Deepak Saxena"); +MODULE_DESCRIPTION("I2O procfs Handler"); + +static void __exit i2o_proc_exit(void) +{ + destroy_i2o_procfs(); + i2o_remove_handler(&i2o_proc_handler); +} + +#ifdef MODULE +module_init(i2o_proc_init); +#endif +module_exit(i2o_proc_exit); + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_scsi.c linux.ac/drivers/message/i2o/i2o_scsi.c --- linux.vanilla/drivers/message/i2o/i2o_scsi.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_scsi.c Fri May 11 18:49:53 2001 @@ -0,0 +1,912 @@ +/* + * 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, 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. + * + * Complications for I2O scsi + * + * o Each (bus,lun) is a logical device in I2O. We keep a map + * table. We spoof failed selection for unmapped units + * o Request sense buffers can come back for free. + * o Scatter gather is a bit dynamic. We have to investigate at + * setup time. + * o Some of our resources are dynamically shared. The i2o core + * needs a message reservation protocol to avoid swap v net + * deadlocking. We need to back off queue requests. + * + * In general the firmware wants to help. Where its help isn't performance + * useful we just ignore the aid. Its not worth the code in truth. + * + * Fixes: + * Steve Ralston : Scatter gather now works + * + * To Do + * 64bit cleanups + * Fix the resource management problems. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../scsi/scsi.h" +#include "../../scsi/hosts.h" +#include "../../scsi/sd.h" +#include "i2o_scsi.h" + +#define VERSION_STRING "Version 0.0.1" + +#define dprintk(x) + +#define MAXHOSTS 32 + +struct i2o_scsi_host +{ + struct i2o_controller *controller; + s16 task[16][8]; /* Allow 16 devices for now */ + unsigned long tagclock[16][8]; /* Tag clock for queueing */ + s16 bus_task; /* The adapter TID */ +}; + +static int scsi_context; +static int lun_done; +static int i2o_scsi_hosts; + +static u32 *retry[32]; +static struct i2o_controller *retry_ctrl[32]; +static struct timer_list retry_timer; +static int retry_ct = 0; + +static atomic_t queue_depth; + +/* + * SG Chain buffer support... + */ + +#define SG_MAX_FRAGS 64 + +/* + * FIXME: we should allocate one of these per bus we find as we + * locate them not in a lump at boot. + */ + +typedef struct _chain_buf +{ + u32 sg_flags_cnt[SG_MAX_FRAGS]; + u32 sg_buf[SG_MAX_FRAGS]; +} chain_buf; + +#define SG_CHAIN_BUF_SZ sizeof(chain_buf) + +#define SG_MAX_BUFS (i2o_num_controllers * I2O_SCSI_CAN_QUEUE) +#define SG_CHAIN_POOL_SZ (SG_MAX_BUFS * SG_CHAIN_BUF_SZ) + +static int max_sg_len = 0; +static chain_buf *sg_chain_pool = NULL; +static int sg_chain_tag = 0; +static int sg_max_frags = SG_MAX_FRAGS; + +/* + * Retry congested frames. This actually needs pushing down into + * i2o core. We should only bother the OSM with this when we can't + * queue and retry the frame. Or perhaps we should call the OSM + * and its default handler should be this in the core, and this + * call a 2nd "I give up" handler in the OSM ? + */ + +static void i2o_retry_run(unsigned long f) +{ + int i; + unsigned long flags; + + save_flags(flags); + cli(); + + for(i=0;i>12)&0xFFF, + m[1]&0xFFF, + m[1]>>24); + printk("Failure Code %d.\n", m[4]>>24); + if(m[4]&(1<<16)) + printk("Format error.\n"); + if(m[4]&(1<<17)) + printk("Path error.\n"); + if(m[4]&(1<<18)) + printk("Path State.\n"); + if(m[4]&(1<<18)) + printk("Congestion.\n"); + + m=(u32 *)bus_to_virt(m[7]); + printk("Failing message is %p.\n", m); + + if((m[4]&(1<<18)) && retry_ct < 32) + { + retry_ctrl[retry_ct]=c; + retry[retry_ct]=m; + if(!retry_ct++) + { + retry_timer.expires=jiffies+1; + add_timer(&retry_timer); + } + } + else + { + /* Create a scsi error for this */ + current_command = (Scsi_Cmnd *)m[3]; + printk("Aborted %ld\n", current_command->serial_number); + + spin_lock_irq(&io_request_lock); + current_command->result = DID_ERROR << 16; + current_command->scsi_done(current_command); + spin_unlock_irq(&io_request_lock); + + /* Now flush the message by making it a NOP */ + m[0]&=0x00FFFFFF; + m[0]|=(I2O_CMD_UTIL_NOP)<<24; + i2o_post_message(c,virt_to_bus(m)); + } + return; + } + + + /* + * Low byte is device status, next is adapter status, + * (then one byte reserved), then request status. + */ + ds=(u8)m[4]; + as=(u8)(m[4]>>8); + st=(u8)(m[4]>>24); + + dprintk(("i2o got a scsi reply %08X: ", m[0])); + dprintk(("m[2]=%08X: ", m[2])); + dprintk(("m[4]=%08X\n", m[4])); + + if(m[2]&0x80000000) + { + if(m[2]&0x40000000) + { + dprintk(("Event.\n")); + lun_done=1; + return; + } + printk(KERN_ERR "i2o_scsi: bus reset reply.\n"); + return; + } + + current_command = (Scsi_Cmnd *)m[3]; + + /* + * Is this a control request coming back - eg an abort ? + */ + + if(current_command==NULL) + { + if(st) + dprintk(("SCSI abort: %08X", m[4])); + dprintk(("SCSI abort completed.\n")); + return; + } + + dprintk(("Completed %ld\n", current_command->serial_number)); + + atomic_dec(&queue_depth); + + if(st == 0x06) + { + if(m[5] < current_command->underflow) + { + int i; + printk(KERN_ERR "SCSI: underflow 0x%08X 0x%08X\n", + m[5], current_command->underflow); + printk("Cmd: "); + for(i=0;i<15;i++) + printk("%02X ", current_command->cmnd[i]); + printk(".\n"); + } + else st=0; + } + + if(st) + { + /* An error has occurred */ + + dprintk((KERN_DEBUG "SCSI error %08X", m[4])); + + if (as == 0x0E) + /* SCSI Reset */ + current_command->result = DID_RESET << 16; + else if (as == 0x0F) + current_command->result = DID_PARITY << 16; + else + current_command->result = DID_ERROR << 16; + } + else + /* + * It worked maybe ? + */ + current_command->result = DID_OK << 16 | ds; + spin_lock(&io_request_lock); + current_command->scsi_done(current_command); + spin_unlock(&io_request_lock); + return; +} + +struct i2o_handler i2o_scsi_handler= +{ + i2o_scsi_reply, + NULL, + NULL, + NULL, + "I2O SCSI OSM", + 0, + I2O_CLASS_SCSI_PERIPHERAL +}; + +static int i2o_find_lun(struct i2o_controller *c, struct i2o_device *d, int *target, int *lun) +{ + u8 reply[8]; + + if(i2o_query_scalar(c, d->lct_data.tid, 0, 3, reply, 4)<0) + return -1; + + *target=reply[0]; + + if(i2o_query_scalar(c, d->lct_data.tid, 0, 4, reply, 8)<0) + return -1; + + *lun=reply[1]; + + dprintk(("SCSI (%d,%d)\n", *target, *lun)); + return 0; +} + +void i2o_scsi_init(struct i2o_controller *c, struct i2o_device *d, struct Scsi_Host *shpnt) +{ + struct i2o_device *unit; + struct i2o_scsi_host *h =(struct i2o_scsi_host *)shpnt->hostdata; + int lun; + int target; + + h->controller=c; + h->bus_task=d->lct_data.tid; + + for(target=0;target<16;target++) + for(lun=0;lun<8;lun++) + h->task[target][lun] = -1; + + for(unit=c->devices;unit!=NULL;unit=unit->next) + { + dprintk(("Class %03X, parent %d, want %d.\n", + unit->lct_data.class_id, unit->lct_data.parent_tid, d->lct_data.tid)); + + /* Only look at scsi and fc devices */ + if ( (unit->lct_data.class_id != I2O_CLASS_SCSI_PERIPHERAL) + && (unit->lct_data.class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL) + ) + continue; + + /* On our bus ? */ + dprintk(("Found a disk (%d).\n", unit->lct_data.tid)); + if ((unit->lct_data.parent_tid == d->lct_data.tid) + || (unit->lct_data.parent_tid == d->lct_data.parent_tid) + ) + { + u16 limit; + dprintk(("Its ours.\n")); + if(i2o_find_lun(c, unit, &target, &lun)==-1) + { + printk(KERN_ERR "i2o_scsi: Unable to get lun for tid %d.\n", unit->lct_data.tid); + continue; + } + dprintk(("Found disk %d %d.\n", target, lun)); + h->task[target][lun]=unit->lct_data.tid; + h->tagclock[target][lun]=jiffies; + + /* Get the max fragments/request */ + i2o_query_scalar(c, d->lct_data.tid, 0xF103, 3, &limit, 2); + + /* sanity */ + if ( limit == 0 ) + { + printk(KERN_WARNING "i2o_scsi: Ignoring unreasonable SG limit of 0 from IOP!\n"); + limit = 1; + } + + shpnt->sg_tablesize = limit; + + dprintk(("i2o_scsi: set scatter-gather to %d.\n", + shpnt->sg_tablesize)); + } + } +} + +int i2o_scsi_detect(Scsi_Host_Template * tpnt) +{ + unsigned long flags; + struct Scsi_Host *shpnt = NULL; + int i; + int count; + + printk("i2o_scsi.c: %s\n", VERSION_STRING); + + if(i2o_install_handler(&i2o_scsi_handler)<0) + { + printk(KERN_ERR "i2o_scsi: Unable to install OSM handler.\n"); + return 0; + } + scsi_context = i2o_scsi_handler.context; + + if((sg_chain_pool = kmalloc(SG_CHAIN_POOL_SZ, GFP_KERNEL)) == NULL) + { + printk("i2o_scsi: Unable to alloc %d byte SG chain buffer pool.\n", SG_CHAIN_POOL_SZ); + printk("i2o_scsi: SG chaining DISABLED!\n"); + sg_max_frags = 11; + } + else + { + printk(" chain_pool: %d bytes @ %p\n", SG_CHAIN_POOL_SZ, sg_chain_pool); + printk(" (%d byte buffers X %d can_queue X %d i2o controllers)\n", + SG_CHAIN_BUF_SZ, I2O_SCSI_CAN_QUEUE, i2o_num_controllers); + sg_max_frags = SG_MAX_FRAGS; // 64 + } + + init_timer(&retry_timer); + retry_timer.data = 0UL; + retry_timer.function = i2o_retry_run; + +// printk("SCSI OSM at %d.\n", scsi_context); + + for (count = 0, i = 0; i < MAX_I2O_CONTROLLERS; i++) + { + struct i2o_controller *c=i2o_find_controller(i); + struct i2o_device *d; + /* + * This controller doesn't exist. + */ + + if(c==NULL) + continue; + + /* + * Fixme - we need some altered device locking. This + * is racing with device addition in theory. Easy to fix. + */ + + for(d=c->devices;d!=NULL;d=d->next) + { + /* + * bus_adapter, SCSI (obsolete), or FibreChannel busses only + */ + if( (d->lct_data.class_id!=I2O_CLASS_BUS_ADAPTER_PORT) // bus_adapter +// && (d->lct_data.class_id!=I2O_CLASS_FIBRE_CHANNEL_PORT) // FC_PORT + ) + continue; + + shpnt = scsi_register(tpnt, sizeof(struct i2o_scsi_host)); + if(shpnt==NULL) + continue; + save_flags(flags); + cli(); + shpnt->unique_id = (u32)d; + shpnt->io_port = 0; + shpnt->n_io_port = 0; + shpnt->irq = 0; + shpnt->this_id = /* Good question */15; + restore_flags(flags); + i2o_scsi_init(c, d, shpnt); + count++; + } + } + i2o_scsi_hosts = count; + + if(count==0) + { + if(sg_chain_pool!=NULL) + { + kfree(sg_chain_pool); + sg_chain_pool = NULL; + } + flush_pending(); + del_timer(&retry_timer); + i2o_remove_handler(&i2o_scsi_handler); + } + + return count; +} + +int i2o_scsi_release(struct Scsi_Host *host) +{ + if(--i2o_scsi_hosts==0) + { + if(sg_chain_pool!=NULL) + { + kfree(sg_chain_pool); + sg_chain_pool = NULL; + } + flush_pending(); + del_timer(&retry_timer); + i2o_remove_handler(&i2o_scsi_handler); + } + return 0; +} + + +const char *i2o_scsi_info(struct Scsi_Host *SChost) +{ + struct i2o_scsi_host *hostdata; + + hostdata = (struct i2o_scsi_host *)SChost->hostdata; + + return(&hostdata->controller->name[0]); +} + + +/* + * From the wd93 driver: + * Returns true if there will be a DATA_OUT phase with this command, + * false otherwise. + * (Thanks to Joerg Dorchain for the research and suggestion.) + * + */ +static int is_dir_out(Scsi_Cmnd *cmd) +{ + switch (cmd->cmnd[0]) + { + case WRITE_6: case WRITE_10: case WRITE_12: + case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER: + case WRITE_VERIFY: case WRITE_VERIFY_12: + case COMPARE: case COPY: case COPY_VERIFY: + case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: + case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: + case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: + case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: + case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK: + case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: + case 0xea: + return 1; + default: + return 0; + } +} + +int i2o_scsi_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) +{ + int i; + int tid; + struct i2o_controller *c; + Scsi_Cmnd *current_command; + struct Scsi_Host *host; + struct i2o_scsi_host *hostdata; + u32 *msg, *mptr; + u32 m; + u32 *lenptr; + int direction; + int scsidir; + u32 len; + u32 reqlen; + u32 tag; + + static int max_qd = 1; + + /* + * Do the incoming paperwork + */ + + host = SCpnt->host; + hostdata = (struct i2o_scsi_host *)host->hostdata; + SCpnt->scsi_done = done; + + if(SCpnt->target > 15) + { + printk(KERN_ERR "i2o_scsi: Wild target %d.\n", SCpnt->target); + return -1; + } + + tid = hostdata->task[SCpnt->target][SCpnt->lun]; + + dprintk(("qcmd: Tid = %d\n", tid)); + + current_command = SCpnt; /* set current command */ + current_command->scsi_done = done; /* set ptr to done function */ + + /* We don't have such a device. Pretend we did the command + and that selection timed out */ + + if(tid == -1) + { + SCpnt->result = DID_NO_CONNECT << 16; + done(SCpnt); + return 0; + } + + dprintk(("Real scsi messages.\n")); + + c = hostdata->controller; + + /* + * Obtain an I2O message. Right now we _have_ to obtain one + * until the scsi layer stuff is cleaned up. + */ + + do + { + mb(); + m = I2O_POST_READ32(c); + } + while(m==0xFFFFFFFF); + msg = (u32 *)(c->mem_offset + m); + + /* + * Put together a scsi execscb message + */ + + len = SCpnt->request_bufflen; + direction = 0x00000000; // SGL IN (osm<--iop) + + /* + * The scsi layer should be handling this stuff + */ + + scsidir = 0x00000000; // DATA NO XFER + if(len) + { + if(is_dir_out(SCpnt)) + { + direction=0x04000000; // SGL OUT (osm-->iop) + scsidir =0x80000000; // DATA OUT (iop-->dev) + } + else + { + scsidir =0x40000000; // DATA IN (iop<--dev) + } + } + + __raw_writel(I2O_CMD_SCSI_EXEC<<24|HOST_TID<<12|tid, &msg[1]); + __raw_writel(scsi_context, &msg[2]); /* So the I2O layer passes to us */ + /* Sorry 64bit folks. FIXME */ + __raw_writel((u32)SCpnt, &msg[3]); /* We want the SCSI control block back */ + + /* LSI_920_PCI_QUIRK + * + * Intermittant observations of msg frame word data corruption + * observed on msg[4] after: + * WRITE, READ-MODIFY-WRITE + * operations. 19990606 -sralston + * + * (Hence we build this word via tag. Its good practice anyway + * we don't want fetches over PCI needlessly) + */ + + tag=0; + + /* + * Attach tags to the devices + */ + if(SCpnt->device->tagged_supported) + { + /* + * Some drives are too stupid to handle fairness issues + * with tagged queueing. We throw in the odd ordered + * tag to stop them starving themselves. + */ + if((jiffies - hostdata->tagclock[SCpnt->target][SCpnt->lun]) > (5*HZ)) + { + tag=0x01800000; /* ORDERED! */ + hostdata->tagclock[SCpnt->target][SCpnt->lun]=jiffies; + } + else + { + /* Hmmm... I always see value of 0 here, + * of which {HEAD_OF, ORDERED, SIMPLE} are NOT! -sralston + */ + if(SCpnt->tag == HEAD_OF_QUEUE_TAG) + tag=0x01000000; + else if(SCpnt->tag == ORDERED_QUEUE_TAG) + tag=0x01800000; + } + } + + /* Direction, disconnect ok, tag, CDBLen */ + __raw_writel(scsidir|0x20000000|SCpnt->cmd_len|tag, &msg[4]); + + mptr=msg+5; + + /* + * Write SCSI command into the message - always 16 byte block + */ + + memcpy_toio(mptr, SCpnt->cmnd, 16); + mptr+=4; + lenptr=mptr++; /* Remember me - fill in when we know */ + + reqlen = 12; // SINGLE SGE + + /* + * Now fill in the SGList and command + * + * FIXME: we need to set the sglist limits according to the + * message size of the I2O controller. We might only have room + * for 6 or so worst case + */ + + if(SCpnt->use_sg) + { + struct scatterlist *sg = (struct scatterlist *)SCpnt->request_buffer; + int chain = 0; + + len = 0; + + if((sg_max_frags > 11) && (SCpnt->use_sg > 11)) + { + chain = 1; + /* + * Need to chain! + */ + __raw_writel(direction|0xB0000000|(SCpnt->use_sg*2*4), mptr++); + __raw_writel(virt_to_bus(sg_chain_pool + sg_chain_tag), mptr); + mptr = (u32*)(sg_chain_pool + sg_chain_tag); + if (SCpnt->use_sg > max_sg_len) + { + max_sg_len = SCpnt->use_sg; + printk("i2o_scsi: Chain SG! SCpnt=%p, SG_FragCnt=%d, SG_idx=%d\n", + SCpnt, SCpnt->use_sg, sg_chain_tag); + } + if ( ++sg_chain_tag == SG_MAX_BUFS ) + sg_chain_tag = 0; + for(i = 0 ; i < SCpnt->use_sg; i++) + { + *mptr++=direction|0x10000000|sg->length; + len+=sg->length; + *mptr++=virt_to_bus(sg->address); + sg++; + } + mptr[-2]=direction|0xD0000000|(sg-1)->length; + } + else + { + for(i = 0 ; i < SCpnt->use_sg; i++) + { + __raw_writel(direction|0x10000000|sg->length, mptr++); + len+=sg->length; + __raw_writel(virt_to_bus(sg->address), mptr++); + sg++; + } + + /* Make this an end of list. Again evade the 920 bug and + unwanted PCI read traffic */ + + __raw_writel(direction|0xD0000000|(sg-1)->length, &mptr[-2]); + } + + if(!chain) + reqlen = mptr - msg; + + __raw_writel(len, lenptr); + + if(len != SCpnt->underflow) + printk("Cmd len %08X Cmd underflow %08X\n", + len, SCpnt->underflow); + } + else + { + dprintk(("non sg for %p, %d\n", SCpnt->request_buffer, + SCpnt->request_bufflen)); + __raw_writel(len = SCpnt->request_bufflen, lenptr); + if(len == 0) + { + reqlen = 9; + } + else + { + __raw_writel(0xD0000000|direction|SCpnt->request_bufflen, mptr++); + __raw_writel(virt_to_bus(SCpnt->request_buffer), mptr++); + } + } + + /* + * Stick the headers on + */ + + __raw_writel(reqlen<<16 | SGL_OFFSET_10, msg); + + /* Queue the message */ + i2o_post_message(c,m); + + atomic_inc(&queue_depth); + + if(atomic_read(&queue_depth)> max_qd) + { + max_qd=atomic_read(&queue_depth); + printk("Queue depth now %d.\n", max_qd); + } + + mb(); + dprintk(("Issued %ld\n", current_command->serial_number)); + + return 0; +} + +static void internal_done(Scsi_Cmnd * SCpnt) +{ + SCpnt->SCp.Status++; +} + +int i2o_scsi_command(Scsi_Cmnd * SCpnt) +{ + i2o_scsi_queuecommand(SCpnt, internal_done); + SCpnt->SCp.Status = 0; + while (!SCpnt->SCp.Status) + barrier(); + return SCpnt->result; +} + +int i2o_scsi_abort(Scsi_Cmnd * SCpnt) +{ + struct i2o_controller *c; + struct Scsi_Host *host; + struct i2o_scsi_host *hostdata; + u32 *msg; + u32 m; + int tid; + + printk("i2o_scsi: Aborting command block.\n"); + + host = SCpnt->host; + hostdata = (struct i2o_scsi_host *)host->hostdata; + tid = hostdata->task[SCpnt->target][SCpnt->lun]; + if(tid==-1) + { + printk(KERN_ERR "impossible command to abort.\n"); + return SCSI_ABORT_NOT_RUNNING; + } + c = hostdata->controller; + + /* + * Obtain an I2O message. Right now we _have_ to obtain one + * until the scsi layer stuff is cleaned up. + */ + + do + { + mb(); + m = I2O_POST_READ32(c); + } + while(m==0xFFFFFFFF); + msg = (u32 *)(c->mem_offset + m); + + __raw_writel(FIVE_WORD_MSG_SIZE, &msg[0]); + __raw_writel(I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|tid, &msg[1]); + __raw_writel(scsi_context, &msg[2]); + __raw_writel(0, &msg[3]); /* Not needed for an abort */ + __raw_writel((u32)SCpnt, &msg[4]); + wmb(); + i2o_post_message(c,m); + wmb(); + return SCSI_ABORT_PENDING; +} + +int i2o_scsi_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) +{ + int tid; + struct i2o_controller *c; + struct Scsi_Host *host; + struct i2o_scsi_host *hostdata; + u32 m; + u32 *msg; + + /* + * Find the TID for the bus + */ + + printk("i2o_scsi: Attempting to reset the bus.\n"); + + host = SCpnt->host; + hostdata = (struct i2o_scsi_host *)host->hostdata; + tid = hostdata->bus_task; + c = hostdata->controller; + + /* + * Now send a SCSI reset request. Any remaining commands + * will be aborted by the IOP. We need to catch the reply + * possibly ? + */ + + m = I2O_POST_READ32(c); + + /* + * No free messages, try again next time - no big deal + */ + + if(m == 0xFFFFFFFF) + return SCSI_RESET_PUNT; + + msg = (u32 *)(c->mem_offset + m); + __raw_writel(FOUR_WORD_MSG_SIZE|SGL_OFFSET_0, &msg[0]); + __raw_writel(I2O_CMD_SCSI_BUSRESET<<24|HOST_TID<<12|tid, &msg[1]); + __raw_writel(scsi_context|0x80000000, &msg[2]); + /* We use the top bit to split controller and unit transactions */ + /* Now store unit,tid so we can tie the completion back to a specific device */ + __raw_writel(c->unit << 16 | tid, &msg[3]); + wmb(); + i2o_post_message(c,m); + return SCSI_RESET_PENDING; +} + +/* + * This is anyones guess quite frankly. + */ + +int i2o_scsi_bios_param(Disk * disk, kdev_t dev, int *ip) +{ + int size; + + size = disk->capacity; + ip[0] = 64; /* heads */ + ip[1] = 32; /* sectors */ + if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */ + ip[0] = 255; /* heads */ + ip[1] = 63; /* sectors */ + ip[2] = size / (255 * 63); /* cylinders */ + } + return 0; +} + +MODULE_AUTHOR("Red Hat Software"); + +static Scsi_Host_Template driver_template = I2OSCSI; + +#include "../../scsi/scsi_module.c" diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/message/i2o/i2o_scsi.h linux.ac/drivers/message/i2o/i2o_scsi.h --- linux.vanilla/drivers/message/i2o/i2o_scsi.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/message/i2o/i2o_scsi.h Sat May 26 17:58:52 2001 @@ -0,0 +1,47 @@ +#ifndef _I2O_SCSI_H +#define _I2O_SCSI_H + +#if !defined(LINUX_VERSION_CODE) +#include +#endif + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) + +#include +#include + +#define I2O_SCSI_ID 15 +#define I2O_SCSI_CAN_QUEUE 4 +#define I2O_SCSI_CMD_PER_LUN 6 + +extern int i2o_scsi_detect(Scsi_Host_Template *); +extern const char *i2o_scsi_info(struct Scsi_Host *); +extern int i2o_scsi_command(Scsi_Cmnd *); +extern int i2o_scsi_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +extern int i2o_scsi_abort(Scsi_Cmnd *); +extern int i2o_scsi_reset(Scsi_Cmnd *, unsigned int); +extern int i2o_scsi_bios_param(Disk *, kdev_t, int *); +extern void i2o_scsi_setup(char *str, int *ints); +extern int i2o_scsi_release(struct Scsi_Host *host); + +#define I2OSCSI { \ + next: NULL, \ + proc_name: "i2o_scsi", \ + name: "I2O SCSI Layer", \ + detect: i2o_scsi_detect, \ + release: i2o_scsi_release, \ + info: i2o_scsi_info, \ + command: i2o_scsi_command, \ + queuecommand: i2o_scsi_queuecommand, \ + abort: i2o_scsi_abort, \ + reset: i2o_scsi_reset, \ + bios_param: i2o_scsi_bios_param, \ + can_queue: I2O_SCSI_CAN_QUEUE, \ + this_id: I2O_SCSI_ID, \ + sg_tablesize: 8, \ + cmd_per_lun: I2O_SCSI_CMD_PER_LUN, \ + unchecked_isa_dma: 0, \ + use_clustering: ENABLE_CLUSTERING \ + } + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/3c59x.c linux.ac/drivers/net/3c59x.c --- linux.vanilla/drivers/net/3c59x.c Mon Apr 30 15:13:16 2001 +++ linux.ac/drivers/net/3c59x.c Tue Apr 24 18:05:49 2001 @@ -92,7 +92,7 @@ - Ignore request_region() return value - already reserved if Cardbus. - Merged some additional Cardbus flags from Don's 0.99Qk - Some fixes for 3c556 (Fred Maciel) - - Fix for EISA initialisation (Jan Rkorajski) + - Fix for EISA initialisation (Jan Rekorajski) - Renamed MII_XCVR_PWR and EEPROM_230 to align with 3c575_cb and D. Becker's drivers - Fixed MII_XCVR_PWR for 3CCFE575CT - Added INVERT_LED_PWR, used it. @@ -220,7 +220,7 @@ #include static char version[] __devinitdata = -"3c59x.c:LK1.1.13 27 Jan 2001 Donald Becker and others. http://www.scyld.com/network/vortex.html\n"; +KERN_INFO "3c59x.c:LK1.1.13 27 Jan 2001 Donald Becker and others. http://www.scyld.com/network/vortex.html\n"; MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("3Com 3c59x/3c90x/3c575 series Vortex/Boomerang/Cyclone driver"); @@ -400,7 +400,7 @@ #define EISA_TBL_OFFSET 0 /* Offset of this entry for vortex_eisa_init */ {"3c590 Vortex 10Mbps", PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, }, - {"3c592 EISA 10mbps Demon/Vortex", /* AKPM: from Don's 3c59x_cb.c 0.49H */ + {"3c592 EISA 10Mbps Demon/Vortex", /* AKPM: from Don's 3c59x_cb.c 0.49H */ PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, }, {"3c597 EISA Fast Demon/Vortex", /* AKPM: from Don's 3c59x_cb.c 0.49H */ PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, }, @@ -920,7 +920,7 @@ struct vortex_chip_info * const vci = &vortex_info_tbl[chip_idx]; if (!printed_version) { - printk (KERN_INFO "%s", version); + printk (version); printk (KERN_INFO "See Documentation/networking/vortex.txt\n"); printed_version = 1; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/Config.in linux.ac/drivers/net/Config.in --- linux.vanilla/drivers/net/Config.in Sat May 26 16:53:06 2001 +++ linux.ac/drivers/net/Config.in Sat May 26 00:47:46 2001 @@ -27,9 +27,10 @@ bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET if [ "$CONFIG_NET_ETHERNET" = "y" ]; then if [ "$CONFIG_ARM" = "y" ]; then - if [ "$CONFIG_ARCH_ACORN" != "y" ]; then + if [ "$CONFIG_ARCH_EBSA110" = "y" ]; then tristate ' AM79C961A support' CONFIG_ARM_AM79C961A - else + fi + if [ "$CONFIG_ARCH_ACORN" = "y" ]; then source drivers/acorn/net/Config.in fi fi @@ -40,7 +41,6 @@ fi tristate ' BMAC (G3 ethernet) support' CONFIG_BMAC tristate ' GMAC (G4/iBook ethernet) support' CONFIG_GMAC - tristate ' Symbios 53c885 (Synergy ethernet) support' CONFIG_NCR885E tristate ' National DP83902AV (Oak ethernet) support' CONFIG_OAKNET fi if [ "$CONFIG_ZORRO" = "y" ]; then @@ -117,6 +117,7 @@ fi tristate ' HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS tristate ' HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN + tristate ' LP486E on board Ethernet' CONFIG_LP486E tristate ' ICL EtherTeam 16i/32 support' CONFIG_ETH16I tristate ' NE2000/NE1000 support' CONFIG_NE2000 if [ "$CONFIG_OBSOLETE" = "y" ]; then @@ -203,6 +204,10 @@ dep_tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN $CONFIG_PCI endmenu + +if [ "$CONFIG_PPC_ISERIES" = "y" ]; then + dep_tristate 'iSeries Virtual Ethernet driver support' CONFIG_VETH $CONFIG_PPC_ISERIES +fi bool 'FDDI driver support' CONFIG_FDDI if [ "$CONFIG_FDDI" = "y" ]; then diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/Makefile linux.ac/drivers/net/Makefile --- linux.vanilla/drivers/net/Makefile Sat May 26 16:53:06 2001 +++ linux.ac/drivers/net/Makefile Sat May 26 00:24:54 2001 @@ -58,7 +58,6 @@ obj-$(CONFIG_MACE) += mace.o obj-$(CONFIG_BMAC) += bmac.o obj-$(CONFIG_GMAC) += gmac.o -obj-$(CONFIG_NCR885E) += ncr885e.o obj-$(CONFIG_OAKNET) += oaknet.o 8390.o @@ -74,6 +73,7 @@ obj-$(CONFIG_DM9102) += dmfe.o obj-$(CONFIG_YELLOWFIN) += yellowfin.o obj-$(CONFIG_ACENIC) += acenic.o +obj-$(CONFIG_VETH) += veth.o obj-$(CONFIG_NATSEMI) += natsemi.o obj-$(CONFIG_STNIC) += stnic.o 8390.o obj-$(CONFIG_FEALNX) += fealnx.o @@ -146,8 +146,6 @@ endif obj-$(CONFIG_STRIP) += strip.o -obj-$(CONFIG_DE650) += de650.o 8390.o -obj-$(CONFIG_3C589) += 3c589.o obj-$(CONFIG_DUMMY) += dummy.o obj-$(CONFIG_BONDING) += bonding.o obj-$(CONFIG_DE600) += de600.o @@ -186,6 +184,10 @@ obj-$(CONFIG_APRICOT) += 82596.o obj-$(CONFIG_MVME16x_NET) += 82596.o obj-$(CONFIG_BVME6000_NET) += 82596.o + +# This is also a 82596 and should probably be merged +obj-$(CONFIG_LP486E) += lp486e.o + obj-$(CONFIG_ETH16I) += eth16i.o obj-$(CONFIG_ARIADNE2) += ariadne2.o 8390.o obj-$(CONFIG_HPLANCE) += hplance.o 7990.o diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/acenic.c linux.ac/drivers/net/acenic.c --- linux.vanilla/drivers/net/acenic.c Mon Apr 30 15:13:16 2001 +++ linux.ac/drivers/net/acenic.c Tue May 1 08:21:44 2001 @@ -157,6 +157,9 @@ #define __devinit __init #endif +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif #ifndef SMP_CACHE_BYTES #define SMP_CACHE_BYTES L1_CACHE_BYTES @@ -260,6 +263,11 @@ #define ace_if_down(dev) {do{} while(0);} #endif +#ifndef pci_set_dma_mask +#define pci_set_dma_mask(dev, mask) dev->dma_mask = mask; +#endif + + #if (LINUX_VERSION_CODE >= 0x02031b) #define NEW_NETINIT #define ACE_PROBE_ARG void @@ -510,7 +518,7 @@ static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1}; static char version[] __initdata = - "acenic.c: v0.80 03/08/2001 Jes Sorensen, linux-acenic@SunSITE.dk\n" + "acenic.c: v0.81 04/20/2001 Jes Sorensen, linux-acenic@SunSITE.dk\n" " http://home.cern.ch/~jes/gige/acenic.html\n"; static struct net_device *root_dev; @@ -1036,7 +1044,7 @@ u32 tig_ver, mac1, mac2, tmp, pci_state; int board_idx, ecode = 0; short i; - unsigned char cache; + unsigned char cache_size; ap = dev->priv; regs = ap->regs; @@ -1169,13 +1177,18 @@ * Ie. having two NICs in the machine, one will have the cache * line set at boot time, the other will not. */ - pci_read_config_byte(ap->pdev, PCI_CACHE_LINE_SIZE, &cache); - if ((cache << 2) != SMP_CACHE_BYTES) { + pci_read_config_byte(ap->pdev, PCI_CACHE_LINE_SIZE, &cache_size); + cache_size <<= 2; + if (cache_size != SMP_CACHE_BYTES) { printk(KERN_INFO " PCI cache line size set incorrectly " - "(%i bytes) by BIOS/FW, correcting to %i\n", - (cache << 2), SMP_CACHE_BYTES); - pci_write_config_byte(ap->pdev, PCI_CACHE_LINE_SIZE, - SMP_CACHE_BYTES >> 2); + "(%i bytes) by BIOS/FW, ", cache_size); + if (cache_size > SMP_CACHE_BYTES) + printk("expecting %i\n", SMP_CACHE_BYTES); + else { + printk("correcting to %i\n", SMP_CACHE_BYTES); + pci_write_config_byte(ap->pdev, PCI_CACHE_LINE_SIZE, + SMP_CACHE_BYTES >> 2); + } } pci_state = readl(®s->PciState); @@ -1186,6 +1199,12 @@ ap->pci_latency); /* + * Make sure to enable the 64 bit DMA mask if we're in a 64bit slot + */ + if (!(pci_state & PCI_32BIT)) + pci_set_dma_mask(ap->pdev, (dma_addr_t)~0ULL); + + /* * Set the max DMA transfer size. Seems that for most systems * the performance is better when no MAX parameter is * set. However for systems enabling PCI write and invalidate, @@ -1210,21 +1229,10 @@ printk(KERN_INFO " Disabling PCI memory " "write and invalidate\n"); } -#ifdef __alpha__ - /* This maximizes throughput on my alpha. */ - tmp |= DMA_WRITE_MAX_128; -#endif } else if (ap->pci_command & PCI_COMMAND_INVALIDATE) { printk(KERN_INFO " PCI memory write & invalidate " "enabled by BIOS, enabling counter measures\n"); -#ifdef __alpha__ - /* All the docs sy MUST NOT. Well, I did. - * Nothing terrible happens, if we load wrong size. - * Bit w&i still works better! - */ - tmp |= DMA_WRITE_MAX_128; -#else switch(SMP_CACHE_BYTES) { case 16: tmp |= DMA_WRITE_MAX_16; @@ -1235,6 +1243,9 @@ case 64: tmp |= DMA_WRITE_MAX_64; break; + case 128: + tmp |= DMA_WRITE_MAX_128; + break; default: printk(KERN_INFO " Cache line size %i not " "supported, PCI write and invalidate " @@ -1243,7 +1254,6 @@ pci_write_config_word(ap->pdev, PCI_COMMAND, ap->pci_command); } -#endif } } @@ -1259,12 +1269,19 @@ * set will give the PCI controller proper hints about * prefetching. */ - tmp = tmp & ~DMA_READ_WRITE_MASK; + tmp &= ~DMA_READ_WRITE_MASK; tmp |= DMA_READ_MAX_64; tmp |= DMA_WRITE_MAX_64; #endif #ifdef __alpha__ + tmp &= ~DMA_READ_WRITE_MASK; tmp |= DMA_READ_MAX_128; + /* + * All the docs sy MUST NOT. Well, I did. + * Nothing terrible happens, if we load wrong size. + * Bit w&i still works better! + */ + tmp |= DMA_WRITE_MAX_128; #endif writel(tmp, ®s->PciState); @@ -2714,7 +2731,7 @@ struct ace_private *ap = dev->priv; struct ace_regs *regs = ap->regs; - if ((new_mtu < 68) || (new_mtu > ACE_JUMBO_MTU)) + if (new_mtu > ACE_JUMBO_MTU) return -EINVAL; writel(new_mtu + ETH_HLEN + 4, ®s->IfMtu); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/acenic.h linux.ac/drivers/net/acenic.h --- linux.vanilla/drivers/net/acenic.h Sat May 26 16:53:06 2001 +++ linux.ac/drivers/net/acenic.h Wed May 23 00:09:53 2001 @@ -207,8 +207,8 @@ /* * udelay() values for when clocking the eeprom */ -#define ACE_SHORT_DELAY 1 -#define ACE_LONG_DELAY 2 +#define ACE_SHORT_DELAY 2 +#define ACE_LONG_DELAY 4 /* diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/aironet4500.h linux.ac/drivers/net/aironet4500.h --- linux.vanilla/drivers/net/aironet4500.h Mon Dec 11 21:00:51 2000 +++ linux.ac/drivers/net/aironet4500.h Sat May 26 18:00:23 2001 @@ -1538,7 +1538,6 @@ extern int awc_start_xmit(struct sk_buff *, struct net_device *); extern void awc_interrupt(int irq, void *dev_id, struct pt_regs *regs); extern struct net_device_stats * awc_get_stats(struct net_device *dev); -extern int awc_rx(struct net_device *dev, struct awc_fid * rx_fid); extern void awc_set_multicast_list(struct net_device *dev); extern int awc_change_mtu(struct net_device *dev, int new_mtu); extern int awc_close(struct net_device *dev); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/aironet4500_card.c linux.ac/drivers/net/aironet4500_card.c --- linux.vanilla/drivers/net/aironet4500_card.c Mon Apr 30 15:13:16 2001 +++ linux.ac/drivers/net/aironet4500_card.c Fri Apr 20 20:50:41 2001 @@ -38,6 +38,7 @@ #include #include #include +#include #include "aironet4500.h" @@ -59,6 +60,13 @@ #include +static struct pci_device_id aironet4500_card_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_AIRONET, PCI_DEVICE_AIRONET_4800_1, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_AIRONET, PCI_DEVICE_AIRONET_4800, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_AIRONET, PCI_DEVICE_AIRONET_4500, PCI_ANY_ID, PCI_ANY_ID, }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, aironet4500_card_pci_tbl); static int reverse_probe; @@ -488,6 +496,14 @@ } +static struct isapnp_device_id id_table[] = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('A','W','L'), ISAPNP_DEVICE(1), 0 }, + {0} +}; + +MODULE_DEVICE_TABLE(isapnp, id_table); + #endif //MODULE #endif /* CONFIG_AIRONET4500_PNP */ @@ -634,7 +650,7 @@ #endif /* CONFIG_AIRONET4500_ISA */ -#ifdef CONFIG_AIRONET4500_365 +#ifdef CONFIG_AIRONET4500_I365 #define port_range 0x40 @@ -957,7 +973,7 @@ } -#endif /* CONFIG_AIRONET4500_365 */ +#endif /* CONFIG_AIRONET4500_I365 */ #ifdef MODULE int init_module(void) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/aironet4500_core.c linux.ac/drivers/net/aironet4500_core.c --- linux.vanilla/drivers/net/aironet4500_core.c Tue Feb 13 21:15:05 2001 +++ linux.ac/drivers/net/aironet4500_core.c Tue May 1 08:41:18 2001 @@ -2564,7 +2564,6 @@ #if LINUX_VERSION_CODE >= 0x20100 MODULE_PARM(awc_debug,"i"); -MODULE_PARM(rx_queue_len,"i"); MODULE_PARM(tx_rate,"i"); MODULE_PARM(channel,"i"); MODULE_PARM(tx_full_rate,"i"); @@ -2594,7 +2593,6 @@ EXPORT_SYMBOL(awc_private_init); EXPORT_SYMBOL(awc_tx_timeout); EXPORT_SYMBOL(awc_start_xmit); -//EXPORT_SYMBOL(awc_rx); EXPORT_SYMBOL(awc_interrupt); EXPORT_SYMBOL(awc_get_stats); EXPORT_SYMBOL(awc_change_mtu); @@ -2970,7 +2968,7 @@ awc_reset(dev); - udelay(10000); + mdelay(10); AWC_LOCK_COMMAND_ISSUING(priv); @@ -3066,31 +3064,7 @@ return retval; } -inline int awc_rx(struct net_device *dev, struct awc_fid * rx_fid) { - -// struct awc_private *lp = (struct awc_private *)dev->priv; - - DEBUG(3, "%s: in rx_packet \n",dev->name); - - if (!rx_fid ){ - DEBUG(3, "%s: not rx_buff in rx_packet \n",dev->name); - return -1; - }; - if ( !rx_fid->skb){ - DEBUG(3, "%s: not rx_buff->skb in rx_packet \n",dev->name); - return -1; - }; - - - rx_fid->skb->protocol = eth_type_trans(rx_fid->skb,dev); - netif_rx(rx_fid->skb); - rx_fid = NULL; - - return 0; -} - - - void awc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +void awc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = (struct net_device *)dev_id; struct awc_private *priv; @@ -3109,8 +3083,6 @@ awc_interrupt_process(dev); my_spin_unlock_irqrestore(&priv->interrupt_spinlock, flags); - - return; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ariadne.c linux.ac/drivers/net/ariadne.c --- linux.vanilla/drivers/net/ariadne.c Mon Apr 30 15:13:16 2001 +++ linux.ac/drivers/net/ariadne.c Sat Apr 14 01:23:46 2001 @@ -160,24 +160,29 @@ unsigned long board = z->resource.start; unsigned long base_addr = board+ARIADNE_LANCE; unsigned long mem_start = board+ARIADNE_RAM; + struct resource *r1, *r2; - if (!request_mem_region(base_addr, sizeof(struct Am79C960), - "Am79C960")) - continue; - if (!request_mem_region(mem_start, ARIADNE_RAM_SIZE, "RAM")) { - release_mem_region(base_addr, sizeof(struct Am79C960)); + r1 = request_mem_region(base_addr, sizeof(struct Am79C960), + "Am79C960"); + if (!r1) continue; + r2 = request_mem_region(mem_start, ARIADNE_RAM_SIZE, "RAM"); + if (!r2) { + release_resource(r1); continue; } dev = init_etherdev(NULL, sizeof(struct ariadne_private)); if (dev == NULL) { - release_mem_region(base_addr, sizeof(struct Am79C960)); - release_mem_region(mem_start, ARIADNE_RAM_SIZE); + release_resource(r1); + release_resource(r2); return -ENOMEM; } - priv = (struct ariadne_private *)dev->priv; - memset(priv, 0, sizeof(struct ariadne_private)); + SET_MODULE_OWNER(dev); + priv = dev->priv; + + r1->name = dev->name; + r2->name = dev->name; priv->dev = dev; dev->dev_addr[0] = 0x00; @@ -218,6 +223,7 @@ volatile struct Am79C960 *lance = (struct Am79C960*)dev->base_addr; u_short in; u_long version; + int i; /* Reset the LANCE */ in = lance->Reset; @@ -307,15 +313,13 @@ netif_start_queue(dev); - if (request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, SA_SHIRQ, - "Ariadne Ethernet", dev)) - return -EAGAIN; + i = request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, SA_SHIRQ, + dev->name, dev); + if (i) return i; lance->RAP = CSR0; /* PCnet-ISA Controller Status */ lance->RDP = INEA|STRT; - MOD_INC_USE_COUNT; - return 0; } @@ -388,8 +392,6 @@ lance->RDP = STOP; free_irq(IRQ_AMIGA_PORTS, dev); - - MOD_DEC_USE_COUNT; return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/arlan.c linux.ac/drivers/net/arlan.c --- linux.vanilla/drivers/net/arlan.c Tue Apr 3 17:32:10 2001 +++ linux.ac/drivers/net/arlan.c Sun Apr 22 01:53:04 2001 @@ -1127,7 +1127,7 @@ struct arlan_conf_stru *conf = ((struct arlan_private *) dev->priv)->Conf; ARLAN_DEBUG_ENTRY("arlan_change_mtu"); - if ((new_mtu < 68) || (new_mtu > 2032)) + if (new_mtu > 2032) return -EINVAL; dev->mtu = new_mtu; if (new_mtu < 256) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/bsd_comp.c linux.ac/drivers/net/bsd_comp.c --- linux.vanilla/drivers/net/bsd_comp.c Tue Feb 13 21:15:04 2001 +++ linux.ac/drivers/net/bsd_comp.c Tue Apr 3 17:54:50 2001 @@ -185,7 +185,7 @@ static int bsd_decompress (void *state, unsigned char *ibuf, int isize, unsigned char *obuf, int osize); -/* These are in ppp.c */ +/* These are in ppp_generic.c */ extern int ppp_register_compressor (struct compressor *cp); extern void ppp_unregister_compressor (struct compressor *cp); @@ -1158,7 +1158,7 @@ * Module support routines *************************************************************/ -int bsdcomp_init(void) +int __init bsdcomp_init(void) { int answer = ppp_register_compressor(&ppp_bsd_compress); if (answer == 0) @@ -1166,7 +1166,7 @@ return answer; } -void bsdcomp_cleanup(void) +void __exit bsdcomp_cleanup(void) { ppp_unregister_compressor(&ppp_bsd_compress); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/eepro.c linux.ac/drivers/net/eepro.c --- linux.vanilla/drivers/net/eepro.c Mon Apr 30 15:13:16 2001 +++ linux.ac/drivers/net/eepro.c Sat May 26 19:16:24 2001 @@ -588,7 +588,7 @@ return -ENODEV; } -static void printEEPROMInfo(short ioaddr, struct net_device *dev) +static void __init printEEPROMInfo(short ioaddr, struct net_device *dev) { unsigned short Word; int i,j; @@ -647,7 +647,7 @@ probes on the ISA bus. A good device probe avoids doing writes, and verifies that the correct device exists and functions. */ -static int eepro_probe1(struct net_device *dev, short ioaddr) +static int __init eepro_probe1(struct net_device *dev, short ioaddr) { unsigned short station_addr[6], id, counter; int i,j, irqMask; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/eepro100.c linux.ac/drivers/net/eepro100.c --- linux.vanilla/drivers/net/eepro100.c Tue Feb 13 21:15:05 2001 +++ linux.ac/drivers/net/eepro100.c Sun May 13 21:52:25 2001 @@ -27,10 +27,6 @@ PCI DMA API fixes, adding pci_dma_sync_single calls where neccesary */ -static const char *version = -"eepro100.c:v1.09j-t 9/29/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n" -"eepro100.c: $Revision: 1.36 $ 2000/11/17 Modified by Andrey V. Savochkin and others\n"; - /* A few user-configurable values that apply to all boards. First set is undocumented and spelled per Intel recommendations. */ @@ -38,12 +34,12 @@ static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */ static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */ /* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */ -static int txdmacount = 128; -static int rxdmacount /* = 0 */; +static int txdmaccount = 128; +static int rxdmaccount /* = 0 */; /* Set the copy breakpoint for the copy-only-tiny-buffer Rx method. Lower values use more memory, but are faster. */ -#if defined(__alpha__) || defined(__sparc__) +#if defined(__alpha__) || defined(__sparc__) || defined(__mips__) static int rx_copybreak = 1518; #else static int rx_copybreak = 200; @@ -96,7 +92,7 @@ #include #include #include -#include +#include #include #include #include @@ -111,6 +107,10 @@ #include #include +static char version[] __devinitdata = +KERN_INFO "eepro100.c:v1.09j-t 9/29/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n" +KERN_INFO "eepro100.c: $Revision: 1.36 $ 2000/11/17 Modified by Andrey V. Savochkin and others\n"; + MODULE_AUTHOR("Maintainer: Andrey V. Savochkin "); MODULE_DESCRIPTION("Intel i82557/i82558/i82559 PCI EtherExpressPro driver"); MODULE_PARM(debug, "i"); @@ -119,8 +119,8 @@ MODULE_PARM(congenb, "i"); MODULE_PARM(txfifo, "i"); MODULE_PARM(rxfifo, "i"); -MODULE_PARM(txdmacount, "i"); -MODULE_PARM(rxdmacount, "i"); +MODULE_PARM(txdmaccount, "i"); +MODULE_PARM(rxdmaccount, "i"); MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(multicast_filter_limit, "i"); @@ -388,7 +388,7 @@ TxUnderrun=0x1000, StatusComplete=0x8000, }; -#define CONFIG_DATA_SIZE 22 +#define CONF_DATA_SIZE 22 struct TxFD { /* Transmit frame descriptor set. */ s32 status; u32 link; /* void * */ @@ -400,7 +400,7 @@ s32 tx_buf_size0; /* Length of Tx frame. */ u32 tx_buf_addr1; /* void *, frame to be transmitted. */ s32 tx_buf_size1; /* Length of Tx frame. */ - /* the structure must have space for at least CONFIG_DATA_SIZE starting + /* the structure must have space for at least CONF_DATA_SIZE starting * from tx_desc_addr field */ }; @@ -487,12 +487,12 @@ /* The parameters for a CmdConfigure operation. There are so many options that it would be difficult to document each bit. We mostly use the default or recommended settings. */ -static const char i82557_config_cmd[CONFIG_DATA_SIZE] = { +static const char i82557_config_cmd[CONF_DATA_SIZE] = { 22, 0x08, 0, 0, 0, 0, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */ 0, 0x2E, 0, 0x60, 0, 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */ 0x3f, 0x05, }; -static const char i82558_config_cmd[CONFIG_DATA_SIZE] = { +static const char i82558_config_cmd[CONF_DATA_SIZE] = { 22, 0x08, 0, 1, 0, 0, 0x22, 0x03, 1, /* 1=Use MII 0=Use AUI */ 0, 0x2E, 0, 0x60, 0x08, 0x88, 0x68, 0, 0x40, 0xf2, 0x84, /* Disable FC */ @@ -549,7 +549,7 @@ const struct pci_device_id *ent) { unsigned long ioaddr; - int irq; + int irq, i; int acpi_idle_state = 0, pm; static int cards_found /* = 0 */; @@ -557,6 +557,17 @@ if (speedo_debug > 0 && did_version++ == 0) printk(version); + /* save power state b4 pci_enable_device overwrites it */ + pm = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pm) { + u16 pwr_command; + pci_read_config_word(pdev, pm + PCI_PM_CTRL, &pwr_command); + acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK; + } + + i = pci_enable_device(pdev); + if (i) return i; + if (!request_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1), "eepro100")) { printk (KERN_ERR "eepro100: cannot reserve I/O ports\n"); @@ -587,17 +598,6 @@ pci_resource_start(pdev, 0), irq); #endif - /* save power state b4 pci_enable_device overwrites it */ - pm = pci_find_capability(pdev, PCI_CAP_ID_PM); - if (pm) { - u16 pwr_command; - pci_read_config_word(pdev, pm + PCI_PM_CTRL, &pwr_command); - acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK; - } - - if (pci_enable_device(pdev)) - goto err_out_free_mmio_region; - pci_set_master(pdev); if (speedo_found1(pdev, ioaddr, cards_found, acpi_idle_state) == 0) @@ -1285,6 +1285,7 @@ del_timer_sync(&sp->timer); /* Reset the Tx and Rx units. */ outl(PortReset, ioaddr + SCBPort); + inl(ioaddr + SCBPort); /* We may get spurious interrupts here. But I don't think that they may do much harm. 1999/12/09 SAW */ udelay(10); @@ -1981,10 +1982,10 @@ cpu_to_le32(TX_RING_ELEM_DMA(sp, (entry + 1) % TX_RING_SIZE)); config_cmd_data = (void *)&sp->tx_ring[entry].tx_desc_addr; /* Construct a full CmdConfig frame. */ - memcpy(config_cmd_data, i82558_config_cmd, CONFIG_DATA_SIZE); + memcpy(config_cmd_data, i82558_config_cmd, CONF_DATA_SIZE); config_cmd_data[1] = (txfifo << 4) | rxfifo; - config_cmd_data[4] = rxdmacount; - config_cmd_data[5] = txdmacount + 0x80; + config_cmd_data[4] = rxdmaccount; + config_cmd_data[5] = txdmaccount + 0x80; config_cmd_data[15] |= (new_rx_mode & 2) ? 1 : 0; /* 0x80 doesn't disable FC 0x84 does. Disable Flow control since we are not ACK-ing any FC interrupts @@ -2192,7 +2193,7 @@ PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1030, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82820FW_4, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_7, PCI_ANY_ID, PCI_ANY_ID, }, { 0,} }; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/eth16i.c linux.ac/drivers/net/eth16i.c --- linux.vanilla/drivers/net/eth16i.c Mon Apr 30 15:13:16 2001 +++ linux.ac/drivers/net/eth16i.c Sat May 26 19:17:14 2001 @@ -354,21 +354,21 @@ #define RESET ID_ROM_0 /* This is the I/O address list to be probed when seeking the card */ -static unsigned int eth16i_portlist[] = { +static unsigned int eth16i_portlist[] __initdata = { 0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300, 0 }; -static unsigned int eth32i_portlist[] = { +static unsigned int eth32i_portlist[] __initdata = { 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000, 0x9000, 0xA000, 0xB000, 0xC000, 0xD000, 0xE000, 0xF000, 0 }; /* This is the Interrupt lookup table for Eth16i card */ -static unsigned int eth16i_irqmap[] = { 9, 10, 5, 15, 0 }; +static unsigned int eth16i_irqmap[] __initdata = { 9, 10, 5, 15, 0 }; #define NUM_OF_ISA_IRQS 4 /* This is the Interrupt lookup table for Eth32i card */ -static unsigned int eth32i_irqmap[] = { 3, 5, 7, 9, 10, 11, 12, 15, 0 }; +static unsigned int eth32i_irqmap[] __initdata = { 3, 5, 7, 9, 10, 11, 12, 15, 0 }; #define EISA_IRQ_REG 0xc89 #define NUM_OF_EISA_IRQS 8 @@ -434,7 +434,7 @@ static struct net_device_stats *eth16i_get_stats(struct net_device *dev); -static char cardname[] = "ICL EtherTeam 16i/32"; +static char cardname[] __initdata = "ICL EtherTeam 16i/32"; int __init eth16i_probe(struct net_device *dev) { @@ -832,7 +832,7 @@ } #endif -static int eth16i_get_irq(int ioaddr) +static int __init eth16i_get_irq(int ioaddr) { unsigned char cbyte; @@ -850,7 +850,7 @@ } } -static int eth16i_check_signature(int ioaddr) +static int __init eth16i_check_signature(int ioaddr) { int i; unsigned char creg[4] = { 0 }; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/hamachi.c linux.ac/drivers/net/hamachi.c --- linux.vanilla/drivers/net/hamachi.c Sat May 26 16:53:07 2001 +++ linux.ac/drivers/net/hamachi.c Mon May 21 00:20:52 2001 @@ -743,7 +743,7 @@ return 0; } -static int read_eeprom(long ioaddr, int location) +static int __init read_eeprom(long ioaddr, int location) { int bogus_cnt = 1000; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/hamradio/bpqether.c linux.ac/drivers/net/hamradio/bpqether.c --- linux.vanilla/drivers/net/hamradio/bpqether.c Mon Apr 30 15:13:17 2001 +++ linux.ac/drivers/net/hamradio/bpqether.c Thu May 24 23:17:20 2001 @@ -192,8 +192,8 @@ unregister_netdevice(&bpq->axdev); kfree(bpq); } - - bpq_prev = bpq; + else + bpq_prev = bpq; } restore_flags(flags); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/irda/nsc-ircc.c linux.ac/drivers/net/irda/nsc-ircc.c --- linux.vanilla/drivers/net/irda/nsc-ircc.c Sat May 26 16:53:08 2001 +++ linux.ac/drivers/net/irda/nsc-ircc.c Fri May 4 16:43:42 2001 @@ -90,7 +90,7 @@ static nsc_chip_t chips[] = { { "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0, nsc_ircc_probe_108, nsc_ircc_init_108 }, - { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf0, + { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8, nsc_ircc_probe_338, nsc_ircc_init_338 }, { NULL } }; @@ -160,7 +160,7 @@ int i = 0; /* Probe for all the NSC chipsets we know about */ - for (chip=chips; chip->name ; chip++,i++) { + for (chip=chips; chip->name ; chip++) { IRDA_DEBUG(2, __FUNCTION__"(), Probing for %s ...\n", chip->name); @@ -196,7 +196,7 @@ * we init the chip, if not we probe the values * set by the BIOS */ - if (io[i] < 2000) { + if (io[i] < 0x2000) { chip->init(chip, &info); } else chip->probe(chip, &info); @@ -602,7 +602,7 @@ outb(CFG_PNP0, cfg_base); reg = inb(cfg_base+1); - pnp = (reg >> 4) & 0x01; + pnp = (reg >> 3) & 0x01; if (pnp) { IRDA_DEBUG(2, "(), Chip is in PnP mode\n"); outb(0x46, cfg_base); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/irda/toshoboe.c linux.ac/drivers/net/irda/toshoboe.c --- linux.vanilla/drivers/net/irda/toshoboe.c Sat May 26 16:53:08 2001 +++ linux.ac/drivers/net/irda/toshoboe.c Wed May 23 23:53:57 2001 @@ -43,9 +43,6 @@ /* Define this to enable FIR and MIR support */ #define ENABLE_FAST -/* Number of ports this driver can support, you also need to edit dev_self below */ -#define NSELFS 4 - /* Size of IO window */ #define CHIP_IO_EXTENT 0x1f @@ -77,7 +74,6 @@ #include #include -static int toshoboe_pmproc (struct pm_dev *dev, pm_request_t rqst, void *data); #include @@ -92,8 +88,6 @@ static const char *driver_name = "toshoboe"; -static struct toshoboe_cb *dev_self[NSELFS + 1]; - static int max_baud = 4000000; /* Shutdown the chip and point the taskfile reg somewhere else */ @@ -644,21 +638,20 @@ return ret; } -#ifdef MODULE - MODULE_DESCRIPTION("Toshiba OBOE IrDA Device Driver"); MODULE_AUTHOR("James McKenzie "); MODULE_PARM (max_baud, "i"); MODULE_PARM_DESC(max_baus, "Maximum baud rate"); -static int -toshoboe_close (struct toshoboe_cb *self) +static void +toshoboe_remove (struct pci_dev *pci_dev) { int i; + struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev); IRDA_DEBUG (4, __FUNCTION__ "()\n"); - ASSERT (self != NULL, return -1; + ASSERT (self != NULL, return; ); if (!self->stopped) @@ -693,16 +686,12 @@ self->taskfilebuf = NULL; self->taskfile = NULL; - return (0); + return; } -#endif - - - static int -toshoboe_open (struct pci_dev *pci_dev) +toshoboe_probe (struct pci_dev *pci_dev, const struct pci_device_id *pdid) { struct toshoboe_cb *self; struct net_device *dev; @@ -713,15 +702,6 @@ IRDA_DEBUG (4, __FUNCTION__ "()\n"); - while (dev_self[i]) - i++; - - if (i == NSELFS) - { - printk (KERN_ERR "Oboe: No more instances available"); - return -ENOMEM; - } - if ((err=pci_enable_device(pci_dev))) return err; @@ -736,12 +716,10 @@ memset (self, 0, sizeof (struct toshoboe_cb)); - dev_self[i] = self; /*This needs moving if we ever get more than one chip */ - self->open = 0; self->stopped = 0; self->pdev = pci_dev; - self->base = pci_dev->resource[0].start; + self->base = pci_resource_start(pci_dev,0); self->io.sir_base = self->base; self->io.irq = pci_dev->irq; @@ -749,19 +727,15 @@ self->io.speed = 9600; /* Lock the port that we need */ - i = check_region (self->io.sir_base, self->io.sir_ext); - if (i < 0) + if (NULL==request_region (self->io.sir_base, self->io.sir_ext, driver_name)) { IRDA_DEBUG (0, __FUNCTION__ "(), can't get iobase of 0x%03x\n", self->io.sir_base); - dev_self[i] = NULL; - kfree (self); - - return -ENODEV; + err = -EBUSY; + goto freeself; } - irda_init_max_qos_capabilies (&self->qos); self->qos.baud_rate.bits = 0; @@ -804,8 +778,8 @@ if (!self->taskfilebuf) { printk (KERN_ERR "toshoboe: kmalloc for DMA failed()\n"); - kfree (self); - return -ENOMEM; + err = -ENOMEM; + goto freeregion; } @@ -839,25 +813,16 @@ if (ok != RX_SLOTS + TX_SLOTS) { printk (KERN_ERR "toshoboe: kmalloc for buffers failed()\n"); + err = -ENOMEM; + goto freebufs; + } - for (i = 0; i < TX_SLOTS; ++i) - if (self->xmit_bufs[i]) - kfree (self->xmit_bufs[i]); - for (i = 0; i < RX_SLOTS; ++i) - if (self->recv_bufs[i]) - kfree (self->recv_bufs[i]); - - kfree (self); - return -ENOMEM; - - } - - request_region (self->io.sir_base, self->io.sir_ext, driver_name); if (!(dev = dev_alloc("irda%d", &err))) { - ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); - return -ENOMEM; + ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); + err = -ENOMEM; + goto freebufs; } dev->priv = (void *) self; self->netdev = dev; @@ -875,12 +840,15 @@ rtnl_unlock(); if (err) { ERROR(__FUNCTION__ "(), register_netdev() failed!\n"); - return -1; + /* XXX there is not freeing for dev? */ + goto freebufs; } + pci_set_drvdata(pci_dev,self); - pmdev = pm_register (PM_PCI_DEV, PM_PCI_ID(pci_dev), toshoboe_pmproc); +/* pmdev = pm_register (PM_PCI_DEV, PM_PCI_ID(pci_dev), toshoboe_pmproc); if (pmdev) pmdev->data = self; + */ printk (KERN_WARNING "ToshOboe: Using "); #ifdef ONETASK @@ -891,16 +859,30 @@ printk (" tasks, version %s\n", rcsid); return (0); +freebufs: + for (i = 0; i < TX_SLOTS; ++i) + if (self->xmit_bufs[i]) + kfree (self->xmit_bufs[i]); + for (i = 0; i < RX_SLOTS; ++i) + if (self->recv_bufs[i]) + kfree (self->recv_bufs[i]); + kfree(self->taskfilebuf); +freeregion: + release_region (self->io.sir_base, self->io.sir_ext); +freeself: + kfree (self); + return err; } static void -toshoboe_gotosleep (struct toshoboe_cb *self) +toshoboe_suspend (struct pci_dev *pci_dev) { int i = 10; + struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev); printk (KERN_WARNING "ToshOboe: suspending\n"); - if (self->stopped) + if (!self || self->stopped) return; self->stopped = 1; @@ -922,10 +904,14 @@ static void -toshoboe_wakeup (struct toshoboe_cb *self) +toshoboe_resume (struct pci_dev *pci_dev) { + struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev); unsigned long flags; + if (!self) + return; + if (!self->stopped) return; @@ -949,108 +935,28 @@ netif_wake_queue(self->netdev); restore_flags (flags); printk (KERN_WARNING "ToshOboe: waking up\n"); - } -static int -toshoboe_pmproc (struct pm_dev *dev, pm_request_t rqst, void *data) -{ - struct toshoboe_cb *self = (struct toshoboe_cb *) dev->data; - if (self) { - switch (rqst) { - case PM_SUSPEND: - toshoboe_gotosleep (self); - break; - case PM_RESUME: - toshoboe_wakeup (self); - break; - } - } - return 0; -} - - -int __init toshoboe_init (void) -{ - struct pci_dev *pci_dev = NULL; - int found = 0; - - do - { - pci_dev = pci_find_device (PCI_VENDOR_ID_TOSHIBA, - PCI_DEVICE_ID_FIR701, pci_dev); - if (pci_dev) - { - printk (KERN_WARNING "ToshOboe: Found 701 chip at 0x%0lx irq %d\n", - pci_dev->resource[0].start, - pci_dev->irq); - - if (!toshoboe_open (pci_dev)) - found++; - } - - } - while (pci_dev); - - if (!found) do - { - pci_dev = pci_find_device (PCI_VENDOR_ID_TOSHIBA, - PCI_DEVICE_ID_FIR701b, pci_dev); - if (pci_dev) - { - printk (KERN_WARNING "ToshOboe: Found 701b chip at 0x%0lx irq %d\n", - pci_dev->resource[0].start, - pci_dev->irq); - - if (!toshoboe_open (pci_dev)) - found++; - } - - } - while (pci_dev); - - - - if (found) - { - return 0; - } - - return -ENODEV; -} - -#ifdef MODULE - -static void -toshoboe_cleanup (void) -{ - int i; - - IRDA_DEBUG (4, __FUNCTION__ "()\n"); - - for (i = 0; i < 4; i++) - { - if (dev_self[i]) - toshoboe_close (dev_self[i]); - } - - pm_unregister_all (toshoboe_pmproc); -} - - +static struct pci_driver toshoboe_pci_driver = { + name : "toshoboe", + id_table : toshoboe_pci_tbl, + probe : toshoboe_probe, + remove : toshoboe_remove, + suspend : toshoboe_suspend, + resume : toshoboe_resume +}; -int -init_module (void) -{ - return toshoboe_init (); +int __init +toshoboe_init (void) { + pci_module_init(&toshoboe_pci_driver); + return 0; } - void -cleanup_module (void) +toshoboe_cleanup (void) { - toshoboe_cleanup (); + pci_unregister_driver(&toshoboe_pci_driver); } - -#endif +module_init(toshoboe_init); +module_exit(toshoboe_cleanup); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/lasi_82596.c linux.ac/drivers/net/lasi_82596.c --- linux.vanilla/drivers/net/lasi_82596.c Tue Feb 13 21:15:05 2001 +++ linux.ac/drivers/net/lasi_82596.c Tue Apr 3 17:54:52 2001 @@ -1009,8 +1009,6 @@ netif_start_queue(dev); - MOD_INC_USE_COUNT; - /* Initialize the 82596 memory */ if (init_i596_mem(dev)) { res = -EAGAIN; @@ -1195,6 +1193,7 @@ DEB(DEB_PROBE,printk(version)); /* The 82596-specific entries in the device structure. */ + SET_MODULE_OWNER(dev); dev->open = i596_open; dev->stop = i596_close; dev->hard_start_xmit = i596_start_xmit; @@ -1423,8 +1422,6 @@ remove_rx_bufs(dev); release_region(dev->base_addr, 12); - - MOD_DEC_USE_COUNT; return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/lp486e.c linux.ac/drivers/net/lp486e.c --- linux.vanilla/drivers/net/lp486e.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/net/lp486e.c Sat May 26 00:34:03 2001 @@ -0,0 +1,1358 @@ +/* Intel Professional Workstation/panther ethernet driver */ +/* lp486e.c: A panther 82596 ethernet driver for linux. */ +/* + History and copyrights: + + Driver skeleton + Written 1993 by Donald Becker. + Copyright 1993 United States Government as represented by the Director, + National Security Agency. This software may only be used and + distributed according to the terms of the GNU Public License + as modified by SRC, incorporated herein by reference. + + The author may be reached as becker@super.org or + C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 + + Apricot + Written 1994 by Mark Evans. + This driver is for the Apricot 82596 bus-master interface + + Modularised 12/94 Mark Evans + + Professional Workstation + Derived from apricot.c by Ard van Breemen + || + + Credits: + Thanks to Murphy Software BV for letting me write this in their time. + Well, actually, I get payed doing this... + (Also: see http://www.murphy.nl for murphy, and my homepage ~ard for + more information on the Professional Workstation) + + Present version + aeb@cwi.nl +*/ +/* + There are currently two motherboards that I know of in the + professional workstation. The only one that I know is the + intel panther motherboard. -- ard +*/ +/* +The pws is equipped with an intel 82596. This is a very intelligent controller +which runs its own micro-code. Communication with the hostprocessor is done +through linked lists of commands and buffers in the hostprocessors memory. +A complete description of the 82596 is available from intel. Search for +a file called "29021806.pdf". It is a complete description of the chip itself. +To use it for the pws some additions are needed regarding generation of +the PORT and CA signal, and the interrupt glue needed for a pc. +I/O map: +PORT SIZE ACTION MEANING +0xCB0 2 WRITE Lower 16 bits for PORT command +0xCB2 2 WRITE Upper 16 bits for PORT command, and issue of PORT command +0xCB4 1 WRITE Generation of CA signal +0xCB8 1 WRITE Clear interrupt glue +All other communication is through memory! +*/ + +#define SLOW_DOWN_IO udelay(5); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* debug print flags */ +#define LOG_SRCDST 0x80000000 +#define LOG_STATINT 0x40000000 +#define LOG_STARTINT 0x20000000 + +#define i596_debug debug + +static int i596_debug = 0; + +static const char * const medianame[] = { + "10baseT", "AUI", + "10baseT-FD", "AUI-FD", +}; + +#define LP486E_TOTAL_SIZE 16 + +#define I596_NULL (0xffffffff) + +#define CMD_EOL 0x8000 /* The last command of the list, stop. */ +#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */ +#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */ + +#define CMD_FLEX 0x0008 /* Enable flexible memory model */ + +enum commands { + CmdNOP = 0, + CmdIASetup = 1, + CmdConfigure = 2, + CmdMulticastList = 3, + CmdTx = 4, + CmdTDR = 5, + CmdDump = 6, + CmdDiagnose = 7 +}; + +char *CUcmdnames[8] = { "NOP", "IASetup", "Configure", "MulticastList", + "Tx", "TDR", "Dump", "Diagnose" }; + +/* Status word bits */ +#define STAT_CX 0x8000 /* The CU finished executing a command + with the Interrupt bit set */ +#define STAT_FR 0x4000 /* The RU finished receiving a frame */ +#define STAT_CNA 0x2000 /* The CU left the active state */ +#define STAT_RNR 0x1000 /* The RU left the active state */ +#define STAT_ACK (STAT_CX | STAT_FR | STAT_CNA | STAT_RNR) +#define STAT_CUS 0x0700 /* Status of CU: 0: idle, 1: suspended, + 2: active, 3-7: unused */ +#define STAT_RUS 0x00f0 /* Status of RU: 0: idle, 1: suspended, + 2: no resources, 4: ready, + 10: no resources due to no more RBDs, + 12: no more RBDs, other: unused */ +#define STAT_T 0x0008 /* Bus throttle timers loaded */ +#define STAT_ZERO 0x0807 /* Always zero */ + +#if 0 +static char *CUstates[8] = { + "idle", "suspended", "active", 0, 0, 0, 0, 0 +}; +static char *RUstates[16] = { + "idle", "suspended", "no resources", 0, "ready", 0, 0, 0, + 0, 0, "no RBDs", 0, "out of RBDs", 0, 0, 0 +}; + +static void +i596_out_status(int status) { + int bad = 0; + char *s; + + printk("status %4.4x:", status); + if (status == 0xffff) + printk(" strange..\n"); + else { + if (status & STAT_CX) + printk(" CU done"); + if (status & STAT_CNA) + printk(" CU stopped"); + if (status & STAT_FR) + printk(" got a frame"); + if (status & STAT_RNR) + printk(" RU stopped"); + if (status & STAT_T) + printk(" throttled"); + if (status & STAT_ZERO) + bad = 1; + s = CUstates[(status & STAT_CUS) >> 8]; + if (!s) + bad = 1; + else + printk(" CU(%s)", s); + s = RUstates[(status & STAT_RUS) >> 4]; + if (!s) + bad = 1; + else + printk(" RU(%s)", s); + if (bad) + printk(" bad status"); + printk("\n"); + } +} +#endif + +/* Command word bits */ +#define ACK_CX 0x8000 +#define ACK_FR 0x4000 +#define ACK_CNA 0x2000 +#define ACK_RNR 0x1000 + +#define CUC_START 0x0100 +#define CUC_RESUME 0x0200 +#define CUC_SUSPEND 0x0300 +#define CUC_ABORT 0x0400 + +#define RX_START 0x0010 +#define RX_RESUME 0x0020 +#define RX_SUSPEND 0x0030 +#define RX_ABORT 0x0040 + +typedef u32 phys_addr; + +static inline phys_addr +va_to_pa(volatile void *x) { + return x ? virt_to_bus(x) : I596_NULL; +} + +static inline void * +pa_to_va(phys_addr x) { + return (x == I596_NULL) ? NULL : bus_to_virt(x); +} + +/* status bits for cmd */ +#define CMD_STAT_C 0x8000 /* CU command complete */ +#define CMD_STAT_B 0x4000 /* CU command in progress */ +#define CMD_STAT_OK 0x2000 /* CU command completed without errors */ +#define CMD_STAT_A 0x1000 /* CU command abnormally terminated */ + +struct i596_cmd { /* 8 bytes */ + unsigned short status; + unsigned short command; + phys_addr pa_next; /* va_to_pa(struct i596_cmd *next) */ +}; + +#define EOF 0x8000 +#define SIZE_MASK 0x3fff + +struct i596_tbd { + unsigned short size; + unsigned short pad; + phys_addr pa_next; /* va_to_pa(struct i596_tbd *next) */ + phys_addr pa_data; /* va_to_pa(char *data) */ + struct sk_buff *skb; +}; + +struct tx_cmd { + struct i596_cmd cmd; + phys_addr pa_tbd; /* va_to_pa(struct i596_tbd *tbd) */ + unsigned short size; + unsigned short pad; +}; + +/* status bits for rfd */ +#define RFD_STAT_C 0x8000 /* Frame reception complete */ +#define RFD_STAT_B 0x4000 /* Frame reception in progress */ +#define RFD_STAT_OK 0x2000 /* Frame received without errors */ +#define RFD_STATUS 0x1fff +#define RFD_LENGTH_ERR 0x1000 +#define RFD_CRC_ERR 0x0800 +#define RFD_ALIGN_ERR 0x0400 +#define RFD_NOBUFS_ERR 0x0200 +#define RFD_DMA_ERR 0x0100 /* DMA overrun failure to acquire system bus */ +#define RFD_SHORT_FRAME_ERR 0x0080 +#define RFD_NOEOP_ERR 0x0040 +#define RFD_TRUNC_ERR 0x0020 +#define RFD_MULTICAST 0x0002 /* 0: destination had our address + 1: destination was broadcast/multicast */ +#define RFD_COLLISION 0x0001 + +/* receive frame descriptor */ +struct i596_rfd { + unsigned short stat; + unsigned short cmd; + phys_addr pa_next; /* va_to_pa(struct i596_rfd *next) */ + phys_addr pa_rbd; /* va_to_pa(struct i596_rbd *rbd) */ + unsigned short count; + unsigned short size; + char data[1532]; +}; + +#define RBD_EL 0x8000 +#define RBD_P 0x4000 +#define RBD_SIZEMASK 0x3fff +#define RBD_EOF 0x8000 +#define RBD_F 0x4000 + +/* receive buffer descriptor */ +struct i596_rbd { + unsigned short size; + unsigned short pad; + phys_addr pa_next; /* va_to_pa(struct i596_tbd *next) */ + phys_addr pa_data; /* va_to_pa(char *data) */ + phys_addr pa_prev; /* va_to_pa(struct i596_tbd *prev) */ + + /* Driver private part */ + struct sk_buff *skb; +}; + +#define RX_RING_SIZE 64 +#define RX_SKBSIZE (ETH_FRAME_LEN+10) +#define RX_RBD_SIZE 32 + +/* System Control Block - 40 bytes */ +struct i596_scb { + u16 status; /* 0 */ + u16 command; /* 2 */ + phys_addr pa_cmd; /* 4 - va_to_pa(struct i596_cmd *cmd) */ + phys_addr pa_rfd; /* 8 - va_to_pa(struct i596_rfd *rfd) */ + u32 crc_err; /* 12 */ + u32 align_err; /* 16 */ + u32 resource_err; /* 20 */ + u32 over_err; /* 24 */ + u32 rcvdt_err; /* 28 */ + u32 short_err; /* 32 */ + u16 t_on; /* 36 */ + u16 t_off; /* 38 */ +}; + +/* Intermediate System Configuration Pointer - 8 bytes */ +struct i596_iscp { + u32 busy; /* 0 */ + phys_addr pa_scb; /* 4 - va_to_pa(struct i596_scb *scb) */ +}; + +/* System Configuration Pointer - 12 bytes */ +struct i596_scp { + u32 sysbus; /* 0 */ + u32 pad; /* 4 */ + phys_addr pa_iscp; /* 8 - va_to_pa(struct i596_iscp *iscp) */ +}; + +/* Selftest and dump results - needs 16-byte alignment */ +/* + * The size of the dump area is 304 bytes. When the dump is executed + * by the Port command an extra word will be appended to the dump area. + * The extra word is a copy of the Dump status word (containing the + * C, B, OK bits). [I find 0xa006, with a0 for C+OK and 6 for dump] + */ +struct i596_dump { + u16 dump[153]; /* (304 = 130h) + 2 bytes */ +}; + +struct i596_private { /* aligned to a 16-byte boundary */ + struct i596_scp scp; /* 0 - needs 16-byte alignment */ + struct i596_iscp iscp; /* 12 */ + struct i596_scb scb; /* 20 */ + u32 dummy; /* 60 */ + struct i596_dump dump; /* 64 - needs 16-byte alignment */ + + struct i596_cmd set_add; + char eth_addr[8]; /* directly follows set_add */ + + struct i596_cmd set_conf; + char i596_config[16]; /* directly follows set_conf */ + + struct i596_cmd tdr; + unsigned long tdr_stat; /* directly follows tdr */ + + int last_restart; + volatile struct i596_rbd *rbd_list; + volatile struct i596_rbd *rbd_tail; + volatile struct i596_rfd *rx_tail; + volatile struct i596_cmd *cmd_tail; + volatile struct i596_cmd *cmd_head; + int cmd_backlog; + unsigned long last_cmd; + struct net_device_stats stats; +}; + +static char init_setup[14] = { + 0x8E, /* length 14 bytes, prefetch on */ + 0xC8, /* default: fifo to 8, monitor off */ + 0x40, /* default: don't save bad frames (apricot.c had 0x80) */ + 0x2E, /* (default is 0x26) + No source address insertion, 8 byte preamble */ + 0x00, /* default priority and backoff */ + 0x60, /* default interframe spacing */ + 0x00, /* default slot time LSB */ + 0xf2, /* default slot time and nr of retries */ + 0x00, /* default various bits + (0: promiscuous mode, 1: broadcast disable, + 2: encoding mode, 3: transmit on no CRS, + 4: no CRC insertion, 5: CRC type, + 6: bit stuffing, 7: padding) */ + 0x00, /* default carrier sense and collision detect */ + 0x40, /* default minimum frame length */ + 0xff, /* (default is 0xff, and that is what apricot.c has; + elp486.c has 0xfb: Enable crc append in memory.) */ + 0x00, /* default: not full duplex */ + 0x7f /* (default is 0x3f) multi IA */ +}; + +static int i596_open(struct net_device *dev); +static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void i596_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int i596_close(struct net_device *dev); +static struct net_device_stats *i596_get_stats(struct net_device *dev); +static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd); +static void print_eth(char *); +static void set_multicast_list(struct net_device *dev); +static void i596_tx_timeout(struct net_device *dev); + +static int +i596_timeout(struct net_device *dev, char *msg, int ct) { + volatile struct i596_private *lp; + int boguscnt = ct; + + lp = (struct i596_private *) dev->priv; + while (lp->scb.command) { + if (--boguscnt == 0) { + printk("%s: %s timed out - stat %4.4x, cmd %4.4x\n", + dev->name, msg, + lp->scb.status, lp->scb.command); + return 1; + } + udelay(5); + } + return 0; +} + +static inline int +init_rx_bufs(struct net_device *dev, int num) { + volatile struct i596_private *lp; + struct i596_rfd *rfd; + int i; + // struct i596_rbd *rbd; + + lp = (struct i596_private *) dev->priv; + lp->scb.pa_rfd = I596_NULL; + + for (i = 0; i < num; i++) { + rfd = kmalloc(sizeof(struct i596_rfd), GFP_KERNEL); + if (rfd == NULL) + break; + + rfd->stat = 0; + rfd->pa_rbd = I596_NULL; + rfd->count = 0; + rfd->size = 1532; + if (i == 0) { + rfd->cmd = CMD_EOL; + lp->rx_tail = rfd; + } else { + rfd->cmd = 0; + } + rfd->pa_next = lp->scb.pa_rfd; + lp->scb.pa_rfd = va_to_pa(rfd); + lp->rx_tail->pa_next = lp->scb.pa_rfd; + } + +#if 0 + for (i = 0; ipad = 0; + rbd->count = 0; + rbd->skb = dev_alloc_skb(RX_SKB_SIZE); + if (!rbd->skb) { + printk("dev_alloc_skb failed"); + } + rbd->next = rfd->rbd; + if (i) { + rfd->rbd->prev = rbd; + rbd->size = RX_SKB_SIZE; + } else { + rbd->size = (RX_SKB_SIZE | RBD_EL); + lp->rbd_tail = rbd; + } + + rfd->rbd = rbd; + } else { + printk("Could not kmalloc rbd\n"); + } + } + lp->rbd_tail->next = rfd->rbd; +#endif + return (i); +} + +static inline void +remove_rx_bufs(struct net_device *dev) { + struct i596_private *lp; + struct i596_rfd *rfd; + + lp = (struct i596_private *) dev->priv; + lp->rx_tail->pa_next = I596_NULL; + + do { + rfd = pa_to_va(lp->scb.pa_rfd); + lp->scb.pa_rfd = rfd->pa_next; + kfree(rfd); + } while (rfd != lp->rx_tail); + + lp->rx_tail = 0; + +#if 0 + for (lp->rbd_list) { + } +#endif +} + +#define PORT_RESET 0x00 /* reset 82596 */ +#define PORT_SELFTEST 0x01 /* selftest */ +#define PORT_ALTSCP 0x02 /* alternate SCB address */ +#define PORT_DUMP 0x03 /* dump */ + +#define IOADDR 0xcb0 /* real constant */ +#define IRQ 10 /* default IRQ - can be changed by ECU */ + +/* The 82596 requires two 16-bit write cycles for a port command */ +static inline void +PORT(phys_addr a, unsigned int cmd) { + if (a & 0xf) + printk("lp486e.c: PORT: address not aligned\n"); + outw(((a & 0xffff) | cmd), IOADDR); + outw(((a>>16) & 0xffff), IOADDR+2); +} + +static inline void +CA(void) { + outb(0, IOADDR+4); + udelay(8); +} + +static inline void +CLEAR_INT(void) { + outb(0, IOADDR+8); +} + +#define SIZE(x) (sizeof(x)/sizeof((x)[0])) + +#if 0 +/* selftest or dump */ +static void +i596_port_do(struct net_device *dev, int portcmd, char *cmdname) { + volatile struct i596_private *lp = dev->priv; + volatile u16 *outp; + int i, m; + + memset((void *)&(lp->dump), 0, sizeof(struct i596_dump)); + outp = &(lp->dump.dump[0]); + + PORT(va_to_pa(outp), portcmd); + mdelay(30); /* random, unmotivated */ + + printk("lp486e i82596 %s result:\n", cmdname); + for (m = SIZE(lp->dump.dump); m && lp->dump.dump[m-1] == 0; m--) + ; + for (i = 0; i < m; i++) { + printk(" %04x", lp->dump.dump[i]); + if (i%8 == 7) + printk("\n"); + } + printk("\n"); +} +#endif + +static int +i596_scp_setup(struct net_device *dev) { + volatile struct i596_private *lp = dev->priv; + int boguscnt; + + /* Setup SCP, ISCP, SCB */ + /* + * sysbus bits: + * only a single byte is significant - here 0x44 + * 0x80: big endian mode (details depend on stepping) + * 0x40: 1 + * 0x20: interrupt pin is active low + * 0x10: lock function disabled + * 0x08: external triggering of bus throttle timers + * 0x06: 00: 82586 compat mode, 01: segmented mode, 10: linear mode + * 0x01: unused + */ + lp->scp.sysbus = 0x00440000; /* linear mode */ + lp->scp.pad = 0; /* must be zero */ + lp->scp.pa_iscp = va_to_pa(&(lp->iscp)); + + /* + * The CPU sets the ISCP to 1 before it gives the first CA() + */ + lp->iscp.busy = 0x0001; + lp->iscp.pa_scb = va_to_pa(&(lp->scb)); + + lp->scb.command = 0; + lp->scb.status = 0; + lp->scb.pa_cmd = I596_NULL; + /* lp->scb.pa_rfd has been initialised already */ + + lp->last_cmd = jiffies; + lp->cmd_backlog = 0; + lp->cmd_head = NULL; + + /* + * Reset the 82596. + * We need to wait 10 systemclock cycles, and + * 5 serial clock cycles. + */ + PORT(0, PORT_RESET); /* address part ignored */ + udelay(100); + + /* + * Before the CA signal is asserted, the default SCP address + * (0x00fffff4) can be changed to a 16-byte aligned value + */ + PORT(va_to_pa(&lp->scp), PORT_ALTSCP); /* change the scp address */ + + /* + * The initialization procedure begins when a + * Channel Attention signal is asserted after a reset. + */ + + CA(); + + /* + * The ISCP busy is cleared by the 82596 after the SCB address is read. + */ + boguscnt = 100; + while (lp->iscp.busy) { + if (--boguscnt == 0) { + /* No i82596 present? */ + printk("%s: i82596 initialization timed out\n", + dev->name); + return 1; + } + udelay(5); + } + /* I find here boguscnt==100, so no delay was required. */ + + return 0; +} + +static int +init_i596(struct net_device *dev) { + volatile struct i596_private *lp; + + if (i596_scp_setup(dev)) + return 1; + + lp = (struct i596_private *) dev->priv; + lp->scb.command = 0; + + memcpy ((void *)lp->i596_config, init_setup, 14); + lp->set_conf.command = CmdConfigure; + i596_add_cmd(dev, (void *)&lp->set_conf); + + memcpy ((void *)lp->eth_addr, dev->dev_addr, 6); + lp->set_add.command = CmdIASetup; + i596_add_cmd(dev, (struct i596_cmd *)&lp->set_add); + + lp->tdr.command = CmdTDR; + i596_add_cmd(dev, (struct i596_cmd *)&lp->tdr); + + if (lp->scb.command && i596_timeout(dev, "i82596 init", 200)) + return 1; + + lp->scb.command = RX_START; + CA(); + + if (lp->scb.command && i596_timeout(dev, "Receive Unit start", 100)) + return 1; + + return 0; +} + +/* Receive a single frame */ +static inline int +i596_rx_one(struct net_device *dev, volatile struct i596_private *lp, + struct i596_rfd *rfd, int *frames) { + + if (rfd->stat & RFD_STAT_OK) { + /* a good frame */ + int pkt_len = (rfd->count & 0x3fff); + struct sk_buff *skb = dev_alloc_skb(pkt_len); + + (*frames)++; + + if (rfd->cmd & CMD_EOL) + printk("Received on EOL\n"); + + if (skb == NULL) { + printk ("%s: i596_rx Memory squeeze, " + "dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + return 1; + } + + skb->dev = dev; + memcpy(skb_put(skb,pkt_len), rfd->data, pkt_len); + + skb->protocol = eth_type_trans(skb,dev); + netif_rx(skb); + lp->stats.rx_packets++; + } else { +#if 0 + printk("Frame reception error status %04x\n", + rfd->stat); +#endif + lp->stats.rx_errors++; + if (rfd->stat & RFD_COLLISION) + lp->stats.collisions++; + if (rfd->stat & RFD_SHORT_FRAME_ERR) + lp->stats.rx_length_errors++; + if (rfd->stat & RFD_DMA_ERR) + lp->stats.rx_over_errors++; + if (rfd->stat & RFD_NOBUFS_ERR) + lp->stats.rx_fifo_errors++; + if (rfd->stat & RFD_ALIGN_ERR) + lp->stats.rx_frame_errors++; + if (rfd->stat & RFD_CRC_ERR) + lp->stats.rx_crc_errors++; + if (rfd->stat & RFD_LENGTH_ERR) + lp->stats.rx_length_errors++; + } + rfd->stat = rfd->count = 0; + return 0; +} + +static int +i596_rx(struct net_device *dev) { + volatile struct i596_private *lp = (struct i596_private *) dev->priv; + struct i596_rfd *rfd; + int frames = 0; + + while (1) { + rfd = pa_to_va(lp->scb.pa_rfd); + if (!rfd) { + printk("i596_rx: NULL rfd?\n"); + return 0; + } +#if 1 + if (rfd->stat && !(rfd->stat & (RFD_STAT_C | RFD_STAT_B))) + printk("SF:%p-%04x\n", rfd, rfd->stat); +#endif + if (!(rfd->stat & RFD_STAT_C)) + break; /* next one not ready */ + if (i596_rx_one(dev, lp, rfd, &frames)) + break; /* out of memory */ + rfd->cmd = CMD_EOL; + lp->rx_tail->cmd = 0; + lp->rx_tail = rfd; + lp->scb.pa_rfd = rfd->pa_next; + } + + return frames; +} + +static void +i596_cleanup_cmd(struct net_device *dev) { + volatile struct i596_private *lp; + struct i596_cmd *cmd; + + lp = (struct i596_private *) dev->priv; + while (lp->cmd_head) { + cmd = (struct i596_cmd *)lp->cmd_head; + + lp->cmd_head = pa_to_va(lp->cmd_head->pa_next); + lp->cmd_backlog--; + + switch ((cmd->command) & 0x7) { + case CmdTx: { + struct tx_cmd *tx_cmd = (struct tx_cmd *) cmd; + struct i596_tbd * tx_cmd_tbd; + tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd); + + dev_kfree_skb_any(tx_cmd_tbd->skb); + + lp->stats.tx_errors++; + lp->stats.tx_aborted_errors++; + + cmd->pa_next = I596_NULL; + kfree((unsigned char *)tx_cmd); + netif_wake_queue(dev); + break; + } + case CmdMulticastList: { + // unsigned short count = *((unsigned short *) (ptr + 1)); + + cmd->pa_next = I596_NULL; + kfree((unsigned char *)cmd); + break; + } + default: { + cmd->pa_next = I596_NULL; + break; + } + } + } + + if (lp->scb.command && i596_timeout(dev, "i596_cleanup_cmd", 100)) + ; + + lp->scb.pa_cmd = va_to_pa(lp->cmd_head); +} + +static inline void +i596_reset(struct net_device *dev, + volatile struct i596_private *lp, int ioaddr) { + + if (lp->scb.command && i596_timeout(dev, "i596_reset", 100)) + ; + + netif_stop_queue(dev); + + lp->scb.command = CUC_ABORT | RX_ABORT; + CA(); + + /* wait for shutdown */ + if (lp->scb.command && i596_timeout(dev, "i596_reset(2)", 400)) + ; + + i596_cleanup_cmd(dev); + i596_rx(dev); + + netif_start_queue(dev); + /*dev_kfree_skb(skb, FREE_WRITE);*/ + init_i596(dev); +} + +static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd) { + volatile struct i596_private *lp = dev->priv; + int ioaddr = dev->base_addr; + unsigned long flags; + + cmd->status = 0; + cmd->command |= (CMD_EOL | CMD_INTR); + cmd->pa_next = I596_NULL; + + save_flags(flags); + cli(); + if (lp->cmd_head) { + lp->cmd_tail->pa_next = va_to_pa(cmd); + } else { + lp->cmd_head = cmd; + if (lp->scb.command && i596_timeout(dev, "i596_add_cmd", 100)) + ; + lp->scb.pa_cmd = va_to_pa(cmd); + lp->scb.command = CUC_START; + CA(); + } + lp->cmd_tail = cmd; + lp->cmd_backlog++; + + lp->cmd_head = pa_to_va(lp->scb.pa_cmd); + restore_flags(flags); + + if (lp->cmd_backlog > 16) { + int tickssofar = jiffies - lp->last_cmd; + if (tickssofar < 25) return; + + printk("%s: command unit timed out, status resetting.\n", + dev->name); + i596_reset(dev, lp, ioaddr); + } +} + +static int +i596_open(struct net_device *dev) { + int i; + + i = request_irq(dev->irq, &i596_interrupt, SA_SHIRQ, dev->name, dev); + if (i) { + printk("%s: IRQ %d not free\n", dev->name, dev->irq); + return i; + } + + if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE) + printk("%s: only able to allocate %d receive buffers\n", + dev->name, i); + + if (i < 4) { +// release buffers + free_irq(dev->irq, dev); + return -EAGAIN; + } + + netif_start_queue(dev); + + init_i596(dev); + + return 0; /* Always succeed */ +} + +static int +i596_start_xmit (struct sk_buff *skb, struct net_device *dev) { + volatile struct i596_private *lp = dev->priv; + struct tx_cmd *tx_cmd; + short length; + + /* If some higher level thinks we've missed a tx-done interrupt + we are passed NULL. n.b. dev_tint handles the cli()/sti() + itself. */ + if (skb == NULL) { + printk ("What about dev_tint\n"); + /* dev_tint(dev); */ + return 0; + } + + /* shouldn't happen */ + if (skb->len <= 0) + return 0; + + length = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; + dev->trans_start = jiffies; + + tx_cmd = (struct tx_cmd *) + kmalloc ((sizeof (struct tx_cmd) + + sizeof (struct i596_tbd)), GFP_ATOMIC); + if (tx_cmd == NULL) { + printk ("%s: i596_xmit Memory squeeze, dropping packet.\n", + dev->name); + lp->stats.tx_dropped++; + + dev_kfree_skb (skb); + } else { + struct i596_tbd *tx_cmd_tbd; + tx_cmd_tbd = (struct i596_tbd *) (tx_cmd + 1); + tx_cmd->pa_tbd = va_to_pa (tx_cmd_tbd); + tx_cmd_tbd->pa_next = I596_NULL; + + tx_cmd->cmd.command = (CMD_FLEX | CmdTx); + + tx_cmd->pad = 0; + tx_cmd->size = 0; + tx_cmd_tbd->pad = 0; + tx_cmd_tbd->size = (EOF | length); + + tx_cmd_tbd->pa_data = va_to_pa (skb->data); + tx_cmd_tbd->skb = skb; + + if (i596_debug & LOG_SRCDST) + print_eth (skb->data); + + i596_add_cmd (dev, (struct i596_cmd *) tx_cmd); + + lp->stats.tx_packets++; + } + + return 0; +} + +static void +i596_tx_timeout (struct net_device *dev) { + volatile struct i596_private *lp = dev->priv; + int ioaddr = dev->base_addr; + + /* Transmitter timeout, serious problems. */ + printk ("%s: transmit timed out, status resetting.\n", dev->name); + lp->stats.tx_errors++; + + /* Try to restart the adaptor */ + if (lp->last_restart == lp->stats.tx_packets) { + printk ("Resetting board.\n"); + + /* Shutdown and restart */ + i596_reset (dev, lp, ioaddr); + } else { + /* Issue a channel attention signal */ + printk ("Kicking board.\n"); + lp->scb.command = (CUC_START | RX_START); + CA(); + lp->last_restart = lp->stats.tx_packets; + } + netif_wake_queue(dev); +} + +static void +print_eth(char *add) { + int i; + + printk ("Dest "); + for (i = 0; i < 6; i++) + printk(" %2.2X", (unsigned char) add[i]); + printk ("\n"); + + printk ("Source"); + for (i = 0; i < 6; i++) + printk(" %2.2X", (unsigned char) add[i+6]); + printk ("\n"); + + printk ("type %2.2X%2.2X\n", + (unsigned char) add[12], (unsigned char) add[13]); +} + +int __init +lp486e_probe(struct net_device *dev) { + volatile struct i596_private *lp; + unsigned char eth_addr[6] = { 0, 0xaa, 0, 0, 0, 0 }; + unsigned char *bios; + int i, j; + int ret = -ENOMEM; + static int probed; + + if (probed) + return -ENODEV; + probed++; + + if (!request_region(IOADDR, LP486E_TOTAL_SIZE, dev->name)) { + printk(KERN_ERR "lp486e: IO address 0x%x in use\n", IOADDR); + return -EBUSY; + } + + /* + * Allocate working memory, 16-byte aligned + */ + dev->mem_start = (unsigned long) + kmalloc(sizeof(struct i596_private) + 0x0f, GFP_KERNEL); + if (!dev->mem_start) + goto err_out; + dev->priv = (void *)((dev->mem_start + 0xf) & 0xfffffff0); + lp = (struct i596_private *) dev->priv; + memset((void *)lp, 0, sizeof(struct i596_private)); + + /* + * Do we really have this thing? + */ + if (i596_scp_setup(dev)) { + ret = -ENODEV; + goto err_out_kfree; + } + + dev->base_addr = IOADDR; + dev->irq = IRQ; + + ether_setup(dev); + + /* + * How do we find the ethernet address? I don't know. + * One possibility is to look at the EISA configuration area + * [0xe8000-0xe9fff]. This contains the ethernet address + * but not at a fixed address - things depend on setup options. + * + * If we find no address, or the wrong address, use + * ifconfig eth0 hw ether a1:a2:a3:a4:a5:a6 + * with the value found in the BIOS setup. + */ + bios = bus_to_virt(0xe8000); + for (j = 0; j < 0x2000; j++) { + if (bios[j] == 0 && bios[j+1] == 0xaa && bios[j+2] == 0) { + printk("%s: maybe address at BIOS 0x%x:", + dev->name, 0xe8000+j); + for (i = 0; i < 6; i++) { + eth_addr[i] = bios[i+j]; + printk(" %2.2X", eth_addr[i]); + } + printk("\n"); + } + } + + printk("%s: lp486e 82596 at %#3lx, IRQ %d,", + dev->name, dev->base_addr, dev->irq); + for (i = 0; i < 6; i++) + printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]); + printk("\n"); + + /* The LP486E-specific entries in the device structure. */ + dev->open = &i596_open; + dev->stop = &i596_close; + dev->hard_start_xmit = &i596_start_xmit; + dev->get_stats = &i596_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->watchdog_timeo = 5*HZ; + dev->tx_timeout = i596_tx_timeout; + +#if 0 + /* selftest reports 0x320925ae - don't know what that means */ + i596_port_do(dev, PORT_SELFTEST, "selftest"); + i596_port_do(dev, PORT_DUMP, "dump"); +#endif + return 0; + +err_out_kfree: + kfree ((void *) dev->mem_start); +err_out: + release_region(IOADDR, LP486E_TOTAL_SIZE); + return ret; +} + +static void inline +i596_handle_CU_completion(struct net_device *dev, + volatile struct i596_private *lp, + unsigned short status, + unsigned short *ack_cmdp) { + volatile struct i596_cmd *cmd; + int frames_out = 0; + int commands_done = 0; + int cmd_val; + + cmd = lp->cmd_head; + + while (lp->cmd_head && (lp->cmd_head->status & CMD_STAT_C)) { + cmd = lp->cmd_head; + + lp->cmd_head = pa_to_va(lp->cmd_head->pa_next); + lp->cmd_backlog--; + + commands_done++; + cmd_val = cmd->command & 0x7; +#if 0 + printk("finished CU %s command (%d)\n", + CUcmdnames[cmd_val], cmd_val); +#endif + switch (cmd_val) { + case CmdTx: + { + struct tx_cmd *tx_cmd; + struct i596_tbd *tx_cmd_tbd; + + tx_cmd = (struct tx_cmd *) cmd; + tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd); + + frames_out++; + if (cmd->status & CMD_STAT_OK) { + if (i596_debug) + print_eth(pa_to_va(tx_cmd_tbd->pa_data)); + } else { + lp->stats.tx_errors++; + if (i596_debug) + printk("transmission failure:%04x\n", + cmd->status); + if (cmd->status & 0x0020) + lp->stats.collisions++; + if (!(cmd->status & 0x0040)) + lp->stats.tx_heartbeat_errors++; + if (cmd->status & 0x0400) + lp->stats.tx_carrier_errors++; + if (cmd->status & 0x0800) + lp->stats.collisions++; + if (cmd->status & 0x1000) + lp->stats.tx_aborted_errors++; + } + dev_kfree_skb_irq(tx_cmd_tbd->skb); + + cmd->pa_next = I596_NULL; + kfree((unsigned char *)tx_cmd); + netif_wake_queue(dev); + break; + } + + case CmdMulticastList: + cmd->pa_next = I596_NULL; + kfree((unsigned char *)cmd); + break; + + case CmdTDR: + { + unsigned long status = *((unsigned long *) (cmd + 1)); + if (status & 0x8000) { + if (i596_debug) + printk("%s: link ok.\n", dev->name); + } else { + if (status & 0x4000) + printk("%s: Transceiver problem.\n", + dev->name); + if (status & 0x2000) + printk("%s: Termination problem.\n", + dev->name); + if (status & 0x1000) + printk("%s: Short circuit.\n", + dev->name); + printk("%s: Time %ld.\n", + dev->name, status & 0x07ff); + } + } + default: + cmd->pa_next = I596_NULL; + lp->last_cmd = jiffies; + + } + } + + cmd = lp->cmd_head; + while (cmd && (cmd != lp->cmd_tail)) { + cmd->command &= 0x1fff; + cmd = pa_to_va(cmd->pa_next); + } + + if (lp->cmd_head) + *ack_cmdp |= CUC_START; + lp->scb.pa_cmd = va_to_pa(lp->cmd_head); +} + +static void +i596_interrupt (int irq, void *dev_instance, struct pt_regs *regs) { + struct net_device *dev = (struct net_device *) dev_instance; + volatile struct i596_private *lp; + unsigned short status, ack_cmd = 0; + int frames_in = 0; + + if (dev == NULL) { + printk ("i596_interrupt(): irq %d for unknown device.\n", irq); + return; + } + + lp = (struct i596_private *) dev->priv; + + /* + * The 82596 examines the command, performs the required action, + * and then clears the SCB command word. + */ + if (lp->scb.command && i596_timeout(dev, "interrupt", 40)) + ; + + /* + * The status word indicates the status of the 82596. + * It is modified only by the 82596. + * + * [So, we must not clear it. I find often status 0xffff, + * which is not one of the values allowed by the docs.] + */ + status = lp->scb.status; +#if 0 + if (i596_debug) { + printk("%s: i596 interrupt, ", dev->name); + i596_out_status(status); + } +#endif + /* Impossible, but it happens - perhaps when we get + a receive interrupt but scb.pa_rfd is I596_NULL. */ + if (status == 0xffff) { + printk("%s: i596_interrupt: got status 0xffff\n", dev->name); + goto out; + } + + ack_cmd = (status & STAT_ACK); + + if (status & (STAT_CX | STAT_CNA)) + i596_handle_CU_completion(dev, lp, status, &ack_cmd); + + if (status & (STAT_FR | STAT_RNR)) { + /* Restart the receive unit when it got inactive somehow */ + if ((status & STAT_RNR) && netif_running(dev)) + ack_cmd |= RX_START; + + if (status & STAT_FR) { + frames_in = i596_rx(dev); + if (!frames_in) + printk("receive frame reported, but no frames\n"); + } + } + + /* acknowledge the interrupt */ + /* + if ((lp->scb.pa_cmd != I596_NULL) && netif_running(dev)) + ack_cmd |= CUC_START; + */ + + if (lp->scb.command && i596_timeout(dev, "i596 interrupt", 100)) + ; + + lp->scb.command = ack_cmd; + + CLEAR_INT(); + CA(); + + out: + return; +} + +static int i596_close(struct net_device *dev) { + volatile struct i596_private *lp = dev->priv; + + netif_stop_queue(dev); + + if (i596_debug) + printk("%s: Shutting down ethercard, status was %4.4x.\n", + dev->name, lp->scb.status); + + lp->scb.command = (CUC_ABORT | RX_ABORT); + CA(); + + i596_cleanup_cmd(dev); + + if (lp->scb.command && i596_timeout(dev, "i596_close", 200)) + ; + + free_irq(dev->irq, dev); + remove_rx_bufs(dev); + + return 0; +} + +static struct net_device_stats * i596_get_stats(struct net_device *dev) { + struct i596_private *lp = dev->priv; + + return &lp->stats; +} + +/* +* Set or clear the multicast filter for this adaptor. +*/ + +static void set_multicast_list(struct net_device *dev) { + volatile struct i596_private *lp = dev->priv; + struct i596_cmd *cmd; + + if (i596_debug > 1) + printk ("%s: set multicast list %d\n", + dev->name, dev->mc_count); + + if (dev->mc_count > 0) { + struct dev_mc_list *dmi; + char *cp; + cmd = (struct i596_cmd *) + kmalloc(sizeof(struct i596_cmd)+2+dev->mc_count*6, + GFP_ATOMIC); + if (cmd == NULL) { + printk ("%s: set_multicast Memory squeeze.\n", + dev->name); + return; + } + cmd->command = CmdMulticastList; + *((unsigned short *) (cmd + 1)) = dev->mc_count * 6; + cp = ((char *)(cmd + 1))+2; + for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) { + memcpy(cp, dmi,6); + cp += 6; + } + if (i596_debug & LOG_SRCDST) + print_eth (((char *)(cmd + 1)) + 2); + i596_add_cmd(dev, cmd); + } else { + if (lp->set_conf.pa_next != I596_NULL) { + return; + } + if (dev->mc_count == 0 && + !(dev->flags & (IFF_PROMISC | IFF_ALLMULTI))) { + if (dev->flags & IFF_ALLMULTI) + dev->flags |= IFF_PROMISC; + lp->i596_config[8] &= ~0x01; + } else { + lp->i596_config[8] |= 0x01; + } + + i596_add_cmd(dev, (struct i596_cmd *) &lp->set_conf); + } +} + +MODULE_AUTHOR("Ard van Breemen "); +MODULE_DESCRIPTION("Intel Panther onboard i82596 driver"); +MODULE_PARM(debug, "i"); +//MODULE_PARM(max_interrupt_work, "i"); +//MODULE_PARM(reverse_probe, "i"); +//MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); + +static struct net_device dev_lp486e; +static int full_duplex; +static int options; +static int io = IOADDR; +static int irq = IRQ; + +static int __init lp486e_init_module(void) { + struct net_device *dev = &dev_lp486e; + dev->irq = irq; + dev->base_addr = io; + dev->init = lp486e_probe; + if (register_netdev(dev) != 0) + return -EIO; + full_duplex = 0; + options = 0; + return 0; +} + +static void __exit lp486e_cleanup_module(void) { + unregister_netdev(&dev_lp486e); + kfree((void *)dev_lp486e.mem_start); + dev_lp486e.priv = NULL; + release_region(dev_lp486e.base_addr, LP486E_TOTAL_SIZE); +} + +module_init(lp486e_init_module); +module_exit(lp486e_cleanup_module); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/macsonic.c linux.ac/drivers/net/macsonic.c --- linux.vanilla/drivers/net/macsonic.c Tue Apr 3 17:32:12 2001 +++ linux.ac/drivers/net/macsonic.c Tue Apr 3 17:54:52 2001 @@ -167,6 +167,8 @@ if ((lp->rba = (char *) kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_DMA)) == NULL) { printk(KERN_ERR "%s: couldn't allocate receive buffers\n", dev->name); + kfree(lp->sonic_desc); + lp->sonic_desc = NULL; return -ENOMEM; } @@ -321,7 +323,7 @@ /* methinks this will always be true but better safe than sorry */ if (dev->priv == NULL) { dev->priv = kmalloc(sizeof(struct sonic_local), GFP_KERNEL); - if (!dev->priv) /* FIXME: kfree dev if necessary */ + if (!dev->priv) return -ENOMEM; } } else { @@ -517,9 +519,14 @@ if (dev) { dev = init_etherdev(dev, sizeof(struct sonic_local)); + if (!dev) + return -ENOMEM; /* methinks this will always be true but better safe than sorry */ - if (dev->priv == NULL) + if (dev->priv == NULL) { dev->priv = kmalloc(sizeof(struct sonic_local), GFP_KERNEL); + if (!dev->priv) /* FIXME: kfree dev if necessary */ + return -ENOMEM; + } } else { dev = init_etherdev(NULL, sizeof(struct sonic_local)); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/natsemi.c linux.ac/drivers/net/natsemi.c --- linux.vanilla/drivers/net/natsemi.c Sat May 26 16:53:09 2001 +++ linux.ac/drivers/net/natsemi.c Mon May 21 00:20:58 2001 @@ -553,7 +553,7 @@ EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6), }; -static int eeprom_read(long addr, int location) +static int __devinit eeprom_read(long addr, int location) { int i; int retval = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ne2.c linux.ac/drivers/net/ne2.c --- linux.vanilla/drivers/net/ne2.c Fri Feb 9 19:40:02 2001 +++ linux.ac/drivers/net/ne2.c Sat May 26 19:19:37 2001 @@ -118,9 +118,9 @@ static int irqs[4] __initdata = {3, 4, 5, 9}; /* From the D-Link ADF file: */ -static unsigned int dlink_addresses[4]= +static unsigned int dlink_addresses[4] __initdata = {0x300, 0x320, 0x340, 0x360}; -static int dlink_irqs[8] = {3, 4, 5, 9, 10, 11, 14, 15}; +static int dlink_irqs[8] __initdata = {3, 4, 5, 9, 10, 11, 14, 15}; struct ne2_adapters_t { unsigned int id; @@ -165,7 +165,7 @@ * */ -static void dlink_put_eeprom(unsigned char value, unsigned int addr) +static void __init dlink_put_eeprom(unsigned char value, unsigned int addr) { int z; unsigned char v1, v2; @@ -186,7 +186,7 @@ } } -static void dlink_send_eeprom_bit(unsigned int bit, unsigned int addr) +static void __init dlink_send_eeprom_bit(unsigned int bit, unsigned int addr) { /* shift data bit into correct position */ @@ -200,7 +200,7 @@ dlink_put_eeprom(0x09 | bit, addr); } -static void dlink_send_eeprom_word(unsigned int value, unsigned int len, unsigned int addr) +static void __init dlink_send_eeprom_word(unsigned int value, unsigned int len, unsigned int addr) { int z; @@ -216,7 +216,7 @@ } } -static unsigned int dlink_get_eeprom(unsigned int eeaddr, unsigned int addr) +static unsigned int __init dlink_get_eeprom(unsigned int eeaddr, unsigned int addr) { int z; unsigned int value = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/net_init.c linux.ac/drivers/net/net_init.c --- linux.vanilla/drivers/net/net_init.c Sat May 26 16:53:09 2001 +++ linux.ac/drivers/net/net_init.c Sat May 26 00:24:54 2001 @@ -236,11 +236,23 @@ static int eth_change_mtu(struct net_device *dev, int new_mtu) { - if ((new_mtu < 68) || (new_mtu > 1500)) + if (new_mtu > 1500) return -EINVAL; dev->mtu = new_mtu; return 0; } + +#if defined(CONFIG_VETH) || defined(CONFIG_VETH_MODULE) + +struct net_device *init_vethdev(struct net_device *dev, int sizeof_priv, int veth) +{ + char name[32]; + + sprintf(name, "veth%d", veth); + return init_netdev(dev, sizeof_priv, name, ether_setup); +} + +#endif #ifdef CONFIG_FDDI diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcmcia/Config.in linux.ac/drivers/net/pcmcia/Config.in --- linux.vanilla/drivers/net/pcmcia/Config.in Sat May 26 16:53:09 2001 +++ linux.ac/drivers/net/pcmcia/Config.in Thu May 17 14:08:46 2001 @@ -20,7 +20,8 @@ fi if [ "$CONFIG_CARDBUS" = "y" ]; then - tristate ' Xircom Tulip-like CardBus support' CONFIG_PCMCIA_XIRTULIP + tristate ' Xircom CardBus support (new driver)' CONFIG_PCMCIA_XIRCOM + tristate ' Xircom Tulip-like CardBus support (old driver)' CONFIG_PCMCIA_XIRTULIP fi bool ' Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcmcia/Makefile linux.ac/drivers/net/pcmcia/Makefile --- linux.vanilla/drivers/net/pcmcia/Makefile Sat May 26 16:53:09 2001 +++ linux.ac/drivers/net/pcmcia/Makefile Sat May 26 00:28:54 2001 @@ -32,14 +32,17 @@ # Cardbus client drivers obj-$(CONFIG_PCMCIA_XIRTULIP) += xircom_tulip_cb.o +obj-$(CONFIG_PCMCIA_XIRCOM) += xircom_cb.o obj-$(CONFIG_PCMCIA_IBMTR) += ibmtr_cs.o include $(TOPDIR)/Rules.make tmp-ibmtr.o: ../tokenring/ibmtr.c - $(CC) $(CFLAGS) -D__NO_VERSION__ -DPCMCIA -c -o $@ ../tokenring/ibmtr.c + $(CC) $(CFLAGS) -D__NO_VERSION__ -DPCMCIA -c -o $@ $< -ibmtr_cs.o: tmp-ibmtr.o ibmtr_cs.c - $(CC) $(CFLAGS) -DPCMCIA -c -o tmp-$@ ibmtr_cs.c - $(LD) -r -o $@ tmp-$@ tmp-ibmtr.o +tmp-ibmtr_cs.o: ibmtr_cs.c + $(CC) $(CFLAGS) -DPCMCIA -c -o $@ $< + +ibmtr_cs.o: tmp-ibmtr.o tmp-ibmtr_cs.o + $(LD) -r -o $@ tmp-ibmtr_cs.o tmp-ibmtr.o diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcmcia/ibmtr_cs.c linux.ac/drivers/net/pcmcia/ibmtr_cs.c --- linux.vanilla/drivers/net/pcmcia/ibmtr_cs.c Mon Apr 30 15:13:17 2001 +++ linux.ac/drivers/net/pcmcia/ibmtr_cs.c Wed May 9 21:54:32 2001 @@ -25,20 +25,9 @@ All we do here is set the card up enough so that the real ibmtr.c driver can find it and work with it properly. - i.e. We set up the io port, irq, mmio memory and shared ram memory. - This enables ibmtr_probe in ibmtr.c to find the card and configure it - as though it was a normal ISA and/or PnP card. - - There is some confusion with the difference between available shared - ram and the amount actually reserved from memory. ibmtr.c sets up - several offsets depending upon the actual on-board memory, not the - reserved memory. We need to get around this to allow the cards to - work with other cards in restricted memory space. Therefore the - pcmcia_reality_check function. - - TODO - - Write the suspend / resume functions. - - Fix Kernel Oops when removing card before ifconfig down + i.e. We set up the io port, irq, mmio memory and shared ram + memory. This enables ibmtr_probe in ibmtr.c to find the card and + configure it as though it was a normal ISA and/or PnP card. CHANGES @@ -49,16 +38,20 @@ Updated to version 2.2.7 to match the first version of the kernel that the modification to ibmtr.c were incorporated into. + v2.2.17 July 2000 Burt Silverman (burts@us.ibm.com) + Address translation feature of PCMCIA controller is usable so + memory windows can be placed in High memory (meaning above + 0xFFFFF.) + ======================================================================*/ #include #include #include #include -#include +#include #include #include -#include #include #include #include @@ -78,8 +71,9 @@ MODULE_PARM(pc_debug, "i"); #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) static char *version = -"ibmtr_cs.c 1.10 1996/01/06 05:19:00 (Steve Kipisz)\n" -" 2.2.7 1999/05/03 12:00:00 (Mike Phillips)\n" ; +"ibmtr_cs.c 1.10 1996/01/06 05:19:00 (Steve Kipisz)\n" +" 2.2.7 1999/05/03 12:00:00 (Mike Phillips)\n" +" 2.4.2 2001/30/28 Midnight (Burt Silverman)\n"; #else #define DEBUG(n, args...) #endif @@ -93,13 +87,13 @@ static int irq_list[4] = { -1 }; /* MMIO base address */ -static u_long mmiobase; +static u_long mmiobase = 0xce000; /* SRAM base address */ -static u_long srambase; +static u_long srambase = 0xd0000; /* SRAM size 8,16,32,64 */ -static u_long sramsize = 16; +static u_long sramsize = 64; /* Ringspeed 4,16 */ static int ringspeed = 16; @@ -114,7 +108,7 @@ /*====================================================================*/ static void ibmtr_config(dev_link_t *link); -static void ibmtr_hw_setup(struct net_device *dev); +static void ibmtr_hw_setup(struct net_device *dev, u_int mmiobase); static void ibmtr_release(u_long arg); static int ibmtr_event(event_t event, int priority, event_callback_args_t *args); @@ -127,18 +121,14 @@ static dev_link_t *dev_list; extern int ibmtr_probe(struct net_device *dev); -unsigned char pcmcia_reality_check(unsigned char gss); - extern int trdev_init(struct net_device *dev); extern void tok_interrupt(int irq, struct pt_regs *regs); -extern int tok_init_card(struct net_device *dev); -extern unsigned char get_sram_size(struct tok_info *ti); /*====================================================================*/ typedef struct ibmtr_dev_t { dev_link_t link; - struct net_device *dev; /* Changed for 2.2.0 */ + struct net_device *dev; dev_node_t node; window_handle_t sram_win_handle; struct tok_info ti; @@ -214,14 +204,13 @@ link->conf.Present = PRESENT_OPTION; dev = init_trdev(NULL,0); - dev->priv = &info->ti; - link->irq.Instance = info->dev = dev; - if (dev == NULL) { ibmtr_detach(link); return NULL; } - + dev->priv = &info->ti; + link->irq.Instance = info->dev = dev; + dev->init = &ibmtr_probe; /* Register with Card Services */ @@ -259,7 +248,7 @@ { struct ibmtr_dev_t *info = link->priv; dev_link_t **linkp; - struct net_device *dev; + struct net_device *dev; DEBUG(0, "ibmtr_detach(0x%p)\n", link); @@ -288,8 +277,10 @@ /* Unlink device structure, free bits */ *linkp = link->next; - if (link->dev) + if (info->dev) { unregister_trdev(info->dev); + kfree(info->dev); + } kfree(info); } /* ibmtr_detach */ @@ -317,7 +308,6 @@ memreq_t mem; int i, last_ret, last_fn; u_char buf[64]; - unsigned char Shared_Ram_Base; DEBUG(0, "ibmtr_config(0x%p)\n", link); @@ -358,41 +348,40 @@ /* Allocate the MMIO memory window */ req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE; - req.Attributes |= WIN_USE_WAIT|WIN_STRICT_ALIGN; - req.Base = mmiobase; + req.Attributes |= WIN_USE_WAIT; + req.Base = 0; req.Size = 0x2000; req.AccessSpeed = 250; link->win = (window_handle_t)link->handle; CS_CHECK(RequestWindow, &link->win, &req); - mem.CardOffset = req.Base; + mem.CardOffset = mmiobase; mem.Page = 0; CS_CHECK(MapMemPage, link->win, &mem); ti->mmio = (u_long)ioremap(req.Base, req.Size); /* Allocate the SRAM memory window */ req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE; - req.Attributes |= WIN_USE_WAIT|WIN_MAP_BELOW_1MB; - req.Base = srambase; + req.Attributes |= WIN_USE_WAIT; + req.Base = 0; req.Size = sramsize * 1024; req.AccessSpeed = 250; info->sram_win_handle = (window_handle_t)link->handle; CS_CHECK(RequestWindow, &info->sram_win_handle, &req); - mem.CardOffset = req.Base; + mem.CardOffset = srambase; mem.Page = 0; CS_CHECK(MapMemPage, info->sram_win_handle, &mem); - Shared_Ram_Base = req.Base >> 12; - - ti->sram = 0; - ti->sram_base = Shared_Ram_Base; - + + ti->sram_base = mem.CardOffset >> 12; + ti->sram_virt = (u_long)ioremap(req.Base, req.Size); + CS_CHECK(RequestConfiguration, link->handle, &link->conf); /* Set up the Token-Ring Controller Configuration Register and turn on the card. Check the "Local Area Network Credit Card Adapters Technical Reference" SC30-3585 for this info. */ - ibmtr_hw_setup(dev); + ibmtr_hw_setup(dev, mmiobase); i = register_trdev(dev); @@ -474,18 +463,20 @@ struct net_device *dev = info->dev; DEBUG(1, "ibmtr_event(0x%06x)\n", event); - + switch (event) { case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { + /* set flag to bypass normal interrupt code */ + ((struct tok_info *)dev->priv)->sram_virt |= 1; netif_device_detach(dev); mod_timer(&link->release, jiffies + HZ/20); } break; case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT; - ibmtr_config(link); + ibmtr_config(link); break; case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; @@ -515,10 +506,9 @@ /*====================================================================*/ -static void ibmtr_hw_setup(struct net_device *dev) +static void ibmtr_hw_setup(struct net_device *dev, u_int mmiobase) { - struct tok_info *ti = dev->priv; - int i; + int i; /* Bizarre IBM behavior, there are 16 bits of information we need to set, but the card only allows us to send 4 bits at a @@ -527,11 +517,11 @@ the actual information */ /* First nibble provides 4 bits of mmio */ - i = ((int)ti->mmio >> 16) & 0x0F; + i = (mmiobase >> 16) & 0x0F; outb(i, dev->base_addr); /* Second nibble provides 3 bits of mmio */ - i = 0x10 | (((int)ti->mmio >> 12) & 0x0E); + i = 0x10 | ((mmiobase >> 12) & 0x0E); outb(i, dev->base_addr); /* Third nibble, hard-coded values */ @@ -541,8 +531,7 @@ /* Fourth nibble sets shared ram page size */ /* 8 = 00, 16 = 01, 32 = 10, 64 = 11 */ - - i = (sramsize >> 4) & 0x07; + i = (sramsize >> 4) & 0x07; i = ((i == 4) ? 3 : i) << 2; i |= 0x30; @@ -552,28 +541,10 @@ i |= 1; outb(i, dev->base_addr); - /* X40 will release the card for use */ - + /* 0x40 will release the card for use */ outb(0x40, dev->base_addr); - - return; -} -/*====================================================================== - - A sweet little function that circumvents the problem with - ibmtr.c trying to use more memory than we can allocate for - the PCMCIA card. ibmtr.c just assumes that if a card has - 64K of shared ram, the entire 64K must be mapped into memory, - whereas resources are sometimes a little tight in card services - so we fool ibmtr.c into thinking the card has less memory on - it than it has. - -======================================================================*/ - -unsigned char pcmcia_reality_check(unsigned char gss) -{ - return (gss < sramsize) ? sramsize : gss; + return; } /*====================================================================*/ @@ -581,7 +552,7 @@ static int __init init_ibmtr_cs(void) { servinfo_t serv; - DEBUG(0, "%s\n", version); + DEBUG(0, "%s", version); CardServices(GetCardServicesInfo, &serv); if (serv.Revision != CS_RELEASE_CODE) { printk(KERN_NOTICE "ibmtr_cs: Card Services release " diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcmcia/xircom_cb.c linux.ac/drivers/net/pcmcia/xircom_cb.c --- linux.vanilla/drivers/net/pcmcia/xircom_cb.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/net/pcmcia/xircom_cb.c Fri May 25 00:06:21 2001 @@ -0,0 +1,1306 @@ +/* + * xircom_cb: A driver for the (tulip-like) Xircom Cardbus ethernet cards + * + * This software is Copyright 2001 by the respective authors, and licensed under the GPL + * License. + * + * Written by Arjan van de Ven for Red Hat, Inc. + * Based on work by Jeff Garzik, Doug Ledford and Donald Becker + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * + * $Id: xircom_cb.c,v 1.10 2001/05/21 15:10:48 fenrus Exp $ + */ + + +#include +#include +#include +#include +#include +#include +#include + + + +#ifdef DEBUG +#define enter() printk("Enter: %s, %s line %i\n",__FUNCTION__,__FILE__,__LINE__) +#define leave() printk("Leave: %s, %s line %i\n",__FUNCTION__,__FILE__,__LINE__) +#else +#define enter() do {} while (0) +#define leave() do {} while (0) +#endif + + +MODULE_DESCRIPTION("Xircom Cardbus ethernet driver"); +MODULE_AUTHOR("Arjan van de Ven "); + + + +/* IO registers on the card, offsets */ +#define CSR0 0x00 +#define CSR1 0x08 +#define CSR2 0x10 +#define CSR3 0x18 +#define CSR4 0x20 +#define CSR5 0x28 +#define CSR6 0x30 +#define CSR7 0x38 +#define CSR8 0x40 +#define CSR9 0x48 +#define CSR10 0x50 +#define CSR11 0x58 +#define CSR12 0x60 +#define CSR13 0x68 +#define CSR14 0x70 +#define CSR15 0x78 +#define CSR16 0x80 + +/* PCI registers */ +#define PCI_POWERMGMT 0x40 + +/* Offsets of the buffers within the descriptor pages, in bytes */ + +#define NUMDESCRIPTORS 4 +#define RXTXBUFSIZE 8192 +#define MAX_PACKETSIZE 1536 + + +#define DescOwnedCard 0x80000000 +#define DescOwnedDriver 0x00000000 + +#define PromiscBit (1<<6) +#define CollisionBit (1<<8) +#define TxActiveBit (1<<13) +#define RxActiveBit (1<<1) +#define LastDescBit (1<<25) +#define LinkStatusBit (1<<27) + +#define PowerMgmtBits ( (1<<31)|(1<<30) ) + +static const unsigned int bufferoffsets[NUMDESCRIPTORS] = {128,2048,4096,6144}; + +/* note: this struct is assumed to be packed as this is the "hardware" layout */ +struct descriptor { + u32 status; + u32 control; + u32 address1; + u32 address2; +}; + + +struct xircom_private { + /* Send and receive buffers, kernel-addressable and dma addressable forms */ + + unsigned char *rx_buffer; + unsigned char *tx_buffer; + + struct descriptor *rx_desc; + struct descriptor *tx_desc; + + dma_addr_t rx_dma_handle; + dma_addr_t tx_dma_handle; + + struct sk_buff *tx_skb[NUMDESCRIPTORS]; + + unsigned long io_port; + + /* transmit_used is the rotating counter that indicates which transmit + descriptor has to be used next */ + unsigned int transmit_used; + + /* Spinlock to serialize register operations. + It must be helt while manipulating the following registers: + CSR0, CSR6, CSR7, CSR9, CSR10, CSR15 + */ + spinlock_t lock; + + + struct pci_dev *pdev; + struct net_device *dev; + struct net_device_stats stats; +}; + + +/* Function prototypes */ +static int xircom_probe(struct pci_dev *pdev, const struct pci_device_id *id); +static void xircom_remove(struct pci_dev *pdev); +static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int xircom_open(struct net_device *dev); +static int xircom_close(struct net_device *dev); +static void xircom_up(struct xircom_private *card); +static struct net_device_stats *xircom_get_stats(struct net_device *dev); + +static void investigate_rx_descriptor(struct net_device *dev,struct xircom_private *card, int descnr, unsigned int bufferoffset); +static unsigned int investigate_tx_descriptor(struct net_device *dev, struct xircom_private *card, unsigned int descnr, unsigned int bufferoffset); +static void read_mac_address(struct xircom_private *card); +static void tranceiver_voodoo(struct xircom_private *card); +static void initialize_card(struct xircom_private *card); +static inline void trigger_transmit(struct xircom_private *card); +static inline void trigger_receive(struct xircom_private *card); +static void setup_descriptors(struct xircom_private *card); +static inline void remove_descriptors(struct xircom_private *card); +static inline unsigned int link_status_changed(struct xircom_private *card); +static void activate_receiver(struct xircom_private *card); +static void deactivate_receiver(struct xircom_private *card); +static void activate_transmitter(struct xircom_private *card); +static void deactivate_transmitter(struct xircom_private *card); +static void enable_transmit_interrupt(struct xircom_private *card); +static void enable_receive_interrupt(struct xircom_private *card); +static void enable_link_interrupt(struct xircom_private *card); +static void disable_all_interrupts(struct xircom_private *card); +static inline unsigned int link_status(struct xircom_private *card); + + + +static struct pci_device_id xircom_pci_table[] __devinitdata = { + {0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID,}, + {0,}, +}; +MODULE_DEVICE_TABLE(pci, xircom_pci_table); + +static struct pci_driver xircom_ops = { + name: "xircom_cb", + id_table: xircom_pci_table, + probe: xircom_probe, + remove: xircom_remove, +}; + + +#ifdef DEBUG +static void print_binary(unsigned int number) +{ + int i,i2; + char buffer[64]; + memset(buffer,0,64); + i2=0; + for (i=31;i>=0;i--) { + if (number & (1<priv; + if (private==NULL) { + printk(KERN_ERR "xircom_probe: failed to allocate private device struct\n"); + return -ENODEV; + } + + /* Allocate the send/receive buffers */ + private->rx_buffer = pci_alloc_consistent(pdev,RXTXBUFSIZE,&private->rx_dma_handle); + if (private->rx_buffer == NULL) { + printk(KERN_ERR "xircom_probe: no memory for rx buffer \n"); + kfree(private); + return -ENODEV; + } + /* the descriptors are stored in the first bytes of the rx_buffer, hence the ugly cast */ + private->rx_desc = (struct descriptor *)private->rx_buffer; + + private->tx_buffer = pci_alloc_consistent(pdev,RXTXBUFSIZE,&private->tx_dma_handle); + if (private->tx_buffer == NULL) { + printk(KERN_ERR "xircom_probe: no memory for tx buffer \n"); + kfree(private->rx_buffer); + kfree(private); + return -ENODEV; + } + /* the descriptors are stored in the first bytes of the tx_buffer, hence the ugly cast */ + private->tx_desc = (struct descriptor *)private->tx_buffer; + + + printk(KERN_INFO "%s: Xircom cardbus revision %i at irq %i \n", dev->name, chip_rev, pdev->irq); + + private->dev = dev; + private->pdev = pdev; + private->io_port = pci_resource_start(pdev, 0); + private->lock = SPIN_LOCK_UNLOCKED; + dev->irq = pdev->irq; + dev->base_addr = private->io_port; + + + initialize_card(private); + read_mac_address(private); + setup_descriptors(private); + + dev->open = &xircom_open; + dev->hard_start_xmit = &xircom_start_xmit; + dev->stop = &xircom_close; + dev->get_stats = &xircom_get_stats; + dev->priv = private; + pci_set_drvdata(pdev,dev); + + + /* start the transmitter to get a heartbeat; don't do + that when there already is one though; Cisco's + really don't like that. */ + if (!link_status(private)) + tranceiver_voodoo(private); + + spin_lock_irqsave(&private->lock,flags); + activate_transmitter(private); + activate_receiver(private); + spin_unlock_irqrestore(&private->lock,flags); + + /* TODO: send 2 dummy packets here */ + + trigger_receive(private); + + leave(); + return 0; +} + + +/* + xircom_remove is called on module-unload or on device-eject. + it unregisters the irq, io-region and network device. + Interrupts and such are already stopped in the "ifconfig ethX down" + code. + */ +static void __devexit xircom_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pdev->driver_data; + struct xircom_private *card; + enter(); + + card=dev->priv; + + if (card->rx_buffer!=NULL) + pci_free_consistent(pdev,RXTXBUFSIZE,card->rx_buffer,card->rx_dma_handle); + card->rx_buffer = NULL; + card->rx_desc = NULL; + if (card->tx_buffer!=NULL) + pci_free_consistent(pdev,RXTXBUFSIZE,card->tx_buffer,card->tx_dma_handle); + card->tx_buffer = NULL; + card->tx_desc = NULL; + + release_region(dev->base_addr, 128); + unregister_netdev(dev); + kfree(dev); + pci_set_drvdata(pdev,NULL); + leave(); +} + +static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct net_device *dev = dev_instance; + struct xircom_private *card = dev->priv; + u32 status; + unsigned int xmit_free_count; + unsigned int i; + + enter(); + + + spin_lock(&card->lock); + status = inl(card->io_port+CSR5); + if (status==0xffffffff) {/* card has been ejected / powered down */ + spin_unlock(&card->lock); + return; + } + + /* Todo: check if there were any events at all; to speed up + returning if we're on a shared interrupt */ + + if (link_status_changed(card)) { + int newlink; + printk(KERN_DEBUG "xircom_cb: Link status has changed \n"); + newlink = link_status(card); + if (newlink) { + printk(KERN_INFO "xircom_cb: Link is %i mbit \n",newlink); + netif_carrier_on(dev); + } else { + printk(KERN_INFO "xircom_cb: Link is absent \n"); + netif_carrier_off(dev); + } + } + + /* Clear all remaining interrupt events */ + status |= 0xffffffff; /* FIXME: make this clear only the + real existing bits */ + outl(status,card->io_port+CSR5); + + xmit_free_count = 0; + + for (i=0;ilock); + leave(); +} + +static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct xircom_private *card; + unsigned long flags; + unsigned int nextdescriptor; + unsigned int desc; + enter(); + + card = (struct xircom_private*)dev->priv; + + spin_lock_irqsave(&card->lock,flags); + + nextdescriptor = (card->transmit_used +1) % (NUMDESCRIPTORS); + desc = card->transmit_used; + + /* only send the packet if the descriptor is free */ + if (card->tx_desc[desc].status==0) { + /* Copy the packet data; zero the memory first as the card + sometimes sends more than you ask it to. */ + + memset(&card->tx_buffer[bufferoffsets[desc]],0,MAX_PACKETSIZE); + memcpy(&(card->tx_buffer[bufferoffsets[desc]]),skb->data,skb->len); + + + /* FIXME: The specification tells us that the length we send HAS to be a multiple of + 4 bytes. */ + + card->tx_desc[desc].control = skb->len; + if (desc == NUMDESCRIPTORS-1) + card->tx_desc[desc].control |= LastDescBit; /* bit 25: last descriptor of the ring */ + + card->tx_desc[desc].control |= 0xF0000000; + /* 0xF0... means want interrupts*/ + card->tx_skb[desc] = skb; + + wmb(); + /* This gives the descriptor to the card */ + card->tx_desc[desc].status = DescOwnedCard; + trigger_transmit(card); + if (((int)card->tx_desc[nextdescriptor].status)<0) { /* next descriptor is occupied... */ + netif_stop_queue(dev); + } + card->transmit_used = nextdescriptor; + spin_unlock_irqrestore(&card->lock,flags); + leave(); + return 0; + } + + + + /* Uh oh... no free descriptor... drop the packet */ + /* This should not happen in theory...*/ + netif_stop_queue(dev); + spin_unlock_irqrestore(&card->lock,flags); + trigger_transmit(card); + leave(); + + return -EIO; +} + + + + +static int xircom_open(struct net_device *dev) +{ + struct xircom_private *xp = (struct xircom_private *) dev->priv; + int retval; + enter(); + printk(KERN_INFO "Xircom cardbus adaptor found, registering as %s, using irq %i \n",dev->name,dev->irq); + retval = request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev); + if (retval) { + printk(KERN_ERR "xircom_cb: Unable to aquire IRQ %i, aborting.\n",dev->irq); + leave(); + return retval; + } + + xircom_up(xp); + leave(); + return 0; +} + +static int xircom_close(struct net_device *dev) +{ + struct xircom_private *card; + unsigned long flags; + + enter(); + card = dev->priv; + netif_stop_queue(dev); /* we don't want to send new packets */ + + + spin_lock_irqsave(&card->lock,flags); + + disable_all_interrupts(card); +#if 0 + /* We can enable this again once we send dummy packets on ifconfig ethX up */ + deactivate_receiver(card); + deactivate_transmitter(card); +#endif + remove_descriptors(card); + + spin_unlock_irqrestore(&card->lock,flags); + + free_irq(dev->irq,dev); + + leave(); + + return 0; + +} + + + +static struct net_device_stats *xircom_get_stats(struct net_device *dev) +{ + struct xircom_private *card = (struct xircom_private *)dev->priv; + return &card->stats; +} + + + + +static void initialize_card(struct xircom_private *card) +{ + unsigned int val; + unsigned long flags; + enter(); + + + spin_lock_irqsave(&card->lock, flags); + + /* First: reset the card */ + val = inl(card->io_port + CSR0); + val |= 0x01; /* Software reset */ + outl(val, card->io_port + CSR0); + + udelay(100); /* give the card some time to reset */ + + val = inl(card->io_port + CSR0); + val &= ~0x01; /* disable Software reset */ + outl(val, card->io_port + CSR0); + + + val = 0; /* Value 0x00 is a safe and conservative value + for the PCI configuration settings */ + outl(val, card->io_port + CSR0); + + + disable_all_interrupts(card); + deactivate_receiver(card); + deactivate_transmitter(card); + + spin_unlock_irqrestore(&card->lock, flags); + + leave(); +} + +/* +trigger_transmit causes the card to check for frames to be transmitted. +This is accomplished by writing to the CSR1 port. The documentation +claims that the act of writing is sufficient and that the value is +ignored; I chose zero. +*/ +static inline void trigger_transmit(struct xircom_private *card) +{ + enter(); + outl(0, card->io_port + CSR1); + leave(); +} + +/* +trigger_receive causes the card to check for empty frames in the +descriptor list in which packets can be received. +This is accomplished by writing to the CSR2 port. The documentation +claims that the act of writing is sufficient and that the value is +ignored; I chose zero. +*/ +static inline void trigger_receive(struct xircom_private *card) +{ + enter(); + outl(0, card->io_port + CSR2); + leave(); +} + +/* +setup_descriptors initializes the send and receive buffers to be valid +descriptors and programs the addresses into the card. +*/ +static void setup_descriptors(struct xircom_private *card) +{ + unsigned int val; + u32 address; + unsigned int i; + enter(); + + + if (card->rx_buffer == NULL) + BUG(); + if (card->tx_buffer == NULL) + BUG(); + + /* Receive descriptors */ + memset(card->rx_desc, 0, 128); /* clear the descriptors */ + for (i=0;i 0x80000000 */ + card->rx_desc[i].status = DescOwnedCard; + /* Rx Descr1: buffer 1 is 1536 bytes, buffer 2 is 0 bytes */ + card->rx_desc[i].control = MAX_PACKETSIZE; + if (i==NUMDESCRIPTORS-1) + card->rx_desc[i].control |= LastDescBit; /* bit 25 is "last descriptor" */ + + /* Rx Descr2: address of the buffer + we store the buffer at the 2nd half of the page */ + + address = card->rx_dma_handle; + + card->rx_desc[i].address1 = cpu_to_le32(address + bufferoffsets[i]); + /* Rx Desc3: address of 2nd buffer -> 0 */ + card->rx_desc[i].address2 = 0; + } + + wmb(); + /* Write the receive descriptor ring address to the card */ + address = card->rx_dma_handle; + val = cpu_to_le32(address); + outl(val, card->io_port + CSR3); /* Receive descr list address */ + + + /* transmit descriptors */ + memset(card->tx_desc, 0, 128); /* clear the descriptors */ + + for (i=0;i 0x00000000 */ + card->tx_desc[i].status = DescOwnedDriver; + /* Tx Descr1: buffer 1 is 1536 bytes, buffer 2 is 0 bytes */ + card->tx_desc[i].control = MAX_PACKETSIZE; + if (i==NUMDESCRIPTORS-1) + card->tx_desc[i].control |= LastDescBit; /* bit 25 is "last descriptor" */ + + /* Tx Descr2: address of the buffer + we store the buffer at the 2nd half of the page */ + address = card->tx_dma_handle; + card->tx_desc[i].address1 = cpu_to_le32(address + bufferoffsets[i]); + /* Tx Desc3: address of 2nd buffer -> 0 */ + card->tx_desc[i].address2 = 0; + } + + wmb(); + /* wite the transmit descriptor ring to the card */ + address = card->tx_dma_handle; + val =cpu_to_le32(address); + outl(val, card->io_port + CSR4); /* xmit descr list address */ + + leave(); +} + +/* +remove_descriptors informs the card the descriptors are no longer +valid by setting the address in the card to 0x00. +*/ +static inline void remove_descriptors(struct xircom_private *card) +{ + unsigned int val; + enter(); + + val = 0; + outl(val, card->io_port + CSR3); /* Receive descriptor address */ + outl(val, card->io_port + CSR4); /* Send descriptor address */ + + leave(); +} + +/* +link_status_changed returns 1 if the card has indicated that +the link status has changed. The new link status has to be read from CSR12. + +This function also clears the status-bit. +*/ +static inline unsigned int link_status_changed(struct xircom_private *card) +{ + unsigned int val; + enter(); + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & LinkStatusBit) == 0) { /* no change */ + leave(); + return 0; + } + + /* clear the event by writing a 1 to the bit in the + status register. */ + val = LinkStatusBit; + outl(val, card->io_port + CSR5); + + leave(); + return 1; +} + + +/* +transmit_active returns 1 if the transmitter on the card is +in a non-stopped state. +*/ +static inline int transmit_active(struct xircom_private *card) +{ + unsigned int val; + enter(); + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & (7 << 20)) == 0) { /* transmitter disabled */ + leave(); + return 0; + } + + leave(); + return 1; +} + +/* +receive_active returns 1 if the receiver on the card is +in a non-stopped state. +*/ +static inline unsigned int receive_active(struct xircom_private *card) +{ + unsigned int val; + enter(); + + + val = inl(card->io_port + CSR5); /* Status register */ + + if ((val & (7 << 17)) == 0) { /* receiver disabled */ + leave(); + return 0; + } + + leave(); + return 1; +} + +/* +activate_receiver enables the receiver on the card. +Before being allowed to active the receiver, the receiver +must be completely de-activated. To achieve this, +this code actually disables the receiver first; then it waits for the +receiver to become inactive, then it activates the receiver and then +it waits for the receiver to be active. + +must be called with the lock held and interrupts disabled. +*/ +static void activate_receiver(struct xircom_private *card) +{ + unsigned int val; + int counter; + enter(); + + + val = inl(card->io_port + CSR6); /* Operation mode */ + + /* If the "active" bit (1) is set and the receiver is already + active, no need to do the expensive thing */ + if ((val& RxActiveBit) && (receive_active(card))) + return; + + + val = val & ~RxActiveBit; /* disable the receiver */ + outl(val, card->io_port + CSR6); + + counter = 10; + while (counter > 0) { + if (!receive_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Receiver failed to deactivate\n"); + } + + /* enable the receiver */ + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val | RxActiveBit; /* enable the receiver */ + outl(val, card->io_port + CSR6); + + /* now wait for the card to activate again */ + counter = 10; + while (counter > 0) { + if (receive_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Receiver failed to re-activate\n"); + } + + leave(); +} + +/* +deactivate_receiver disables the receiver on the card. +To achieve this this code disables the receiver first; +then it waits for the receiver to become inactive. + +must be called with the lock held and interrupts disabled. +*/ +static void deactivate_receiver(struct xircom_private *card) +{ + unsigned int val; + int counter; + enter(); + + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val & ~RxActiveBit; /* disable the receiver */ + outl(val, card->io_port + CSR6); + + counter = 10; + while (counter > 0) { + if (!receive_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Receiver failed to deactivate\n"); + } + + + leave(); +} + + +/* +activate_transmitter enables the transmitter on the card. +Before being allowed to active the transmitter, the transmitter +must be completely de-activated. To achieve this, +this code actually disables the transmitter first; then it waits for the +transmitter to become inactive, then it activates the transmitter and then +it waits for the transmitter to be active again. + +must be called with the lock held and interrupts disabled. +*/ +static void activate_transmitter(struct xircom_private *card) +{ + unsigned int val; + int counter; + enter(); + + + val = inl(card->io_port + CSR6); /* Operation mode */ + + /* If the "active" bit (13) is set and the receiver is already + active, no need to do the expensive thing */ + if ((val & TxActiveBit) && (transmit_active(card))) + return; + + val = val & ~TxActiveBit; /* disable the transmitter */ + outl(val, card->io_port + CSR6); + + counter = 10; + while (counter > 0) { + if (!transmit_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Transmitter failed to deactivate\n"); + } + + /* enable the transmitter */ + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val | TxActiveBit; /* enable the transmitter */ + outl(val, card->io_port + CSR6); + + /* now wait for the card to activate again */ + counter = 10; + while (counter > 0) { + if (transmit_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Transmitter failed to re-activate\n"); + } + + leave(); +} + +/* +deactivate_transmitter disables the transmitter on the card. +To achieve this this code disables the transmitter first; +then it waits for the transmitter to become inactive. + +must be called with the lock held and interrupts disabled. +*/ +static void deactivate_transmitter(struct xircom_private *card) +{ + unsigned int val; + int counter; + enter(); + + val = inl(card->io_port + CSR6); /* Operation mode */ + val = val & ~TxActiveBit; /* disable the transmitter */ + outl(val, card->io_port + CSR6); + + counter = 20; + while (counter > 0) { + if (!transmit_active(card)) + break; + /* wait a while */ + udelay(50); + counter--; + if (counter <= 0) + printk(KERN_ERR "xircom_cb: Transmitter failed to deactivate\n"); + } + + + leave(); +} + + +/* +enable_transmit_interrupt enables the transmit interrupt + +must be called with the lock held and interrupts disabled. +*/ +static void enable_transmit_interrupt(struct xircom_private *card) +{ + unsigned int val; + enter(); + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val |= 1; /* enable the transmit interrupt */ + outl(val, card->io_port + CSR7); + + leave(); +} + + +/* +enable_receive_interrupt enables the receive interrupt + +must be called with the lock held and interrupts disabled. +*/ +static void enable_receive_interrupt(struct xircom_private *card) +{ + unsigned int val; + enter(); + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val = val | (1 << 6); /* enable the receive interrupt */ + outl(val, card->io_port + CSR7); + + leave(); +} + +/* +enable_link_interrupt enables the link status change interrupt + +must be called with the lock held and interrupts disabled. +*/ +static void enable_link_interrupt(struct xircom_private *card) +{ + unsigned int val; + enter(); + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val = val | LinkStatusBit; /* enable the link status chage interrupt */ + outl(val, card->io_port + CSR7); + + leave(); +} + + + +/* +disable_all_interrupts disables all interrupts + +must be called with the lock held and interrupts disabled. +*/ +static void disable_all_interrupts(struct xircom_private *card) +{ + unsigned int val; + enter(); + + val = 0; /* disable all interrupts */ + outl(val, card->io_port + CSR7); + + leave(); +} + +/* +enable_common_interrupts enables several weird interrupts + +must be called with the lock held and interrupts disabled. +*/ +static void enable_common_interrupts(struct xircom_private *card) +{ + unsigned int val; + enter(); + + val = inl(card->io_port + CSR7); /* Interrupt enable register */ + val |= (1<<16); /* Normal Interrupt Summary */ + val |= (1<<15); /* Abnormal Interrupt Summary */ + val |= (1<<13); /* Fatal bus error */ + val |= (1<<8); /* Receive Process Stopped */ + val |= (1<<7); /* Receive Buffer Unavailable */ + val |= (1<<5); /* Transmit Underflow */ + val |= (1<<2); /* Transmit Buffer Unavailable */ + val |= (1<<1); /* Transmit Process Stopped */ + outl(val, card->io_port + CSR7); + + leave(); +} + +/* +enable_promisc starts promisc mode + +must be called with the lock held and interrupts disabled. +*/ +static inline void enable_promisc(struct xircom_private *card) +{ + unsigned int val; + enter(); + + val = inl(card->io_port + CSR6); + val = val | PromiscBit; /* Bit 6 */ + outl(val, card->io_port + CSR6); + + printk(KERN_INFO "xircom_cb: enabling promiscues mode \n"); + leave(); +} + + + + +/* +link_status() checks the the links status and will return 0 for no link, +10 for 10mbit link and 100 for.. guess what. + +Must be called in locked state with interrupts disabled +*/ +static inline unsigned int link_status(struct xircom_private *card) +{ + unsigned int val; + enter(); + + val = inb(card->io_port + CSR12); + + if (!(val&(1<<2))) /* bit 2 is 0 for 10mbit link, 1 for not an 10mbit link */ + return 10; + if (!(val&(1<<1))) /* bit 1 is 0 for 100mbit link, 1 for not an 100mbit link */ + return 100; + + /* If we get here -> no link at all */ + + leave(); + return 0; +} + + + +/* + +set_half_duplex() sets the card to half duplex mode. In order to do this, +set_half_duplex() has to deactivate the transmitter and receiver first. It +will re-enable the transmitter and receiver if those were active from the +beginning. + +Must be called in locked state +*/ +static void set_half_duplex(struct xircom_private *card) +{ + unsigned int val; + int rx,tx; + enter(); + + rx=receive_active(card); + tx=transmit_active(card); + + deactivate_transmitter(card); + deactivate_receiver(card); + + val = inb(card->io_port + CSR6); + val &= ~(1<<9); + outb(val,card->io_port + CSR6); + if (rx) + activate_receiver(card); + if (tx) + activate_transmitter(card); + + leave(); +} + + +/* + read_mac_address() reads the MAC address from the NIC and stores it in the "dev" structure. + + This function will take the spinlock itself and can, as a result, not be called with the lock helt. + */ +static void read_mac_address(struct xircom_private *card) +{ + unsigned char j, tuple, link, data_id, data_count; + unsigned long flags; + int i; + + enter(); + + spin_lock_irqsave(&card->lock, flags); + + outl(1 << 12, card->io_port + CSR9); /* enable boot rom access */ + for (i = 0x100; i < 0x1f7; i += link + 2) { + outl(i, card->io_port + CSR10); + tuple = inl(card->io_port + CSR9) & 0xff; + outl(i + 1, card->io_port + CSR10); + link = inl(card->io_port + CSR9) & 0xff; + outl(i + 2, card->io_port + CSR10); + data_id = inl(card->io_port + CSR9) & 0xff; + outl(i + 3, card->io_port + CSR10); + data_count = inl(card->io_port + CSR9) & 0xff; + if ((tuple == 0x22) && (data_id == 0x04) && (data_count == 0x06)) { + /* + * This is it. We have the data we want. + */ + for (j = 0; j < 6; j++) { + outl(i + j + 4, card->io_port + CSR10); + card->dev->dev_addr[j] = inl(card->io_port + CSR9) & 0xff; + } + break; + } else if (link == 0) { + break; + } + } + spin_unlock_irqrestore(&card->lock, flags); +#ifdef DEBUG + for (i = 0; i < 6; i++) + printk("%c%2.2X", i ? ':' : ' ', card->dev->dev_addr[i]); + printk("\n"); +#endif + leave(); +} + + +/* + tranceiver_voodoo() enables the external UTP plug thingy. + it's called voodoo as I stole this code and cannot cross-reference + it with the specification. + */ +static void tranceiver_voodoo(struct xircom_private *card) +{ + unsigned long flags; + u32 tmp32; + + enter(); + + /* disable all powermanagement */ + pci_read_config_dword(card->pdev, PCI_POWERMGMT,&tmp32); + tmp32 &= ~PowerMgmtBits; + pci_write_config_dword(card->pdev, PCI_POWERMGMT, tmp32); + + setup_descriptors(card); + + spin_lock_irqsave(&card->lock, flags); + + outl(0x0008, card->io_port + CSR15); + udelay(25); + outl(0xa8050000, card->io_port + CSR15); + udelay(25); + outl(0xa00f0000, card->io_port + CSR15); + udelay(25); + + spin_unlock_irqrestore(&card->lock, flags); + + netif_start_queue(card->dev); + leave(); +} + + +static void xircom_up(struct xircom_private *card) +{ + unsigned long flags; + int i; + u32 tmp32; + + enter(); + + /* disable all powermanagement */ + pci_read_config_dword(card->pdev, PCI_POWERMGMT,&tmp32); + tmp32 &= ~PowerMgmtBits; + pci_write_config_dword(card->pdev, PCI_POWERMGMT, tmp32); + + setup_descriptors(card); + + spin_lock_irqsave(&card->lock, flags); + + + enable_link_interrupt(card); + enable_transmit_interrupt(card); + enable_receive_interrupt(card); + enable_common_interrupts(card); + enable_promisc(card); + + /* The card can have received packets already, read them away now */ + for (i=0;idev,card,i,bufferoffsets[i]); + + + set_half_duplex(card); + spin_unlock_irqrestore(&card->lock, flags); + trigger_receive(card); + trigger_transmit(card); + netif_start_queue(card->dev); + leave(); +} + +static void investigate_rx_descriptor(struct net_device *dev,struct xircom_private *card, int descnr, unsigned int bufferoffset) +{ + int status; + + enter(); + status = card->rx_desc[descnr].status; + + if ((status > 0)) { /* packet received */ + + /* TODO: discard error packets */ + + short pkt_len = ((status >> 16) & 0x7ff) - 4; /* minus 4, we don't want the CRC */ + struct sk_buff *skb; + + if (pkt_len > 1518) { + printk(KERN_ERR "xircom_cb: Packet length %i is bogus \n",pkt_len); + pkt_len = 1518; + } + + skb = dev_alloc_skb(pkt_len + 2); + if (skb == NULL) { + card->stats.rx_dropped++; + goto out; + } + skb->dev = dev; + skb_reserve(skb, 2); + eth_copy_and_sum(skb, &card->rx_buffer[bufferoffset], pkt_len, 0); + skb_put(skb, pkt_len); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + card->stats.rx_packets++; + card->stats.rx_bytes += pkt_len; + + out: + /* give the buffer back to the card */ + card->rx_desc[descnr].status = DescOwnedCard; + trigger_receive(card); + } + + leave(); + +} + + +/* Returns 1 if the descriptor is free or became free */ +static unsigned int investigate_tx_descriptor(struct net_device *dev, struct xircom_private *card, unsigned int descnr, unsigned int bufferoffset) +{ + int status,retval = 0; + enter(); + + status = card->tx_desc[descnr].status; + + if (status == DescOwnedDriver) + return 1; +#if 0 + if (status & 0x8000) { /* Major error */ + printk(KERN_ERR "Major transmit error status %x \n", status); + card->tx_desc[descnr].status = 0; + netif_wake_queue (dev); + } +#endif + if (status > 0) { /* bit 31 is 0 when done */ + card->stats.tx_packets++; + if (card->tx_skb[descnr]!=NULL) { + card->stats.tx_bytes += card->tx_skb[descnr]->len; + dev_kfree_skb_irq(card->tx_skb[descnr]); + } + card->tx_skb[descnr] = NULL; + /* Bit 8 in the status field is 1 if there was a collision */ + if (status & CollisionBit) + card->stats.collisions++; + card->tx_desc[descnr].status = DescOwnedDriver; /* descriptor is free again */ + retval = 1; + } + + leave(); + return retval; +} + + +static int __init xircom_init(void) +{ + pci_register_driver(&xircom_ops); + return 0; +} + +static void __exit xircom_exit(void) +{ + pci_unregister_driver(&xircom_ops); +} + +module_init(xircom_init) +module_exit(xircom_exit) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcmcia/xircom_cb.h linux.ac/drivers/net/pcmcia/xircom_cb.h --- linux.vanilla/drivers/net/pcmcia/xircom_cb.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/net/pcmcia/xircom_cb.h Tue Apr 3 17:54:53 2001 @@ -0,0 +1,45 @@ +#ifndef _INCLUDE_GUARD_XIRCOM_CB_H_ +#define _INCLUDE_GUARD_XIRCOM_CB_H_ + +static int xircom_probe(struct pci_dev *pdev, const struct pci_device_id *id); +static void xircom_remove(struct pci_dev *pdev); +static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int xircom_open(struct net_device *dev); +static int xircom_close(struct net_device *dev); +static struct net_device_stats *xircom_get_stats(struct net_device *dev); + + + + +static void investigate_read_descriptor(struct net_device *dev,int number, int descnr, unsigned int bufferoffset); +static void investigate_write_descriptor(struct net_device *dev,int number, int descnr, unsigned int bufferoffset); + + +static void xircom_up(int number); +static void read_mac_address(int number); +static void tranceiver_voodoo(int number); +static void initialize_card(int number); +static void trigger_transmit(int number); +static void trigger_receive(int number); +static void setup_descriptors(int number); +static void remove_descriptors(int number); +static int link_status_changed(int number); +static int packed_is_received(int number); +static int packed_is_sent(int number); +static int transmit_active(int number); +static int receive_active(int number); +static int activate_receiver(int number); +static int deactivate_receiver(int number); +static int activate_transmitter(int number); +static int deactivate_transmitter(int number); +static int enable_transmit_interrupt(int number); +static int disable_transmit_interrupt(int number); +static int disable_receive_interrupt(int number); +static int enable_receive_interrupt(int number); +static int enable_link_interrupt(int number); +static int disable_link_interrupt(int number); +static int disable_all_interrupts(int number); +static int link_status(int number); + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/pcnet32.c linux.ac/drivers/net/pcnet32.c --- linux.vanilla/drivers/net/pcnet32.c Mon Apr 30 15:13:17 2001 +++ linux.ac/drivers/net/pcnet32.c Sat May 26 19:21:25 2001 @@ -481,7 +481,7 @@ -static int __init +static int __devinit pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent) { static int card_idx; @@ -516,7 +516,7 @@ * Called from both pcnet32_probe_vlbus and pcnet_probe_pci. * pdev will be NULL when called from pcnet32_probe_vlbus. */ -static int __init +static int __devinit pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int card_idx, struct pci_dev *pdev) { struct pcnet32_private *lp; @@ -653,7 +653,7 @@ } if( memcmp( promaddr, dev->dev_addr, 6) ) { - printk(" warning PROM address does not match CSR address"); + printk(" warning PROM address does not match CSR address\n"); #if defined(__i386__) printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name); memcpy(dev->dev_addr, promaddr, 6); @@ -1375,6 +1375,13 @@ * DOS packet driver after a warm reboot */ lp->a.write_bcr (ioaddr, 20, 4); + + /* + * FIXME: What happens if the bcr write is posted, the buffers are + * freed and there is still incoming DMA traffic + */ + +#warning "PCI posting bug" free_irq(dev->irq, dev); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ppp_async.c linux.ac/drivers/net/ppp_async.c --- linux.vanilla/drivers/net/ppp_async.c Mon Apr 30 15:13:17 2001 +++ linux.ac/drivers/net/ppp_async.c Sun Apr 22 01:17:48 2001 @@ -95,7 +95,7 @@ static void async_lcp_peek(struct asyncppp *ap, unsigned char *data, int len, int inbound); -struct ppp_channel_ops async_ops = { +static struct ppp_channel_ops async_ops = { ppp_async_send, ppp_async_ioctl }; @@ -307,7 +307,7 @@ write_wakeup: ppp_asynctty_wakeup, }; -int +static int __init ppp_async_init(void) { int err; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ppp_deflate.c linux.ac/drivers/net/ppp_deflate.c --- linux.vanilla/drivers/net/ppp_deflate.c Tue Feb 13 21:15:05 2001 +++ linux.ac/drivers/net/ppp_deflate.c Tue Apr 3 17:54:53 2001 @@ -598,7 +598,7 @@ * Module interface table *************************************************************/ -/* These are in ppp.c */ +/* These are in ppp_generic.c */ extern int ppp_register_compressor (struct compressor *cp); extern void ppp_unregister_compressor (struct compressor *cp); @@ -639,7 +639,7 @@ z_comp_stats, /* decomp_stat */ }; -int deflate_init(void) +int __init deflate_init(void) { int answer = ppp_register_compressor(&ppp_deflate); if (answer == 0) @@ -649,7 +649,7 @@ return answer; } -void deflate_cleanup(void) +void __exit deflate_cleanup(void) { ppp_unregister_compressor(&ppp_deflate); ppp_unregister_compressor(&ppp_deflate_draft); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ppp_generic.c linux.ac/drivers/net/ppp_generic.c --- linux.vanilla/drivers/net/ppp_generic.c Mon Apr 30 15:13:17 2001 +++ linux.ac/drivers/net/ppp_generic.c Mon Apr 30 15:22:08 2001 @@ -878,7 +878,7 @@ return err; } -int +static int ppp_net_init(struct net_device *dev) { dev->hard_header_len = PPP_HDRLEN; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/ppp_synctty.c linux.ac/drivers/net/ppp_synctty.c --- linux.vanilla/drivers/net/ppp_synctty.c Mon Apr 30 15:13:17 2001 +++ linux.ac/drivers/net/ppp_synctty.c Sun Apr 22 01:17:55 2001 @@ -96,7 +96,7 @@ static void ppp_sync_input(struct syncppp *ap, const unsigned char *buf, char *flags, int count); -struct ppp_channel_ops sync_ops = { +static struct ppp_channel_ops sync_ops = { ppp_sync_send, ppp_sync_ioctl }; @@ -365,7 +365,7 @@ write_wakeup: ppp_sync_wakeup, }; -int +static int __init ppp_sync_init(void) { int err; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/rrunner.c linux.ac/drivers/net/rrunner.c --- linux.vanilla/drivers/net/rrunner.c Mon Apr 30 15:13:17 2001 +++ linux.ac/drivers/net/rrunner.c Thu Apr 19 22:43:43 2001 @@ -117,6 +117,14 @@ static int probed __initdata = 0; +#if LINUX_VERSION_CODE >= 0x20400 +static struct pci_device_id rrunner_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_ESSENTIAL, PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER, PCI_ANY_ID, PCI_ANY_ID, }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, rrunner_pci_tbl); +#endif /* LINUX_VERSION_CODE >= 0x20400 */ + #ifdef NEW_NETINIT int __init rr_hippi_probe (void) #else @@ -137,9 +145,6 @@ return -ENODEV; probed++; - if (!pci_present()) /* is PCI BIOS even present? */ - return -ENODEV; - version_disp = 0; while((pdev = pci_find_device(PCI_VENDOR_ID_ESSENTIAL, @@ -176,6 +181,7 @@ sprintf(rrpriv->name, "RoadRunner serial HIPPI"); dev->irq = pdev->irq; + SET_MODULE_OWNER(dev); dev->open = &rr_open; dev->hard_start_xmit = &rr_start_xmit; dev->stop = &rr_close; @@ -1183,7 +1189,6 @@ netif_start_queue(dev); - MOD_INC_USE_COUNT; return ecode; error: @@ -1348,7 +1353,6 @@ free_irq(dev->irq, dev); spin_unlock(&rrpriv->lock); - MOD_DEC_USE_COUNT; return 0; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/setup.c linux.ac/drivers/net/setup.c --- linux.vanilla/drivers/net/setup.c Tue Feb 13 21:15:05 2001 +++ linux.ac/drivers/net/setup.c Mon Apr 9 23:25:02 2001 @@ -10,7 +10,6 @@ #include extern int slip_init_ctrl_dev(void); -extern int strip_init_ctrl_dev(void); extern int x25_asy_init_ctrl_dev(void); extern int dmascc_init(void); @@ -23,13 +22,13 @@ extern int scc_enet_init(void); extern int fec_enet_init(void); extern int dlci_setup(void); -extern int lapbeth_init(void); extern int sdla_setup(void); extern int sdla_c_setup(void); extern int comx_init(void); extern int lmc_setup(void); extern int madgemc_probe(void); +extern int uml_net_probe(void); /* Pad device name to IFNAMSIZ=16. F.e. __PAD6 is string of 9 zeros. */ #define __PAD6 "\0\0\0\0\0\0\0\0\0" @@ -64,9 +63,6 @@ #if defined(CONFIG_SDLA) {sdla_c_setup, 0}, #endif -#if defined(CONFIG_LAPBETHER) - {lapbeth_init, 0}, -#endif #if defined(CONFIG_ARCNET) {arcnet_init, 0}, #endif @@ -107,6 +103,10 @@ #ifdef CONFIG_MADGEMC {madgemc_probe, 0}, #endif +#ifdef CONFIG_NET_UM_ETH + {uml_net_probe, 0}, +#endif + {NULL, 0}, }; @@ -139,9 +139,6 @@ #endif #if defined(CONFIG_X25_ASY) x25_asy_init_ctrl_dev(); -#endif -#if defined(CONFIG_STRIP) - strip_init_ctrl_dev(); #endif } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/sis900.c linux.ac/drivers/net/sis900.c --- linux.vanilla/drivers/net/sis900.c Mon Apr 30 15:13:17 2001 +++ linux.ac/drivers/net/sis900.c Tue Apr 24 18:05:49 2001 @@ -80,7 +80,7 @@ SIS_900 = 0, SIS_7016 }; -static char * card_names[] = { +static char * card_names[] __devinitdata = { "SiS 900 PCI Fast Ethernet", "SiS 7016 PCI Fast Ethernet" }; @@ -602,7 +602,7 @@ * Note that location is in word (16 bits) unit */ -static u16 read_eeprom(long ioaddr, int location) +static u16 __devinit read_eeprom(long ioaddr, int location) { int i; u16 retval = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/sk98lin/skge.c linux.ac/drivers/net/sk98lin/skge.c --- linux.vanilla/drivers/net/sk98lin/skge.c Mon Apr 30 15:13:17 2001 +++ linux.ac/drivers/net/sk98lin/skge.c Thu Apr 19 22:44:18 2001 @@ -452,12 +452,14 @@ printk(KERN_ERR "%s: Unable to map I/O register, " "SK 98xx No. %i will be disabled.\n", dev->name, boards_found); + kfree(dev); break; } pAC->Index = boards_found; if (SkGeBoardInit(dev, pAC)) { FreeResources(dev); + kfree(dev); continue; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/sk_g16.c linux.ac/drivers/net/sk_g16.c --- linux.vanilla/drivers/net/sk_g16.c Mon Apr 30 15:13:17 2001 +++ linux.ac/drivers/net/sk_g16.c Sat May 26 19:25:04 2001 @@ -544,12 +544,13 @@ { int ioaddr; /* I/O port address used for POS regs */ int *port, ports[] = SK_IO_PORTS; /* SK_G16 supported ports */ + static unsigned version_printed; /* get preconfigured base_addr from dev which is done in Space.c */ int base_addr = dev->base_addr; - PRINTK(("%s: %s", SK_NAME, rcsid)); - rcsid = NULL; /* We do not want to use this further */ + if (version_printed++ == 0) + PRINTK(("%s: %s", SK_NAME, rcsid)); if (base_addr > 0x0ff) /* Check a single specified address */ { @@ -2080,7 +2081,7 @@ * YY/MM/DD uid Description -*/ -void SK_print_ram(struct net_device *dev) +void __init SK_print_ram(struct net_device *dev) { int i; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/sk_mca.c linux.ac/drivers/net/sk_mca.c --- linux.vanilla/drivers/net/sk_mca.c Tue Feb 13 21:15:05 2001 +++ linux.ac/drivers/net/sk_mca.c Tue Apr 24 18:05:49 2001 @@ -91,6 +91,7 @@ #include #include #include +#include #include #include #include @@ -151,7 +152,7 @@ /* deduce resources out of POS registers */ -static void getaddrs(int slot, int junior, int *base, int *irq, +static void __init getaddrs(int slot, int junior, int *base, int *irq, skmca_medium * medium) { u_char pos0, pos1, pos2; @@ -197,7 +198,7 @@ is disabled and won't get detected using the standard probe. We therefore have to scan the slots manually :-( */ -static int dofind(int *junior, int firstslot) +static int __init dofind(int *junior, int firstslot) { int slot; unsigned int id; @@ -524,7 +525,7 @@ /* probe for device's irq */ -static int ProbeIRQ(struct SKMCA_NETDEV *dev) +static int __init ProbeIRQ(struct SKMCA_NETDEV *dev) { unsigned long imaskval, njiffies, irq; u16 csr0val; @@ -1072,7 +1073,7 @@ static int startslot; /* counts through slots when probing multiple devices */ -int skmca_probe(struct SKMCA_NETDEV *dev) +int __init skmca_probe(struct SKMCA_NETDEV *dev) { int force_detect = 0; int junior, slot, i; @@ -1250,8 +1251,8 @@ }; #endif -int irq = 0; -int io = 0; +int irq; +int io; int init_module(void) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/smc-mca.c linux.ac/drivers/net/smc-mca.c --- linux.vanilla/drivers/net/smc-mca.c Tue Apr 3 17:32:14 2001 +++ linux.ac/drivers/net/smc-mca.c Tue Apr 24 18:05:49 2001 @@ -91,7 +91,7 @@ char *name; }; -static const struct smc_mca_adapters_t smc_mca_adapters[] = { +static struct smc_mca_adapters_t smc_mca_adapters[] __initdata = { { 0x61c8, "SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)" }, { 0x61c9, "SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)" }, { 0x6fc0, "WD Ethercard PLUS/A (WD8003E/A or WD8003ET/A)" }, diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/starfire_firmware.pl linux.ac/drivers/net/starfire_firmware.pl --- linux.vanilla/drivers/net/starfire_firmware.pl Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/net/starfire_firmware.pl Tue Apr 3 17:54:54 2001 @@ -0,0 +1,31 @@ +#!/usr/bin/perl + +# This script can be used to generate a new starfire_firmware.h +# from GFP_RX.DAT and GFP_TX.DAT, files included with the DDK +# and also with the Novell drivers. + +open FW, "GFP_RX.DAT" || die; +open FWH, ">starfire_firmware.h" || die; + +printf(FWH "static u32 firmware_rx[] = {\n"); +$counter = 0; +while ($foo = ) { + chomp; + printf(FWH " 0x%s, 0x0000%s,\n", substr($foo, 4, 8), substr($foo, 0, 4)); + $counter++; +} + +close FW; +open FW, "GFP_TX.DAT" || die; + +printf(FWH "};\t/* %d Rx instructions */\n#define FIRMWARE_RX_SIZE %d\n\nstatic u32 firmware_tx[] = {\n", $counter, $counter); +$counter = 0; +while ($foo = ) { + chomp; + printf(FWH " 0x%s, 0x0000%s,\n", substr($foo, 4, 8), substr($foo, 0, 4)); + $counter++; +} + +close FW; +printf(FWH "};\t/* %d Tx instructions */\n#define FIRMWARE_TX_SIZE %d\n", $counter, $counter); +close(FWH); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/sun3lance.c linux.ac/drivers/net/sun3lance.c --- linux.vanilla/drivers/net/sun3lance.c Mon Apr 30 15:13:18 2001 +++ linux.ac/drivers/net/sun3lance.c Sat May 26 19:26:20 2001 @@ -880,7 +880,7 @@ #ifdef MODULE -static char devicename[9] = { 0, }; +static char devicename[9]; static struct net_device sun3lance_dev = { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/tokenring/ibmtr.c linux.ac/drivers/net/tokenring/ibmtr.c --- linux.vanilla/drivers/net/tokenring/ibmtr.c Tue Apr 3 17:32:14 2001 +++ linux.ac/drivers/net/tokenring/ibmtr.c Thu May 17 21:03:37 2001 @@ -10,7 +10,8 @@ * I used Donald Becker's (becker@cesdis.gsfc.nasa.gov) device driver work * as a base for most of my initial work. * - * Changes by Peter De Schrijver (Peter.Deschrijver@linux.cc.kuleuven.ac.be) : + * Changes by Peter De Schrijver + * (Peter.Deschrijver@linux.cc.kuleuven.ac.be) : * * + changed name to ibmtr.c in anticipation of other tr boards. * + changed reset code and adapter open code. @@ -47,6 +48,7 @@ * + added -DPCMCIA to support PCMCIA * + detecting PCMCIA Card Removal in interrupt handler. If * ISRP is FF, then a PCMCIA card has been removed + * 10/2000 Burt needed a new method to avoid crashing the OS * * Changes by Paul Norton (pnorton@cts.com) : * + restructured the READ.LOG logic to prevent the transmit SRB @@ -83,7 +85,7 @@ * Changes by Jochen Friedrich to enable RFC1469 Option 2 multicasting * i.e. using functional address C0 00 00 04 00 00 to transmit and * receive multicast packets. - * + * * Changes by Mike Sullivan (based on original sram patch by Dave Grothe * to support windowing into on adapter shared ram. * i.e. Use LANAID to setup a PnP configuration with 16K RAM. Paging @@ -91,6 +93,13 @@ * * Changes by Peter De Schrijver (p2@mind.be) : * + fixed a problem with PCMCIA card removal + * + * Change by Mike Sullivan et al.: + * + added turbo card support. No need to use lanaid to configure + * the adapter into isa compatiblity mode. + * + * Changes by Burt Silverman to allow the computer to behave nicely when + * a cable is pulled or not in place, or a PCMCIA card is removed hot. */ /* change the define of IBMTR_DEBUG_MESSAGES to a nonzero value @@ -100,207 +109,247 @@ #include -#define NO_AUTODETECT 1 -#undef NO_AUTODETECT -/* #undef ENABLE_PAGING */ -#define ENABLE_PAGING 1 - +#ifdef PCMCIA +#undef MODULE +#undef ENABLE_PAGING +#else +#define ENABLE_PAGING 1 +#endif #define FALSE 0 #define TRUE (!FALSE) -/* changes the output format of driver initialisation */ -#define TR_NEWFORMAT 1 +/* changes the output format of driver initialization */ #define TR_VERBOSE 0 /* some 95 OS send many non UI frame; this allow removing the warning */ #define TR_FILTERNONUI 1 -/* version and credits */ -static char *version = -"ibmtr.c: v1.3.57 8/ 7/94 Peter De Schrijver and Mark Swanson\n" -" v2.1.125 10/20/98 Paul Norton \n" -" v2.2.0 12/30/98 Joel Sloan \n" -" v2.2.1 02/08/00 Mike Sullivan \n"; - -static char pcchannelid[] = { - 0x05, 0x00, 0x04, 0x09, - 0x04, 0x03, 0x04, 0x0f, - 0x03, 0x06, 0x03, 0x01, - 0x03, 0x01, 0x03, 0x00, - 0x03, 0x09, 0x03, 0x09, - 0x03, 0x00, 0x02, 0x00 -}; - -static char mcchannelid[] = { - 0x04, 0x0d, 0x04, 0x01, - 0x05, 0x02, 0x05, 0x03, - 0x03, 0x06, 0x03, 0x03, - 0x05, 0x08, 0x03, 0x04, - 0x03, 0x05, 0x03, 0x01, - 0x03, 0x08, 0x02, 0x00 -}; - -#include #include -#include -#include -#include #include -#include -#include -#include -#include #include #include -#include -#include -#include #include #include #include -#include -#include - - #define DPRINTK(format, args...) printk("%s: " format, dev->name , ## args) #define DPRINTD(format, args...) DummyCall("%s: " format, dev->name , ## args) #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) -#if TR_NEWFORMAT +/* version and credits */ +#ifndef PCMCIA +static char version[] __initdata = + "\nibmtr.c: v1.3.57 8/ 7/94 Peter De Schrijver and Mark Swanson\n" + " v2.1.125 10/20/98 Paul Norton \n" + " v2.2.0 12/30/98 Joel Sloan \n" + " v2.2.1 02/08/00 Mike Sullivan \n" + " v2.2.2 07/27/00 Burt Silverman \n" + " v2.4.0 03/01/01 Mike Sullivan \n"; +#endif + /* this allows displaying full adapter information */ -const char *channel_def[] __initdata = { - "ISA", "MCA", "ISA P&P" +char *channel_def[] __initdata = { "ISA", "MCA", "ISA P&P" }; + +static char pcchannelid[] __devinitdata = { + 0x05, 0x00, 0x04, 0x09, + 0x04, 0x03, 0x04, 0x0f, + 0x03, 0x06, 0x03, 0x01, + 0x03, 0x01, 0x03, 0x00, + 0x03, 0x09, 0x03, 0x09, + 0x03, 0x00, 0x02, 0x00 +}; + +static char mcchannelid[] __devinitdata = { + 0x04, 0x0d, 0x04, 0x01, + 0x05, 0x02, 0x05, 0x03, + 0x03, 0x06, 0x03, 0x03, + 0x05, 0x08, 0x03, 0x04, + 0x03, 0x05, 0x03, 0x01, + 0x03, 0x08, 0x02, 0x00 }; -char __init *adapter_def(char type) +char __devinit *adapter_def(char type) { - switch (type) - { - case 0xF : return "PC Adapter | PC Adapter II | Adapter/A"; - case 0xE : return "16/4 Adapter | 16/4 Adapter/A (long)"; - case 0xD : return "16/4 Adapter/A (short) | 16/4 ISA-16 Adapter"; - case 0xC : return "Auto 16/4 Adapter"; - default : return "adapter (unknown type)"; + switch (type) { + case 0xF: return "PC Adapter | PC Adapter II | Adapter/A"; + case 0xE: return "16/4 Adapter | 16/4 Adapter/A (long)"; + case 0xD: return "16/4 Adapter/A (short) | 16/4 ISA-16 Adapter"; + case 0xC: return "Auto 16/4 Adapter"; + default: return "adapter (unknown type)"; }; }; -#endif -#if !TR_NEWFORMAT -unsigned char ibmtr_debug_trace=1; /* Patch or otherwise alter to - control tokenring tracing. */ -#else -unsigned char ibmtr_debug_trace=0; -#endif -#define TRC_INIT 0x01 /* Trace initialization & PROBEs */ -#define TRC_INITV 0x02 /* verbose init trace points */ +#define TRC_INIT 0x01 /* Trace initialization & PROBEs */ +#define TRC_INITV 0x02 /* verbose init trace points */ +unsigned char ibmtr_debug_trace = 0; -int ibmtr_probe(struct net_device *dev); +int ibmtr_probe(struct net_device *dev); static int ibmtr_probe1(struct net_device *dev, int ioaddr); -static unsigned char get_sram_size(struct tok_info *adapt_info); -#ifdef PCMCIA -extern unsigned char pcmcia_reality_check(unsigned char gss); -#endif -static int tok_init_card(struct net_device *dev); -void tok_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static int trdev_init(struct net_device *dev); +static unsigned char get_sram_size(struct tok_info *adapt_info); +static int trdev_init(struct net_device *dev); +static int tok_open(struct net_device *dev); +static int tok_init_card(struct net_device *dev); +void tok_open_adapter(unsigned long dev_addr); +static void open_sap(unsigned char type, struct net_device *dev); +static void tok_set_multicast_list(struct net_device *dev); +static int tok_send_packet(struct sk_buff *skb, struct net_device *dev); +static int tok_close(struct net_device *dev); +void tok_interrupt(int irq, void *dev_id, struct pt_regs *regs); static void initial_tok_int(struct net_device *dev); -static void open_sap(unsigned char type,struct net_device *dev); -void tok_open_adapter(unsigned long dev_addr); -static void tr_rx(struct net_device *dev); -static void tr_tx(struct net_device *dev); -static int tok_open(struct net_device *dev); -static int tok_close(struct net_device *dev); -static int tok_send_packet(struct sk_buff *skb, struct net_device *dev); -static struct net_device_stats * tok_get_stats(struct net_device *dev); -static void tok_set_multicast_list(struct net_device *dev); -void ibmtr_readlog(struct net_device *dev); -void ibmtr_reset_timer(struct timer_list *tmr, struct net_device *dev); -int ibmtr_change_mtu(struct net_device *dev, int mtu); +static void tr_tx(struct net_device *dev); +static void tr_rx(struct net_device *dev); +void ibmtr_reset_timer(struct timer_list*tmr,struct net_device *dev); +static void tok_rerun(unsigned long dev_addr); +void ibmtr_readlog(struct net_device *dev); +static struct net_device_stats *tok_get_stats(struct net_device *dev); +int ibmtr_change_mtu(struct net_device *dev, int mtu); +static void find_turbo_adapters(int *iolist); -static unsigned int ibmtr_portlist[] __initdata = { - 0xa20, 0xa24, 0 +static int ibmtr_portlist[IBMTR_MAX_ADAPTERS+1] __devinitdata = { + 0xa20, 0xa24, 0, 0, 0 }; +static int __devinitdata turbo_io[IBMTR_MAX_ADAPTERS] = {0}; +static int __devinitdata turbo_irq[IBMTR_MAX_ADAPTERS] = {0}; +static int __devinitdata turbo_searched = 0; -static __u32 ibmtr_mem_base = 0xd0000; +#ifndef PCMCIA +static __u32 ibmtr_mem_base __initdata = 0xd0000; +#endif -static void __init PrtChanID(char *pcid, short stride) +static void __devinit PrtChanID(char *pcid, short stride) { short i, j; - for (i=0, j=0; i<24; i++, j+=stride) + for (i = 0, j = 0; i < 24; i++, j += stride) printk("%1x", ((int) pcid[j]) & 0x0f); printk("\n"); } -static void __init HWPrtChanID (__u32 pcid, short stride) +static void __devinit HWPrtChanID(__u32 pcid, short stride) { short i, j; - for (i=0, j=0; i<24; i++, j+=stride) - printk("%1x", ((int)isa_readb(pcid + j)) & 0x0f); + for (i = 0, j = 0; i < 24; i++, j += stride) + printk("%1x", ((int) readb(pcid + j)) & 0x0f); printk("\n"); } -/* +static void __devinit find_turbo_adapters(int *iolist) { + int ram_addr; + int index=0; + __u32 chanid; + int found_turbo=0; + unsigned char *tchanid, ctemp; + int i,j; + + if (turbo_searched == 1) return; + turbo_searched=1; + for (ram_addr=0xC0000; ram_addr < 0xE0000; ram_addr+=0x2000) { + + __u32 intf_tbl=0; + + found_turbo=1; + chanid=(CHANNEL_ID + ram_addr); + tchanid=pcchannelid; + ctemp=isa_readb(chanid) & 0x0f; + if (ctemp != *tchanid) continue; + for (i=2,j=1; i<=46; i=i+2,j++) { + if ((isa_readb(chanid+i) & 0x0f) != tchanid[j]){ + found_turbo=0; + break; + } + } + if (!found_turbo) continue; + + isa_writeb(0x90, ram_addr+0x1E01); + for(i=2; i<0x0f; i++) { + isa_writeb(0x00, ram_addr+0x1E01+i); + } + isa_writeb(0x00, ram_addr+0x1E01); + for(i=jiffies+TR_BUSY_INTERVAL; time_before_eq(jiffies,i);); + intf_tbl=ntohs(isa_readw(ram_addr+ACA_OFFSET+ACA_RW+WRBR_EVEN)); + if (intf_tbl) { +#if IBMTR_DEBUG_MESSAGES + printk("ibmtr::find_turbo_adapters, Turbo found at " + "ram_addr %x\n",ram_addr); + printk("ibmtr::find_turbo_adapters, interface_table "); + for(i=0; i<6; i++) { + printk("%x:",isa_readb(ram_addr+intf_tbl+i)); + } + printk("\n"); +#endif + turbo_io[index]=ntohs(isa_readw(ram_addr+intf_tbl+4)); + turbo_irq[index]=isa_readb(ram_addr+intf_tbl+3); + outb(0, turbo_io[index] + ADAPTRESET); + for(i=jiffies+TR_RST_TIME;time_before_eq(jiffies,i);); + outb(0, turbo_io[index] + ADAPTRESETREL); + index++; + continue; + } +#if IBMTR_DEBUG_MESSAGES + printk("ibmtr::find_turbo_adapters, ibmtr card found at" + " %x but not a Turbo model\n",ram_addr); +#endif + } + for(i=0; ibase_addr; + int i; + int base_addr = dev->base_addr; - if (base_addr > 0x1ff) - { - /* - * Check a single specified location. - */ - - if (ibmtr_probe1(dev, base_addr)) - return -ENODEV; - else - return 0; + if (base_addr && base_addr <= 0x1ff) /* Don't probe at all. */ + return -ENXIO; + if (base_addr > 0x1ff) { /* Check a single specified location. */ + if (!ibmtr_probe1(dev, base_addr)) return 0; + return -ENODEV; } - else if (base_addr != 0) /* Don't probe at all. */ - return -ENXIO; - - for (i = 0; ibmtr_portlist[i]; i++) - { - int ioaddr = ibmtr_portlist[i]; - if (check_region(ioaddr, IBMTR_IO_EXTENT)) - continue; - if (!ibmtr_probe1(dev, ioaddr)) - return 0; - } + find_turbo_adapters(ibmtr_portlist); + for (i = 0; ibmtr_portlist[i]; i++) { + int ioaddr = ibmtr_portlist[i]; - return -ENODEV; + if (check_region(ioaddr, IBMTR_IO_EXTENT)) continue; + if (!ibmtr_probe1(dev, ioaddr)) return 0; + } + return -ENODEV; } -static int __init ibmtr_probe1(struct net_device *dev, int PIOaddr) +/*****************************************************************************/ + +static int __devinit ibmtr_probe1(struct net_device *dev, int PIOaddr) { - unsigned char segment=0, intr=0, irq=0, i=0, j=0, cardpresent=NOTOK,temp=0; - __u32 t_mmio=0; - struct tok_info *ti=0; + + unsigned char segment, intr=0, irq=0, i, j, cardpresent=NOTOK, temp=0; + __u32 t_mmio = 0; + struct tok_info *ti = 0; __u32 cd_chanid; unsigned char *tchanid, ctemp; #ifndef PCMCIA - unsigned long timeout; + unsigned char t_irq=0; + unsigned long timeout; + static int version_printed; #endif #ifndef MODULE @@ -309,466 +358,414 @@ #endif #endif - /* Query the adapter PIO base port which will return - * indication of where MMIO was placed. We also have a - * coded interrupt number. - */ - - segment = inb(PIOaddr); - - /* - * Out of range values so we'll assume non-existent IO device + /* Query the adapter PIO base port which will return + * indication of where MMIO was placed. We also have a + * coded interrupt number. */ - - if (segment < 0x40 || segment > 0xe0) - return -ENODEV; - + segment = inb(PIOaddr); + if (segment < 0x40 || segment > 0xe0) { + /* Out of range values so we'll assume non-existent IO device + * but this is not necessarily a problem, esp if a turbo + * adapter is being used. */ +#if IBMTR_DEBUG_MESSAGES + DPRINTK("ibmtr_probe1(): unhappy that inb(0x%X) == 0x%X, " + "Hardware Problem?\n",PIOaddr,segment); +#endif + return -ENODEV; + } /* - * Compute the linear base address of the MMIO area - * as LINUX doesn't care about segments + * Compute the linear base address of the MMIO area + * as LINUX doesn't care about segments */ - - t_mmio=(((__u32)(segment & 0xfc) << 11) + 0x80000); - intr = segment & 0x03; /* low bits is coded interrupt # */ + t_mmio = (u32)ioremap(((__u32) (segment & 0xfc) << 11) + 0x80000,2048); + if (!t_mmio) { + DPRINTK("Cannot remap mmiobase memory area") ; + return -ENODEV ; + } + intr = segment & 0x03; /* low bits is coded interrupt # */ if (ibmtr_debug_trace & TRC_INIT) - DPRINTK("PIOaddr: %4hx seg/intr: %2x mmio base: %08X intr: %d\n", - PIOaddr, (int)segment, t_mmio, (int)intr); - - /* - * Now we will compare expected 'channelid' strings with - * what we is there to learn of ISA/MCA or not TR card - */ - - cd_chanid = (CHANNEL_ID + t_mmio); /* for efficiency */ - tchanid=pcchannelid; - cardpresent=TR_ISA; /* try ISA */ + DPRINTK("PIOaddr: %4hx seg/intr: %2x mmio base: %08X intr: %d\n" + , PIOaddr, (int) segment, t_mmio, (int) intr); /* - * Suboptimize knowing first byte different + * Now we will compare expected 'channelid' strings with + * what we is there to learn of ISA/MCA or not TR card */ - - ctemp = isa_readb(cd_chanid) & 0x0f; - if (ctemp != *tchanid) { /* NOT ISA card, try MCA */ - tchanid=mcchannelid; - cardpresent=TR_MCA; - if (ctemp != *tchanid) /* Neither ISA nor MCA */ - cardpresent=NOTOK; - } - - if (cardpresent != NOTOK) - { - /* - * Know presumed type, try rest of ID - */ - for (i=2,j=1; i<=46; i=i+2,j++) - { - if ((isa_readb(cd_chanid+i) & 0x0f) != tchanid[j]) { - cardpresent=NOTOK; /* match failed, not TR card */ - break; - } +#ifdef PCMCIA + ti = dev->priv; /*BMS moved up here */ + t_mmio = ti->mmio; /*BMS to get virtual address */ + irq = ti->irq; /*BMS to display the irq! */ +#endif + cd_chanid = (CHANNEL_ID + t_mmio); /* for efficiency */ + tchanid = pcchannelid; + cardpresent = TR_ISA; /* try ISA */ + + /* Suboptimize knowing first byte different */ + ctemp = readb(cd_chanid) & 0x0f; + if (ctemp != *tchanid) { /* NOT ISA card, try MCA */ + tchanid = mcchannelid; + cardpresent = TR_MCA; + if (ctemp != *tchanid) /* Neither ISA nor MCA */ + cardpresent = NOTOK; + } + if (cardpresent != NOTOK) { + /* Know presumed type, try rest of ID */ + for (i = 2, j = 1; i <= 46; i = i + 2, j++) { + if( (readb(cd_chanid+i)&0x0f) == tchanid[j]) continue; + /* match failed, not TR card */ + cardpresent = NOTOK; + break; } } - /* - * If we have an ISA board check for the ISA P&P version, - * as it has different IRQ settings + * If we have an ISA board check for the ISA P&P version, + * as it has different IRQ settings */ - - if (cardpresent == TR_ISA && (isa_readb(AIPFID + t_mmio)==0x0e)) - cardpresent=TR_ISAPNP; - - if (cardpresent == NOTOK) { /* "channel_id" did not match, report */ - if (ibmtr_debug_trace & TRC_INIT) { - DPRINTK("Channel ID string not found for PIOaddr: %4hx\n", PIOaddr); - DPRINTK("Expected for ISA: "); PrtChanID(pcchannelid,1); - DPRINTK(" found: "); HWPrtChanID(cd_chanid,2); - DPRINTK("Expected for MCA: "); PrtChanID(mcchannelid,1); - } - return -ENODEV; + if (cardpresent == TR_ISA && (readb(AIPFID + t_mmio) == 0x0e)) + cardpresent = TR_ISAPNP; + if (cardpresent == NOTOK) { /* "channel_id" did not match, report */ + if (!(ibmtr_debug_trace & TRC_INIT)) return -ENODEV; + DPRINTK( "Channel ID string not found for PIOaddr: %4hx\n", + PIOaddr); + DPRINTK("Expected for ISA: "); + PrtChanID(pcchannelid, 1); + DPRINTK(" found: "); +/* BMS Note that this can be misleading, when hardware is flaky, because you + are reading it a second time here. So with my flaky hardware, I'll see my- + self in this block, with the HW ID matching the ISA ID exactly! */ + HWPrtChanID(cd_chanid, 2); + DPRINTK("Expected for MCA: "); + PrtChanID(mcchannelid, 1); } - /* Now, allocate some of the pl0 buffers for this driver.. */ - - /* If called from PCMCIA, ti is already set up, so no need to + /* If called from PCMCIA, it is already set up, so no need to waste the memory, just use the existing structure */ - #ifndef PCMCIA - ti = (struct tok_info *)kmalloc(sizeof(struct tok_info), GFP_KERNEL); - if (ti == NULL) - return -ENOMEM; - + ti = (struct tok_info *) kmalloc(sizeof(struct tok_info), GFP_KERNEL); + if (ti == NULL) return -ENOMEM; memset(ti, 0, sizeof(struct tok_info)); -#else - ti = dev->priv ; + ti->mmio = t_mmio; + dev->priv = ti; /* this seems like the logical use of the + field ... let's try some empirical tests + using the token-info structure -- that + should fit with out future hope of multiple + adapter support as well /dwm */ + for(i=0; iturbo=1; + t_irq=turbo_irq[i]; + } #endif - ti->mmio= t_mmio; ti->readlog_pending = 0; - init_waitqueue_head(&ti->wait_for_tok_int); init_waitqueue_head(&ti->wait_for_reset); - dev->priv = ti; /* this seems like the logical use of the - field ... let's try some empirical tests - using the token-info structure -- that - should fit with out future hope of multiple - adapter support as well /dwm */ - - /* if PCMCIA, then the card is recognized as TR_ISAPNP - * and there is no need to set up the interrupt, it is already done. */ - + /* if PCMCIA, the card can be recognized as either TR_ISA or TR_ISAPNP + * depending which card is inserted. */ + #ifndef PCMCIA - switch (cardpresent) - { - case TR_ISA: - if (intr==0) - irq=9; /* irq2 really is irq9 */ - if (intr==1) - irq=3; - if (intr==2) - irq=6; - if (intr==3) - irq=7; - ti->global_int_enable=GLOBAL_INT_ENABLE+((irq==9) ? 2 : irq); - ti->adapter_int_enable=PIOaddr+ADAPTINTREL; - ti->sram=0; -#if !TR_NEWFORMAT - DPRINTK("ti->global_int_enable: %04X\n",ti->global_int_enable); -#endif - break; - case TR_MCA: - if (intr==0) - irq=9; - if (intr==1) - irq=3; - if (intr==2) - irq=10; - if (intr==3) - irq=11; - ti->global_int_enable=0; - ti->adapter_int_enable=0; - ti->sram=((__u32)(inb(PIOaddr+ADAPTRESETREL) & 0xfe) << 12); - break; - case TR_ISAPNP: - if (intr==0) - irq=9; - if (intr==1) - irq=3; - if (intr==2) - irq=10; - if (intr==3) - irq=11; - timeout = jiffies + TR_SPIN_INTERVAL; - while(!isa_readb(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)) - if (time_after(jiffies, timeout)) { - DPRINTK("Hardware timeout during initialization.\n"); - kfree(ti); - return -ENODEV; - } - - ti->sram=((__u32)isa_readb(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)<<12); - ti->global_int_enable=PIOaddr+ADAPTINTREL; - ti->adapter_int_enable=PIOaddr+ADAPTINTREL; - break; - } -#endif + switch (cardpresent) { + case TR_ISA: + if (intr == 0) irq = 9; /* irq2 really is irq9 */ + if (intr == 1) irq = 3; + if (intr == 2) irq = 6; + if (intr == 3) irq = 7; + ti->adapter_int_enable = PIOaddr + ADAPTINTREL; + break; + case TR_MCA: + if (intr == 0) irq = 9; + if (intr == 1) irq = 3; + if (intr == 2) irq = 10; + if (intr == 3) irq = 11; + ti->global_int_enable = 0; + ti->adapter_int_enable = 0; + ti->sram_virt=(__u32)(inb(PIOaddr+ADAPTRESETREL) & 0xfe) << 12; + break; + case TR_ISAPNP: + if (!t_irq) { + if (intr == 0) irq = 9; + if (intr == 1) irq = 3; + if (intr == 2) irq = 10; + if (intr == 3) irq = 11; + } else + irq=t_irq; + timeout = jiffies + TR_SPIN_INTERVAL; + while (!readb(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)){ + if (!time_after(jiffies, timeout)) continue; + DPRINTK( "Hardware timeout during initialization.\n"); + kfree(ti); + return -ENODEV; + } + ti->sram_virt = + ((__u32)readb(ti->mmio+ACA_OFFSET+ACA_RW+RRR_EVEN)<<12); + ti->adapter_int_enable = PIOaddr + ADAPTINTREL; + break; + } /*end switch (cardpresent) */ +#endif /*not PCMCIA */ - if (ibmtr_debug_trace & TRC_INIT) { /* just report int */ - DPRINTK("irq=%d",irq); - if (ibmtr_debug_trace & TRC_INITV) { /* full chat in verbose only */ - DPRINTK(", ti->mmio=%08X",ti->mmio); - printk(", segment=%02X",segment); + if (ibmtr_debug_trace & TRC_INIT) { /* just report int */ + DPRINTK("irq=%d", irq); + printk(", sram_virt=0x%x", ti->sram_virt); + if(ibmtr_debug_trace&TRC_INITV){ /* full chat in verbose only */ + DPRINTK(", ti->mmio=%08X", ti->mmio); + printk(", segment=%02X", segment); } printk(".\n"); } /* Get hw address of token ring card */ -#if !TR_NEWFORMAT - DPRINTK("hw address: "); -#endif - j=0; - for (i=0; i<0x18; i=i+2) - { + j = 0; + for (i = 0; i < 0x18; i = i + 2) { /* technical reference states to do this */ - temp = isa_readb(ti->mmio + AIP + i) & 0x0f; -#if !TR_NEWFORMAT - printk("%1X",ti->hw_address[j]=temp); -#else - ti->hw_address[j]=temp; -#endif - if(j&1) - dev->dev_addr[(j/2)]=ti->hw_address[j]+(ti->hw_address[j-1]<<4); + temp = readb(ti->mmio + AIP + i) & 0x0f; + ti->hw_address[j] = temp; + if (j & 1) + dev->dev_addr[(j / 2)] = + ti->hw_address[j]+ (ti->hw_address[j - 1] << 4); ++j; } -#ifndef TR_NEWFORMAT - printk("\n"); -#endif - - /* get Adapter type: 'F' = Adapter/A, 'E' = 16/4 Adapter II,...*/ - ti->adapter_type = isa_readb(ti->mmio + AIPADAPTYPE); + /* get Adapter type: 'F' = Adapter/A, 'E' = 16/4 Adapter II,... */ + ti->adapter_type = readb(ti->mmio + AIPADAPTYPE); /* get Data Rate: F=4Mb, E=16Mb, D=4Mb & 16Mb ?? */ - ti->data_rate = isa_readb(ti->mmio + AIPDATARATE); + ti->data_rate = readb(ti->mmio + AIPDATARATE); /* Get Early Token Release support?: F=no, E=4Mb, D=16Mb, C=4&16Mb */ - ti->token_release = isa_readb(ti->mmio + AIPEARLYTOKEN); + ti->token_release = readb(ti->mmio + AIPEARLYTOKEN); /* How much shared RAM is on adapter ? */ -#ifdef PCMCIA - ti->avail_shared_ram = pcmcia_reality_check(get_sram_size(ti)); - ibmtr_mem_base = ti->sram_base << 12 ; -#else - ti->avail_shared_ram = get_sram_size(ti); -#endif - /* We need to set or do a bunch of work here based on previous results.. */ + if (ti->turbo) { + ti->avail_shared_ram=127; + } else { + ti->avail_shared_ram = get_sram_size(ti);/*in 512 byte units */ + } + /* We need to set or do a bunch of work here based on previous results*/ /* Support paging? What sizes?: F=no, E=16k, D=32k, C=16 & 32k */ - ti->shared_ram_paging = isa_readb(ti->mmio + AIPSHRAMPAGE); + ti->shared_ram_paging = readb(ti->mmio + AIPSHRAMPAGE); - /* Available DHB 4Mb size: F=2048, E=4096, D=4464 */ - switch (isa_readb(ti->mmio + AIP4MBDHB)) { - case 0xe : - ti->dhb_size4mb = 4096; - break; - case 0xd : - ti->dhb_size4mb = 4464; - break; - default : - ti->dhb_size4mb = 2048; - break; + /* Available DHB 4Mb size: F=2048, E=4096, D=4464 */ + switch (readb(ti->mmio + AIP4MBDHB)) { + case 0xe: ti->dhb_size4mb = 4096; break; + case 0xd: ti->dhb_size4mb = 4464; break; + default: ti->dhb_size4mb = 2048; break; } /* Available DHB 16Mb size: F=2048, E=4096, D=8192, C=16384, B=17960 */ - switch (isa_readb(ti->mmio + AIP16MBDHB)) { - case 0xe : - ti->dhb_size16mb = 4096; - break; - case 0xd : - ti->dhb_size16mb = 8192; - break; - case 0xc : - ti->dhb_size16mb = 16384; - break; - case 0xb : - ti->dhb_size16mb = 17960; - break; - default : - ti->dhb_size16mb = 2048; - break; + switch (readb(ti->mmio + AIP16MBDHB)) { + case 0xe: ti->dhb_size16mb = 4096; break; + case 0xd: ti->dhb_size16mb = 8192; break; + case 0xc: ti->dhb_size16mb = 16384; break; + case 0xb: ti->dhb_size16mb = 17960; break; + default: ti->dhb_size16mb = 2048; break; } -#if !TR_NEWFORMAT - DPRINTK("atype=%x, drate=%x, trel=%x, asram=%dK, srp=%x, " - "dhb(4mb=%x, 16mb=%x)\n",ti->adapter_type, - ti->data_rate, ti->token_release, ti->avail_shared_ram/2, - ti->shared_ram_paging, ti->dhb_size4mb, ti->dhb_size16mb); -#endif - - /* We must figure out how much shared memory space this adapter - * will occupy so that if there are two adapters we can fit both - * in. Given a choice, we will limit this adapter to 32K. The - * maximum space will will use for two adapters is 64K so if the - * adapter we are working on demands 64K (it also doesn't support - * paging), then only one adapter can be supported. + /* We must figure out how much shared memory space this adapter + * will occupy so that if there are two adapters we can fit both + * in. Given a choice, we will limit this adapter to 32K. The + * maximum space will will use for two adapters is 64K so if the + * adapter we are working on demands 64K (it also doesn't support + * paging), then only one adapter can be supported. */ /* - * determine how much of total RAM is mapped into PC space + * determine how much of total RAM is mapped into PC space */ - ti->mapped_ram_size=1<<((((isa_readb(ti->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD)) >>2) & 0x03) + 4); - ti->page_mask=0; - if (ti->shared_ram_paging == 0xf) { /* No paging in adapter */ - ti->mapped_ram_size = ti->avail_shared_ram; - } else { -#ifdef ENABLE_PAGING - unsigned char pg_size=0; -#endif - -#if !TR_NEWFORMAT - DPRINTK("shared ram page size: %dK\n",ti->mapped_ram_size/2); -#endif + ti->mapped_ram_size= /*sixteen to onehundredtwentyeight 512byte blocks*/ + 1<< ((readb(ti->mmio+ACA_OFFSET+ACA_RW+RRR_ODD) >> 2 & 0x03) + 4); + ti->page_mask = 0; + if (ti->turbo) ti->page_mask=0xf0; + else if (ti->shared_ram_paging == 0xf); /* No paging in adapter */ + else { #ifdef ENABLE_PAGING - switch(ti->shared_ram_paging) - { + unsigned char pg_size = 0; + /* BMS: page size: PCMCIA, use configuration register; + ISAPNP, use LANAIDC config tool from www.ibm.com */ + switch (ti->shared_ram_paging) { case 0xf: break; case 0xe: - ti->page_mask=(ti->mapped_ram_size==32) ? 0xc0 : 0; - pg_size=32; /* 16KB page size */ + ti->page_mask = (ti->mapped_ram_size == 32) ? 0xc0 : 0; + pg_size = 32; /* 16KB page size */ break; case 0xd: - ti->page_mask=(ti->mapped_ram_size==64) ? 0x80 : 0; - pg_size=64; /* 32KB page size */ + ti->page_mask = (ti->mapped_ram_size == 64) ? 0x80 : 0; + pg_size = 64; /* 32KB page size */ break; case 0xc: - switch (ti->mapped_ram_size) { - case 32: - ti->page_mask=0xc0; - pg_size=32; - break; - case 64: - ti->page_mask=0x80; - pg_size=64; - break; - } + switch (ti->mapped_ram_size) { + case 32: + ti->page_mask = 0xc0; + pg_size = 32; + break; + case 64: + ti->page_mask = 0x80; + pg_size = 64; + break; + } break; default: - DPRINTK("Unknown shared ram paging info %01X\n",ti->shared_ram_paging); + DPRINTK("Unknown shared ram paging info %01X\n", + ti->shared_ram_paging); kfree(ti); return -ENODEV; break; - } + } /*end switch shared_ram_paging */ - if (ibmtr_debug_trace & TRC_INIT) - DPRINTK("Shared RAM paging code: " - "%02X mapped RAM size: %dK shared RAM size: %dK page mask: %0xX\n:", - ti->shared_ram_paging, ti->mapped_ram_size/2, ti->avail_shared_ram/2, ti->page_mask); - - if (ti->page_mask) { - if (pg_size > ti->mapped_ram_size) { - DPRINTK("Page size (%d) > mapped ram window (%d), can't page.\n", - pg_size/2, ti->mapped_ram_size/2); - ti->page_mask = 0; /* reset paging */ - } - } else if (pg_size > ti->mapped_ram_size) { - DPRINTK("Page size (%d) > mapped ram window (%d), can't page.\n", - pg_size/2, ti->mapped_ram_size/2); - } -#endif + if (ibmtr_debug_trace & TRC_INIT) + DPRINTK("Shared RAM paging code: %02X, " + "mapped RAM size: %dK, shared RAM size: %dK, " + "page mask: %02X\n:", + ti->shared_ram_paging, ti->mapped_ram_size / 2, + ti->avail_shared_ram / 2, ti->page_mask); +#endif /*ENABLE_PAGING */ } + +#ifndef PCMCIA /* finish figuring the shared RAM address */ - if (cardpresent==TR_ISA) { - static __u32 ram_bndry_mask[]={0xffffe000, 0xffffc000, 0xffff8000, 0xffff0000}; + if (cardpresent == TR_ISA) { + static __u32 ram_bndry_mask[] = + { 0xffffe000, 0xffffc000, 0xffff8000, 0xffff0000 }; __u32 new_base, rrr_32, chk_base, rbm; - rrr_32 = ((isa_readb(ti->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD))>>2) & 0x00000003; + rrr_32=readb(ti->mmio+ACA_OFFSET+ACA_RW+RRR_ODD) >> 2 & 0x03; rbm = ram_bndry_mask[rrr_32]; - new_base = (ibmtr_mem_base + (~rbm)) & rbm; /* up to boundary */ - chk_base = new_base + (ti->mapped_ram_size<<9); + new_base = (ibmtr_mem_base + (~rbm)) & rbm;/* up to boundary */ + chk_base = new_base + (ti->mapped_ram_size << 9); if (chk_base > (ibmtr_mem_base + IBMTR_SHARED_RAM_SIZE)) { - DPRINTK("Shared RAM for this adapter (%05x) exceeds driver" - " limit (%05x), adapter not started.\n", - chk_base, ibmtr_mem_base + IBMTR_SHARED_RAM_SIZE); + DPRINTK("Shared RAM for this adapter (%05x) exceeds " + "driver limit (%05x), adapter not started.\n", + chk_base, ibmtr_mem_base + IBMTR_SHARED_RAM_SIZE); kfree(ti); - return -ENODEV; - } else { /* seems cool, record what we have figured out */ + return -ENODEV; + } else { /* seems cool, record what we have figured out */ ti->sram_base = new_base >> 12; ibmtr_mem_base = chk_base; } } - -#if !TR_NEWFORMAT - DPRINTK("Using %dK shared RAM\n",ti->mapped_ram_size/2); -#endif + else ti->sram_base = ti->sram_virt >> 12; /* The PCMCIA has already got the interrupt line and the io port, so no chance of anybody else getting it - MLP */ - -#ifndef PCMCIA - if (request_irq (dev->irq = irq, &tok_interrupt,0,"ibmtr", dev) != 0) { - DPRINTK("Could not grab irq %d. Halting Token Ring driver.\n",irq); + if (request_irq(dev->irq = irq, &tok_interrupt, 0, "ibmtr", dev) != 0) { + DPRINTK("Could not grab irq %d. Halting Token Ring driver.\n", + irq); kfree(ti); return -ENODEV; } - /*?? Now, allocate some of the PIO PORTs for this driver.. */ - request_region(PIOaddr,IBMTR_IO_EXTENT,"ibmtr"); /* record PIOaddr range as busy */ + /* record PIOaddr range as busy */ + request_region(PIOaddr, IBMTR_IO_EXTENT, "ibmtr"); + if (!version_printed++) { + printk(version); + } #endif - -#if !TR_NEWFORMAT - DPRINTK("%s",version); /* As we have passed card identification, - let the world know we're here! */ -#else - - if (version) { - printk("%s",version); - version = NULL; - } DPRINTK("%s %s found\n", - channel_def[cardpresent-1], adapter_def(ti->adapter_type)); + channel_def[cardpresent - 1], adapter_def(ti->adapter_type)); DPRINTK("using irq %d, PIOaddr %hx, %dK shared RAM.\n", - irq, PIOaddr, ti->mapped_ram_size/2); + irq, PIOaddr, ti->mapped_ram_size / 2); DPRINTK("Hardware address : %02X:%02X:%02X:%02X:%02X:%02X\n", dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); - if (ti->page_mask) - DPRINTK("Shared RAM paging enabled. Page size: %uK Shared Ram size %dK\n", - ((ti->page_mask ^ 0xff)+1)>>2,ti->avail_shared_ram/2); - else - DPRINTK("Shared RAM paging disabled. ti->page_mask %x\n",ti->page_mask); -#endif + if (ti->page_mask) + DPRINTK("Shared RAM paging enabled. " + "Page size: %uK Shared Ram size %dK\n", + ((ti->page_mask^0xff)+1) >>2, ti->avail_shared_ram / 2); + else + DPRINTK("Shared RAM paging disabled. ti->page_mask %x\n", + ti->page_mask); + /* Calculate the maximum DHB we can use */ + /* two cases where avail_shared_ram doesn't equal mapped_ram_size: + 1. avail_shared_ram is 127 but mapped_ram_size is 128 (typical) + 2. user has configured adapter for less than avail_shared_ram + but is not using paging (she should use paging, I believe) + */ if (!ti->page_mask) { - ti->avail_shared_ram=ti->mapped_ram_size; + ti->avail_shared_ram= + MIN(ti->mapped_ram_size,ti->avail_shared_ram); } + switch (ti->avail_shared_ram) { - case 16 : /* 8KB shared RAM */ - ti->dhb_size4mb = MIN(ti->dhb_size4mb, 2048); + case 16: /* 8KB shared RAM */ + ti->dhb_size4mb = MIN(ti->dhb_size4mb, 2048); ti->rbuf_len4 = 1032; - ti->rbuf_cnt4 = 2; + ti->rbuf_cnt4=2; ti->dhb_size16mb = MIN(ti->dhb_size16mb, 2048); ti->rbuf_len16 = 1032; - ti->rbuf_cnt16 = 2; + ti->rbuf_cnt16=2; break; - case 32 : /* 16KB shared RAM */ - ti->dhb_size4mb = MIN(ti->dhb_size4mb, 2048); - ti->rbuf_len4 = 520; - ti->rbuf_cnt4 = 9; - ti->dhb_size16mb = MIN(ti->dhb_size16mb, 2048); - ti->rbuf_len16 = 1032; /* 1024 usable */ - ti->rbuf_cnt16 = 4; + case 32: /* 16KB shared RAM */ + ti->dhb_size4mb = MIN(ti->dhb_size4mb, 4464); + ti->rbuf_len4 = 1032; + ti->rbuf_cnt4=4; + ti->dhb_size16mb = MIN(ti->dhb_size16mb, 4096); + ti->rbuf_len16 = 1032; /*1024 usable */ + ti->rbuf_cnt16=4; break; - case 64 : /* 32KB shared RAM */ - ti->dhb_size4mb = MIN(ti->dhb_size4mb, 2048); + case 64: /* 32KB shared RAM */ + ti->dhb_size4mb = MIN(ti->dhb_size4mb, 4464); ti->rbuf_len4 = 1032; - ti->rbuf_cnt4 = 6; - ti->dhb_size16mb = MIN(ti->dhb_size16mb, 2048); + ti->rbuf_cnt4=6; + ti->dhb_size16mb = MIN(ti->dhb_size16mb, 10240); ti->rbuf_len16 = 1032; - ti->rbuf_cnt16 = 10; + ti->rbuf_cnt16=6; break; - case 127 : /* 63KB shared RAM */ - ti->dhb_size4mb = MIN(ti->dhb_size4mb, 2048); + case 127: /* 63.5KB shared RAM */ + ti->dhb_size4mb = MIN(ti->dhb_size4mb, 4464); ti->rbuf_len4 = 1032; - ti->rbuf_cnt4 = 6; - ti->dhb_size16mb = MIN(ti->dhb_size16mb, 2048); + ti->rbuf_cnt4=6; + ti->dhb_size16mb = MIN(ti->dhb_size16mb, 16384); ti->rbuf_len16 = 1032; - ti->rbuf_cnt16 = 16; + ti->rbuf_cnt16=16; break; - case 128 : /* 64KB shared RAM */ - ti->dhb_size4mb = MIN(ti->dhb_size4mb, 2048); + case 128: /* 64KB shared RAM */ + ti->dhb_size4mb = MIN(ti->dhb_size4mb, 4464); ti->rbuf_len4 = 1032; - ti->rbuf_cnt4 = 6; - ti->dhb_size16mb = MIN(ti->dhb_size16mb, 2048); + ti->rbuf_cnt4=6; + ti->dhb_size16mb = MIN(ti->dhb_size16mb, 17960); ti->rbuf_len16 = 1032; - ti->rbuf_cnt16 = 18; + ti->rbuf_cnt16=16; break; - default : - ti->dhb_size4mb = 2048; + default: + ti->dhb_size4mb = 2048; ti->rbuf_len4 = 1032; - ti->rbuf_cnt4 = 2; + ti->rbuf_cnt4=2; ti->dhb_size16mb = 2048; ti->rbuf_len16 = 1032; - ti->rbuf_cnt16 = 2; + ti->rbuf_cnt16=2; break; } - - ti->maxmtu16 = (ti->rbuf_len16*ti->rbuf_cnt16)-((ti->rbuf_cnt16)<<3)-TR_HLEN; - ti->maxmtu4 = (ti->rbuf_len4*ti->rbuf_cnt4)-((ti->rbuf_cnt4)<<3)-TR_HLEN; - DPRINTK("Maximum MTU 16Mbps: %d, 4Mbps: %d\n", - ti->maxmtu16, ti->maxmtu4); - - dev->base_addr=PIOaddr; /* set the value for device */ - + /* this formula is not smart enough for the paging case + ti->rbuf_cnt = (ti->avail_shared_ram * BLOCKSZ - ADAPT_PRIVATE - + ARBLENGTH - SSBLENGTH - DLC_MAX_SAP * SAPLENGTH - + DLC_MAX_STA * STALENGTH - ti->dhb_sizemb * NUM_DHB - + SRBLENGTH - ASBLENGTH) / ti->rbuf_len; + */ + ti->maxmtu16 = (ti->rbuf_len16 - 8) * ti->rbuf_cnt16 - TR_HLEN; + ti->maxmtu4 = (ti->rbuf_len4 - 8) * ti->rbuf_cnt4 - TR_HLEN; + /*BMS assuming 18 bytes of Routing Information (usually works) */ + DPRINTK("Maximum Receive Internet Protocol MTU 16Mbps: %d, 4Mbps: %d\n", + ti->maxmtu16, ti->maxmtu4); + + dev->base_addr = PIOaddr; /* set the value for device */ + dev->mem_start = ti->sram_base << 12; + dev->mem_end = dev->mem_start + (ti->mapped_ram_size << 9) - 1; trdev_init(dev); - tok_init_card(dev); + return 0; /* Return 0 to indicate we have found a Token Ring card. */ +} /*ibmtr_probe1() */ - return 0; /* Return 0 to indicate we have found a Token Ring card. */ -} +/*****************************************************************************/ /* query the adapter for the size of shared RAM */ +/* the function returns the RAM size in units of 512 bytes */ -static unsigned char __init get_sram_size(struct tok_info *adapt_info) +static unsigned char __devinit get_sram_size(struct tok_info *adapt_info) { - unsigned char avail_sram_code; - static unsigned char size_code[]={ 0,16,32,64,127,128 }; + static unsigned char size_code[] = { 0, 16, 32, 64, 127, 128 }; /* Adapter gives 'F' -- use RRR bits 3,2 'E' -- 8kb 'D' -- 16kb @@ -776,30 +773,27 @@ 'B' - 64KB less 512 bytes at top (WARNING ... must zero top bytes in INIT */ - avail_sram_code=0xf-isa_readb(adapt_info->mmio + AIPAVAILSHRAM); - if (avail_sram_code) - return size_code[avail_sram_code]; - else /* for code 'F', must compute size from RRR(3,2) bits */ - return 1<<((isa_readb(adapt_info->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD)>>2)+4); + avail_sram_code = 0xf - readb(adapt_info->mmio + AIPAVAILSHRAM); + if (avail_sram_code) return size_code[avail_sram_code]; + else /* for code 'F', must compute size from RRR(3,2) bits */ + return 1 << + ((readb(adapt_info->mmio+ACA_OFFSET+ACA_RW+RRR_ODD)>>2&3)+4); } -static int __init trdev_init(struct net_device *dev) -{ - struct tok_info *ti=(struct tok_info *)dev->priv; +/*****************************************************************************/ - /* init the spinlock */ - spin_lock_init(&ti->lock); +static int __devinit trdev_init(struct net_device *dev) +{ + struct tok_info *ti = (struct tok_info *) dev->priv; SET_PAGE(ti->srb_page); - ti->open_status = CLOSED; - - dev->init = tok_init_card; - dev->open = tok_open; - dev->stop = tok_close; - dev->hard_start_xmit = tok_send_packet; - dev->get_stats = tok_get_stats; + ti->open_failure = NO ; + dev->open = tok_open; + dev->stop = tok_close; + dev->hard_start_xmit = tok_send_packet; + dev->get_stats = tok_get_stats; dev->set_multicast_list = tok_set_multicast_list; - dev->change_mtu = ibmtr_change_mtu; + dev->change_mtu = ibmtr_change_mtu; #ifndef MODULE #ifndef PCMCIA @@ -809,23 +803,173 @@ return 0; } +/*****************************************************************************/ + +static int tok_init_card(struct net_device *dev) +{ + struct tok_info *ti; + short PIOaddr; + unsigned long i; + + PIOaddr = dev->base_addr; + ti = (struct tok_info *) dev->priv; + /* Special processing for first interrupt after reset */ + ti->do_tok_int = FIRST_INT; + /* Reset adapter */ + writeb(~INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN); + outb(0, PIOaddr + ADAPTRESET); + + current->state=TASK_UNINTERRUPTIBLE; + schedule_timeout(TR_RST_TIME); /* wait 50ms */ + + outb(0, PIOaddr + ADAPTRESETREL); +#ifdef ENABLE_PAGING + if (ti->page_mask) + writeb(SRPR_ENABLE_PAGING,ti->mmio+ACA_OFFSET+ACA_RW+SRPR_EVEN); +#endif + writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); + i = sleep_on_timeout(&ti->wait_for_reset, 4 * HZ); + return i? 0 : -EAGAIN; +} + +/*****************************************************************************/ +static int tok_open(struct net_device *dev) +{ + struct tok_info *ti = (struct tok_info *) dev->priv; + int i; + + /*the case we were left in a failure state during a previous open */ + if (ti->open_failure == YES) { + DPRINTK("Last time you were disconnected, how about now?\n"); + printk("You can't insert with an ICS connector half-cocked.\n"); + } + + ti->open_status = CLOSED; /* CLOSED or OPEN */ + ti->sap_status = CLOSED; /* CLOSED or OPEN */ + ti->open_failure = NO; /* NO or YES */ + ti->open_mode = MANUAL; /* MANUAL or AUTOMATIC */ + /* 12/2000 not typical Linux, but we can use RUNNING to let us know when + the network has crapped out or cables are disconnected. Useful because + the IFF_UP flag stays up the whole time, until ifconfig tr0 down. + */ + dev->flags &= ~IFF_RUNNING; + + ti->sram_virt &= ~1; /* to reverse what we do in tok_close */ + /* init the spinlock */ + ti->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; + + i = tok_init_card(dev); + if (i) return i; + + while (1){ + tok_open_adapter((unsigned long) dev); + i= interruptible_sleep_on_timeout(&ti->wait_for_reset, 25 * HZ); + /* sig catch: estimate opening adapter takes more than .5 sec*/ + if (i>(245*HZ)/10) break; /* fancier than if (i==25*HZ) */ + if (i==0) break; + if (ti->open_status == OPEN && ti->sap_status==OPEN) { + netif_start_queue(dev); + MOD_INC_USE_COUNT; + DPRINTK("Adapter is up and running\n"); + return 0; + } + current->state=TASK_INTERRUPTIBLE; + i=schedule_timeout(TR_RETRY_INTERVAL); /* wait 30 seconds */ + if(i!=0) break; /*prob. a signal, like the i>24*HZ case above */ + } + outb(0, dev->base_addr + ADAPTRESET);/* kill pending interrupts*/ + DPRINTK("TERMINATED via signal\n"); /*BMS useful */ + return -EAGAIN; +} + +/*****************************************************************************/ + +#define COMMAND_OFST 0 +#define OPEN_OPTIONS_OFST 8 +#define NUM_RCV_BUF_OFST 24 +#define RCV_BUF_LEN_OFST 26 +#define DHB_LENGTH_OFST 28 +#define NUM_DHB_OFST 30 +#define DLC_MAX_SAP_OFST 32 +#define DLC_MAX_STA_OFST 33 + +void tok_open_adapter(unsigned long dev_addr) +{ + struct net_device *dev = (struct net_device *) dev_addr; + struct tok_info *ti; + int i; + + ti = (struct tok_info *) dev->priv; + SET_PAGE(ti->init_srb_page); + writeb(~SRB_RESP_INT, ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD); + for (i = 0; i < sizeof(struct dir_open_adapter); i++) + writeb(0, ti->init_srb + i); + writeb(DIR_OPEN_ADAPTER, ti->init_srb + COMMAND_OFST); + writew(htons(OPEN_PASS_BCON_MAC), ti->init_srb + OPEN_OPTIONS_OFST); + if (ti->ring_speed == 16) { + writew(htons(ti->dhb_size16mb), ti->init_srb + DHB_LENGTH_OFST); + writew(htons(ti->rbuf_cnt16), ti->init_srb + NUM_RCV_BUF_OFST); + writew(htons(ti->rbuf_len16), ti->init_srb + RCV_BUF_LEN_OFST); + } else { + writew(htons(ti->dhb_size4mb), ti->init_srb + DHB_LENGTH_OFST); + writew(htons(ti->rbuf_cnt4), ti->init_srb + NUM_RCV_BUF_OFST); + writew(htons(ti->rbuf_len4), ti->init_srb + RCV_BUF_LEN_OFST); + } + writeb(NUM_DHB, /* always 2 */ ti->init_srb + NUM_DHB_OFST); + writeb(DLC_MAX_SAP, ti->init_srb + DLC_MAX_SAP_OFST); + writeb(DLC_MAX_STA, ti->init_srb + DLC_MAX_STA_OFST); + ti->srb = ti->init_srb; /* We use this one in the interrupt handler */ + ti->srb_page = ti->init_srb_page; + DPRINTK("Opening adapter: Xmit bfrs: %d X %d, Rcv bfrs: %d X %d\n", + readb(ti->init_srb + NUM_DHB_OFST), + ntohs(readw(ti->init_srb + DHB_LENGTH_OFST)), + ntohs(readw(ti->init_srb + NUM_RCV_BUF_OFST)), + ntohs(readw(ti->init_srb + RCV_BUF_LEN_OFST))); + writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); + writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); +} + +/*****************************************************************************/ + +static void open_sap(unsigned char type, struct net_device *dev) +{ + int i; + struct tok_info *ti = (struct tok_info *) dev->priv; + + SET_PAGE(ti->srb_page); + for (i = 0; i < sizeof(struct dlc_open_sap); i++) + writeb(0, ti->srb + i); + +#define MAX_I_FIELD_OFST 14 +#define SAP_VALUE_OFST 16 +#define SAP_OPTIONS_OFST 17 +#define STATION_COUNT_OFST 18 + + writeb(DLC_OPEN_SAP, ti->srb + COMMAND_OFST); + writew(htons(MAX_I_FIELD), ti->srb + MAX_I_FIELD_OFST); + writeb(SAP_OPEN_IND_SAP | SAP_OPEN_PRIORITY, ti->srb+ SAP_OPTIONS_OFST); + writeb(SAP_OPEN_STATION_CNT, ti->srb + STATION_COUNT_OFST); + writeb(type, ti->srb + SAP_VALUE_OFST); + writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); +} + + +/*****************************************************************************/ static void tok_set_multicast_list(struct net_device *dev) { - struct tok_info *ti=(struct tok_info *)dev->priv; + struct tok_info *ti = (struct tok_info *) dev->priv; struct dev_mc_list *mclist; unsigned char address[4]; int i; - if(ti->open_status==CLOSED) - return; - + /*BMS the next line is CRUCIAL or you may be sad when you */ + /*BMS ifconfig tr down or hot unplug a PCMCIA card ??hownowbrowncow*/ + if (/*BMSHELPdev->start == 0 ||*/ ti->open_status != OPEN) return; address[0] = address[1] = address[2] = address[3] = 0; - mclist = dev->mc_list; - for (i=0; i< dev->mc_count; i++) - { + for (i = 0; i < dev->mc_count; i++) { address[0] |= mclist->dmi_addr[2]; address[1] |= mclist->dmi_addr[3]; address[2] |= mclist->dmi_addr[4]; @@ -833,76 +977,168 @@ mclist = mclist->next; } SET_PAGE(ti->srb_page); - for (i=0; isrb+i); + for (i = 0; i < sizeof(struct srb_set_funct_addr); i++) + writeb(0, ti->srb + i); - isa_writeb(DIR_SET_FUNC_ADDR, - ti->srb + offsetof(struct srb_set_funct_addr, command)); +#define FUNCT_ADDRESS_OFST 6 + writeb(DIR_SET_FUNC_ADDR, ti->srb + COMMAND_OFST); + for (i = 0; i < 4; i++) + writeb(address[i], ti->srb + FUNCT_ADDRESS_OFST + i); + writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); +#if TR_VERBOSE DPRINTK("Setting functional address: "); - - for (i=0; i<4; i++) - { - isa_writeb(address[i], - ti->srb + offsetof(struct srb_set_funct_addr, funct_address)+i); - printk("%02X ", address[i]); - } - isa_writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); + for (i=0;i<4;i++) printk("%02X ", address[i]); printk("\n"); +#endif } -static int tok_open(struct net_device *dev) -{ - struct tok_info *ti=(struct tok_info *)dev->priv; +/*****************************************************************************/ - if (ti->open_status==CLOSED) tok_init_card(dev); +#define STATION_ID_OFST 4 - if (ti->open_status==IN_PROGRESS) sleep_on(&ti->wait_for_reset); +static int tok_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct tok_info *ti; + unsigned long flags; + ti = (struct tok_info *) dev->priv; - if (ti->open_status==SUCCESS) { - /* NEED to see smem size *AND* reset high 512 bytes if needed */ - netif_start_queue(dev); - MOD_INC_USE_COUNT; + netif_stop_queue(dev); - return 0; - } else return -EAGAIN; + /* lock against other CPUs */ + spin_lock_irqsave(&(ti->lock), flags); -} + /* Save skb; we'll need it when the adapter asks for the data */ + ti->current_skb = skb; + SET_PAGE(ti->srb_page); + writeb(XMIT_UI_FRAME, ti->srb + COMMAND_OFST); + writew(ti->exsap_station_id, ti->srb + STATION_ID_OFST); + writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); + spin_unlock_irqrestore(&(ti->lock), flags); + dev->trans_start = jiffies; + return 0; +} + +/*****************************************************************************/ static int tok_close(struct net_device *dev) { + struct tok_info *ti = (struct tok_info *) dev->priv; - struct tok_info *ti=(struct tok_info *) dev->priv; - - if(ti->open_status!=CLOSED) { - netif_stop_queue(dev); - SET_PAGE(ti->srb_page); - isa_writeb(DIR_CLOSE_ADAPTER, - ti->srb + offsetof(struct srb_close_adapter, command)); - isa_writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); - - ti->open_status=CLOSED; + /* Important for PCMCIA hot unplug, otherwise, we'll pull the card, */ + /* unloading the module from memory, and then if a timer pops, ouch */ + del_timer(&ti->tr_timer); + outb(0, dev->base_addr + ADAPTRESET); + ti->sram_virt |= 1; + ti->open_status = CLOSED; - sleep_on(&ti->wait_for_tok_int); + netif_stop_queue(dev); + DPRINTK("Adapter is closed.\n"); + MOD_DEC_USE_COUNT; + return 0; +} - SET_PAGE(ti->srb_page); - if (isa_readb(ti->srb + offsetof(struct srb_close_adapter, ret_code))) - DPRINTK("close adapter failed: %02X\n", - (int)isa_readb(ti->srb + offsetof(struct srb_close_adapter, ret_code))); +/*****************************************************************************/ -#ifdef PCMCIA - ti->sram = 0 ; -#endif - DPRINTK("Adapter closed.\n"); - MOD_DEC_USE_COUNT; +#define RETCODE_OFST 2 +#define OPEN_ERROR_CODE_OFST 6 +#define ASB_ADDRESS_OFST 8 +#define SRB_ADDRESS_OFST 10 +#define ARB_ADDRESS_OFST 12 +#define SSB_ADDRESS_OFST 14 + +static char *printphase[]= {"Lobe media test","Physical insertion", + "Address verification","Roll call poll","Request Parameters"}; +static char *printerror[]={"Function failure","Signal loss","Reserved", + "Frequency error","Timeout","Ring failure","Ring beaconing", + "Duplicate node address", + "Parameter request-retry count exceeded","Remove received", + "IMPL force received","Duplicate modifier", + "No monitor detected","Monitor contention failed for RPL"}; + +void dir_open_adapter (struct net_device *dev) { + + struct tok_info *ti = (struct tok_info *) dev->priv; + unsigned char ret_code; + __u16 err; + + ti->srb = ntohs(readw(ti->init_srb + SRB_ADDRESS_OFST)); + ti->ssb = ntohs(readw(ti->init_srb + SSB_ADDRESS_OFST)); + ti->arb = ntohs(readw(ti->init_srb + ARB_ADDRESS_OFST)); + ti->asb = ntohs(readw(ti->init_srb + ASB_ADDRESS_OFST)); + if (ti->page_mask) { + ti->srb_page = (ti->srb >> 8) & ti->page_mask; + ti->srb &= ~(ti->page_mask << 8); + ti->ssb_page = (ti->ssb >> 8) & ti->page_mask; + ti->ssb &= ~(ti->page_mask << 8); + ti->arb_page = (ti->arb >> 8) & ti->page_mask; + ti->arb &= ~(ti->page_mask << 8); + ti->asb_page = (ti->asb >> 8) & ti->page_mask; + ti->asb &= ~(ti->page_mask << 8); + } + ti->srb += ti->sram_virt; + ti->ssb += ti->sram_virt; + ti->arb += ti->sram_virt; + ti->asb += ti->sram_virt; + ti->current_skb = NULL; + ret_code = readb(ti->init_srb + RETCODE_OFST); + err = ntohs(readw(ti->init_srb + OPEN_ERROR_CODE_OFST)); + if (!ret_code) { + ti->open_status = OPEN; /* TR adapter is now available */ + if (ti->open_mode == AUTOMATIC) { + DPRINTK("Adapter reopened.\n"); + } + writeb(~SRB_RESP_INT, ti->mmio+ACA_OFFSET+ACA_RESET+ISRP_ODD); + open_sap(EXTENDED_SAP, dev); + return; } - - return 0; + ti->open_failure = YES; + if (ret_code == 7){ + if (err == 0x24) { + if (!ti->auto_speedsave) { + DPRINTK("Open failed: Adapter speed must match " + "ring speed if Automatic Ring Speed Save is " + "disabled.\n"); + ti->open_action = FAIL; + }else + DPRINTK("Retrying open to adjust to " + "ring speed, "); + } else if (err == 0x2d) { + DPRINTK("Physical Insertion: No Monitor Detected, "); + printk("retrying after %ds delay...\n", + TR_RETRY_INTERVAL/HZ); + } else if (err == 0x11) { + DPRINTK("Lobe Media Function Failure (0x11), "); + printk(" retrying after %ds delay...\n", + TR_RETRY_INTERVAL/HZ); + } else { + char **prphase = printphase; + char **prerror = printerror; + DPRINTK("TR Adapter misc open failure, error code = "); + printk("0x%x, Phase: %s, Error: %s\n", + err, prphase[err/16 -1], prerror[err%16 -1]); + printk(" retrying after %ds delay...\n", + TR_RETRY_INTERVAL/HZ); + } + } else DPRINTK("open failed: ret_code = %02X..., ", ret_code); + if (ti->open_action != FAIL) { + if (ti->open_mode==AUTOMATIC){ + ti->open_action = REOPEN; + ibmtr_reset_timer(&(ti->tr_timer), dev); + return; + } + wake_up(&ti->wait_for_reset); + return; + } + DPRINTK("FAILURE, CAPUT\n"); } -void tok_interrupt (int irq, void *dev_id, struct pt_regs *regs) +/******************************************************************************/ + +void tok_interrupt(int irq, void *dev_id, struct pt_regs *regs) { unsigned char status; + /* unsigned char status_even ; */ struct tok_info *ti; struct net_device *dev; #ifdef ENABLE_PAGING @@ -911,839 +1147,621 @@ dev = dev_id; #if TR_VERBOSE - DPRINTK("Int from tok_driver, dev : %p\n",dev); + DPRINTK("Int from tok_driver, dev : %p irq%d regs=%p\n", dev,irq,regs); #endif - ti = (struct tok_info *) dev->priv; + ti = (struct tok_info *) dev->priv; + if (ti->sram_virt & 1) + return; /* PCMCIA card extraction flag */ spin_lock(&(ti->lock)); #ifdef ENABLE_PAGING - save_srpr=isa_readb(ti->mmio+ACA_OFFSET+ACA_RW+SRPR_EVEN); + save_srpr = readb(ti->mmio + ACA_OFFSET + ACA_RW + SRPR_EVEN); #endif - /* Disable interrupts till processing is finished */ - isa_writeb((~INT_ENABLE), ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN); + /* Disable interrupts till processing is finished */ + writeb((~INT_ENABLE), ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN); /* Reset interrupt for ISA boards */ - if (ti->adapter_int_enable) - outb(0,ti->adapter_int_enable); - else - outb(0,ti->global_int_enable); - - - switch (ti->do_tok_int) { - - case NOT_FIRST: - - /* Begin the regular interrupt handler HERE inline to avoid - the extra levels of logic and call depth for the - original solution. */ - - status=isa_readb(ti->mmio + ACA_OFFSET + ACA_RW + ISRP_ODD); -#ifdef PCMCIA - /* Check if the PCMCIA card was pulled. */ - if (status == 0xFF) - { - DPRINTK("PCMCIA card removed.\n"); - ti->open_status=CLOSED; - goto return_point ; - } - - /* Check ISRP EVEN too. */ - if ( isa_readb (ti->mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN) == 0xFF) - { - DPRINTK("PCMCIA card removed.\n"); - ti->open_status=CLOSED; - goto return_point ; - } + if (ti->adapter_int_enable) + outb(0, ti->adapter_int_enable); + else /* used for PCMCIA cards */ + outb(0, ti->global_int_enable); + if (ti->do_tok_int == FIRST_INT){ + initial_tok_int(dev); +#ifdef ENABLE_PAGING + writeb(save_srpr, ti->mmio + ACA_OFFSET + ACA_RW + SRPR_EVEN); #endif + spin_unlock(&(ti->lock)); + return; + } + /* Begin interrupt handler HERE inline to avoid the extra + levels of logic and call depth for the original solution. */ + status = readb(ti->mmio + ACA_OFFSET + ACA_RW + ISRP_ODD); + /*BMSstatus_even = readb (ti->mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN) */ + /*BMSdebugprintk("tok_interrupt: ISRP_ODD = 0x%x ISRP_EVEN = 0x%x\n", */ + /*BMS status,status_even); */ + if (status & ADAP_CHK_INT) { + int i; + __u32 check_reason; + __u8 check_reason_page = 0; + check_reason = + ntohs(readw(ti->mmio+ ACA_OFFSET+ACA_RW + WWCR_EVEN)); + if (ti->page_mask) { + check_reason_page = (check_reason >> 8) & ti->page_mask; + check_reason &= ~(ti->page_mask << 8); + } + check_reason += ti->sram_virt; + SET_PAGE(check_reason_page); - if (status & ADAP_CHK_INT) { - - int i; - __u32 check_reason; - __u8 check_reason_page=0; - - check_reason=ntohs(isa_readw(ti->sram + ACA_OFFSET + ACA_RW +WWCR_EVEN)); - if (ti->page_mask) { - check_reason_page=(check_reason>>8) & ti->page_mask; - check_reason &= ~(ti->page_mask << 8); + DPRINTK("Adapter check interrupt\n"); + DPRINTK("8 reason bytes follow: "); + for (i = 0; i < 8; i++, check_reason++) + printk("%02X ", (int) readb(check_reason)); + printk("\n"); + writeb(~ADAP_CHK_INT, ti->mmio+ ACA_OFFSET+ACA_RESET+ ISRP_ODD); + status = readb(ti->mmio + ACA_OFFSET + ACA_RW + ISRA_EVEN); + DPRINTK("ISRA_EVEN == 0x02%x\n",status); + ti->open_status = CLOSED; + ti->sap_status = CLOSED; + ti->open_mode = AUTOMATIC; + dev->flags &= ~IFF_RUNNING; + netif_stop_queue(dev); + ti->open_action = RESTART; + outb(0, dev->base_addr + ADAPTRESET); + ibmtr_reset_timer(&(ti->tr_timer), dev);/*BMS try to reopen*/ + return; + } + if (readb(ti->mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN) + & (TCR_INT | ERR_INT | ACCESS_INT)) { + DPRINTK("adapter error: ISRP_EVEN : %02x\n", + (int)readb(ti->mmio+ ACA_OFFSET + ACA_RW + ISRP_EVEN)); + writeb(~(TCR_INT | ERR_INT | ACCESS_INT), + ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN); + status= readb(ti->mmio+ ACA_OFFSET + ACA_RW + ISRA_EVEN);/*BMS*/ + DPRINTK("ISRA_EVEN == 0x02%x\n",status);/*BMS*/ + writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); +#ifdef ENABLE_PAGING + writeb(save_srpr, ti->mmio + ACA_OFFSET + ACA_RW + SRPR_EVEN); +#endif + spin_unlock(&(ti->lock)); + return; + } + if (status & SRB_RESP_INT) { /* SRB response */ + SET_PAGE(ti->srb_page); +#if TR_VERBOSE + DPRINTK("SRB resp: cmd=%02X rsp=%02X\n", + readb(ti->srb), readb(ti->srb + RETCODE_OFST)); +#endif + switch (readb(ti->srb)) { /* SRB command check */ + case XMIT_DIR_FRAME:{ + unsigned char xmit_ret_code; + xmit_ret_code = readb(ti->srb + RETCODE_OFST); + if (xmit_ret_code == 0xff) break; + DPRINTK("error on xmit_dir_frame request: %02X\n", + xmit_ret_code); + if (ti->current_skb) { + dev_kfree_skb_irq(ti->current_skb); + ti->current_skb = NULL; } - check_reason += ti->sram; - SET_PAGE(check_reason_page); - - DPRINTK("Adapter check interrupt\n"); - DPRINTK("8 reason bytes follow: "); - for(i=0; i<8; i++, check_reason++) - printk("%02X ", (int)isa_readb(check_reason)); - printk("\n"); - - isa_writeb((~ADAP_CHK_INT), ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD); - isa_writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); - - } else if (isa_readb(ti->mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN) - & (TCR_INT | ERR_INT | ACCESS_INT)) { + /*dev->tbusy = 0;*/ + netif_wake_queue(dev); + if (ti->readlog_pending) + ibmtr_readlog(dev); + break; + } + case XMIT_UI_FRAME:{ + unsigned char xmit_ret_code; - DPRINTK("adapter error: ISRP_EVEN : %02x\n", - (int)isa_readb(ti->mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN)); - isa_writeb(~(TCR_INT | ERR_INT | ACCESS_INT), - ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN); - isa_writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); - - } else if (status - & (SRB_RESP_INT | ASB_FREE_INT | ARB_CMD_INT | SSB_RESP_INT)) { - /* SRB, ASB, ARB or SSB response */ + xmit_ret_code = readb(ti->srb + RETCODE_OFST); + if (xmit_ret_code == 0xff) break; + DPRINTK("error on xmit_ui_frame request: %02X\n", + xmit_ret_code); + if (ti->current_skb) { + dev_kfree_skb_irq(ti->current_skb); + ti->current_skb = NULL; + } + netif_wake_queue(dev); + if (ti->readlog_pending) + ibmtr_readlog(dev); + break; + } + case DIR_OPEN_ADAPTER: + dir_open_adapter(dev); + break; + case DLC_OPEN_SAP: + if (readb(ti->srb + RETCODE_OFST)) { + DPRINTK("open_sap failed: ret_code = %02X, " + "retrying\n", + (int) readb(ti->srb + RETCODE_OFST)); + ti->open_action = REOPEN; + ibmtr_reset_timer(&(ti->tr_timer), dev); + break; + } + ti->exsap_station_id = readw(ti->srb + STATION_ID_OFST); + ti->sap_status = OPEN;/* TR adapter is now available */ + if (ti->open_mode==MANUAL){ + wake_up(&ti->wait_for_reset); + break; + } + netif_wake_queue(dev); + dev->flags |= IFF_RUNNING;/*BMS 12/2000*/ + break; + case DIR_INTERRUPT: + case DIR_MOD_OPEN_PARAMS: + case DIR_SET_GRP_ADDR: + case DIR_SET_FUNC_ADDR: + case DLC_CLOSE_SAP: + if (readb(ti->srb + RETCODE_OFST)) + DPRINTK("error on %02X: %02X\n", + (int) readb(ti->srb + COMMAND_OFST), + (int) readb(ti->srb + RETCODE_OFST)); + break; + case DIR_READ_LOG: + if (readb(ti->srb + RETCODE_OFST)){ + DPRINTK("error on dir_read_log: %02X\n", + (int) readb(ti->srb + RETCODE_OFST)); + netif_wake_queue(dev); + break; + } +#if IBMTR_DEBUG_MESSAGES - if (status & SRB_RESP_INT) { /* SRB response */ - SET_PAGE(ti->srb_page); +#define LINE_ERRORS_OFST 0 +#define INTERNAL_ERRORS_OFST 1 +#define BURST_ERRORS_OFST 2 +#define AC_ERRORS_OFST 3 +#define ABORT_DELIMITERS_OFST 4 +#define LOST_FRAMES_OFST 6 +#define RECV_CONGEST_COUNT_OFST 7 +#define FRAME_COPIED_ERRORS_OFST 8 +#define FREQUENCY_ERRORS_OFST 9 +#define TOKEN_ERRORS_OFST 10 + + DPRINTK("Line errors %02X, Internal errors %02X, " + "Burst errors %02X\n" "A/C errors %02X, " + "Abort delimiters %02X, Lost frames %02X\n" + "Receive congestion count %02X, " + "Frame copied errors %02X\nFrequency errors %02X, " + "Token errors %02X\n", + (int) readb(ti->srb + LINE_ERRORS_OFST), + (int) readb(ti->srb + INTERNAL_ERRORS_OFST), + (int) readb(ti->srb + BURST_ERRORS_OFST), + (int) readb(ti->srb + AC_ERRORS_OFST), + (int) readb(ti->srb + ABORT_DELIMITERS_OFST), + (int) readb(ti->srb + LOST_FRAMES_OFST), + (int) readb(ti->srb + RECV_CONGEST_COUNT_OFST), + (int) readb(ti->srb + FRAME_COPIED_ERRORS_OFST), + (int) readb(ti->srb + FREQUENCY_ERRORS_OFST), + (int) readb(ti->srb + TOKEN_ERRORS_OFST)); +#endif + netif_wake_queue(dev); + break; + default: + DPRINTK("Unknown command %02X encountered\n", + (int) readb(ti->srb)); + } /* end switch SRB command check */ + writeb(~SRB_RESP_INT, ti->mmio+ ACA_OFFSET+ACA_RESET+ ISRP_ODD); + } /* if SRB response */ + if (status & ASB_FREE_INT) { /* ASB response */ + SET_PAGE(ti->asb_page); #if TR_VERBOSE - DPRINTK("SRB resp: cmd=%02X rsp=%02X\n", - isa_readb(ti->srb), - isa_readb(ti->srb + offsetof(struct srb_xmit, ret_code))); -#endif - - switch(isa_readb(ti->srb)) { /* SRB command check */ - - case XMIT_DIR_FRAME: { - unsigned char xmit_ret_code; - - xmit_ret_code=isa_readb(ti->srb + offsetof(struct srb_xmit, ret_code)); - if (xmit_ret_code != 0xff) { - DPRINTK("error on xmit_dir_frame request: %02X\n", - xmit_ret_code); - if (ti->current_skb) { - dev_kfree_skb_irq(ti->current_skb); - ti->current_skb=NULL; - } - netif_wake_queue(dev); - if (ti->readlog_pending) ibmtr_readlog(dev); - } - } - break; - - case XMIT_UI_FRAME: { - unsigned char xmit_ret_code; - - xmit_ret_code=isa_readb(ti->srb + offsetof(struct srb_xmit, ret_code)); - if (xmit_ret_code != 0xff) { - DPRINTK("error on xmit_ui_frame request: %02X\n", - xmit_ret_code); - if (ti->current_skb) { - dev_kfree_skb_irq(ti->current_skb); - ti->current_skb=NULL; - } - netif_wake_queue(dev); - if (ti->readlog_pending) - ibmtr_readlog(dev); - } - } - break; - - case DIR_OPEN_ADAPTER: { - unsigned char open_ret_code; - __u16 open_error_code; - - ti->srb=ntohs(isa_readw(ti->init_srb +offsetof(struct srb_open_response, srb_addr))); - ti->ssb=ntohs(isa_readw(ti->init_srb +offsetof(struct srb_open_response, ssb_addr))); - ti->arb=ntohs(isa_readw(ti->init_srb +offsetof(struct srb_open_response, arb_addr))); - ti->asb=ntohs(isa_readw(ti->init_srb +offsetof(struct srb_open_response, asb_addr))); - if (ti->page_mask) { - ti->srb_page=(ti->srb>>8) & ti->page_mask; - ti->srb &= ~(ti->page_mask<<8); - ti->ssb_page=(ti->ssb>>8) & ti->page_mask; - ti->ssb &= ~(ti->page_mask<<8); - ti->arb_page=(ti->arb>>8) & ti->page_mask; - ti->arb &= ~(ti->page_mask<<8); - ti->asb_page=(ti->asb>>8) & ti->page_mask; - ti->asb &= ~(ti->page_mask<<8); - } - ti->srb+=ti->sram; - ti->ssb+=ti->sram; - ti->arb+=ti->sram; - ti->asb+=ti->sram; - - ti->current_skb=NULL; - - open_ret_code = isa_readb(ti->init_srb +offsetof(struct srb_open_response, ret_code)); - open_error_code = ntohs(isa_readw(ti->init_srb +offsetof(struct srb_open_response, error_code))); - - if (open_ret_code==7) { - - if (!ti->auto_ringspeedsave && (open_error_code==0x24)) { - DPRINTK("Open failed: Adapter speed must match ring " - "speed if Automatic Ring Speed Save is disabled.\n"); - ti->open_status=FAILURE; - wake_up(&ti->wait_for_reset); - } else if (open_error_code==0x24) - DPRINTK("Retrying open to adjust to ring speed.\n"); - else if ((open_error_code==0x2d) && ti->auto_ringspeedsave) - DPRINTK("No signal detected for Auto Speed Detection.\n"); - else if (open_error_code==0x11) - { - if (ti->retry_count--) - DPRINTK("Ring broken/disconnected, retrying...\n"); - else { - DPRINTK("Ring broken/disconnected, open failed.\n"); - ti->open_status = FAILURE; - } - } - else DPRINTK("Unrecoverable error: error code = %04x.\n", - open_error_code); - - } else if (!open_ret_code) { -#if !TR_NEWFORMAT - DPRINTK("board opened...\n"); -#else - DPRINTK("Adapter initialized and opened.\n"); + DPRINTK("ASB resp: cmd=%02X\n", readb(ti->asb)); #endif - isa_writeb(~(SRB_RESP_INT), - ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD); - isa_writeb(~(CMD_IN_SRB), - ti->mmio + ACA_OFFSET + ACA_RESET + ISRA_ODD); - open_sap(EXTENDED_SAP,dev); - - /* YdW probably hates me */ - goto skip_reset; - } else - DPRINTK("open failed: ret_code = %02X, retrying\n", - open_ret_code); - - if (ti->open_status != FAILURE) { - ibmtr_reset_timer(&(ti->tr_timer), dev); - } - - } - break; - - case DIR_CLOSE_ADAPTER: - wake_up(&ti->wait_for_tok_int); - break; - - case DLC_OPEN_SAP: - if (isa_readb(ti->srb+offsetof(struct dlc_open_sap, ret_code))) { - DPRINTK("open_sap failed: ret_code = %02X,retrying\n", - (int)isa_readb(ti->srb+offsetof(struct dlc_open_sap, ret_code))); - ibmtr_reset_timer(&(ti->tr_timer), dev); - } else { - ti->exsap_station_id= - isa_readw(ti->srb+offsetof(struct dlc_open_sap, station_id)); - ti->open_status=SUCCESS; /* TR adapter is now available */ - wake_up(&ti->wait_for_reset); - } - break; - - case DIR_INTERRUPT: - case DIR_MOD_OPEN_PARAMS: - case DIR_SET_GRP_ADDR: - case DIR_SET_FUNC_ADDR: - case DLC_CLOSE_SAP: - if (isa_readb(ti->srb+offsetof(struct srb_interrupt, ret_code))) - DPRINTK("error on %02X: %02X\n", - (int)isa_readb(ti->srb+offsetof(struct srb_interrupt, command)), - (int)isa_readb(ti->srb+offsetof(struct srb_interrupt, ret_code))); - break; - - case DIR_READ_LOG: - if (isa_readb(ti->srb+offsetof(struct srb_read_log, ret_code))) - DPRINTK("error on dir_read_log: %02X\n", - (int)isa_readb(ti->srb+offsetof(struct srb_read_log, ret_code))); - else - if (IBMTR_DEBUG_MESSAGES) { - DPRINTK( - "Line errors %02X, Internal errors %02X, Burst errors %02X\n" - "A/C errors %02X, Abort delimiters %02X, Lost frames %02X\n" - "Receive congestion count %02X, Frame copied errors %02X\n" - "Frequency errors %02X, Token errors %02X\n", - (int)isa_readb(ti->srb+offsetof(struct srb_read_log, - line_errors)), - (int)isa_readb(ti->srb+offsetof(struct srb_read_log, - internal_errors)), - (int)isa_readb(ti->srb+offsetof(struct srb_read_log, - burst_errors)), - (int)isa_readb(ti->srb+offsetof(struct srb_read_log, A_C_errors)), - (int)isa_readb(ti->srb+offsetof(struct srb_read_log, - abort_delimiters)), - (int)isa_readb(ti->srb+offsetof(struct srb_read_log, - lost_frames)), - (int)isa_readb(ti->srb+offsetof(struct srb_read_log, - recv_congest_count)), - (int)isa_readb(ti->srb+offsetof(struct srb_read_log, - frame_copied_errors)), - (int)isa_readb(ti->srb+offsetof(struct srb_read_log, - frequency_errors)), - (int)isa_readb(ti->srb+offsetof(struct srb_read_log, - token_errors))); - } - netif_wake_queue(dev); - break; - - default: - DPRINTK("Unknown command %02X encountered\n", - (int)isa_readb(ti->srb)); - - } /* SRB command check */ - isa_writeb(~CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_RESET + ISRA_ODD); - isa_writeb(~SRB_RESP_INT, ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD); + switch (readb(ti->asb)) { /* ASB command check */ + case REC_DATA: + case XMIT_UI_FRAME: + case XMIT_DIR_FRAME: + break; + default: + DPRINTK("unknown command in asb %02X\n", + (int) readb(ti->asb)); + } /* switch ASB command check */ + if (readb(ti->asb + 2) != 0xff) /* checks ret_code */ + DPRINTK("ASB error %02X in cmd %02X\n", + (int) readb(ti->asb + 2), (int) readb(ti->asb)); + writeb(~ASB_FREE_INT, ti->mmio+ ACA_OFFSET+ACA_RESET+ ISRP_ODD); + } /* if ASB response */ - skip_reset: - } /* SRB response */ +#define STATUS_OFST 6 +#define NETW_STATUS_OFST 6 - if (status & ASB_FREE_INT) { /* ASB response */ - SET_PAGE(ti->asb_page); + if (status & ARB_CMD_INT) { /* ARB response */ + SET_PAGE(ti->arb_page); #if TR_VERBOSE - DPRINTK("ASB resp: cmd=%02X\n", isa_readb(ti->asb)); - #endif - - switch(isa_readb(ti->asb)) { /* ASB command check */ - - case REC_DATA: - case XMIT_UI_FRAME: - case XMIT_DIR_FRAME: - break; - - default: - DPRINTK("unknown command in asb %02X\n", - (int)isa_readb(ti->asb)); - - } /* ASB command check */ - - if (isa_readb(ti->asb+2)!=0xff) /* checks ret_code */ - DPRINTK("ASB error %02X in cmd %02X\n", - (int)isa_readb(ti->asb+2),(int)isa_readb(ti->asb)); - isa_writeb(~ASB_FREE_INT, ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD); - - } /* ASB response */ - - if (status & ARB_CMD_INT) { /* ARB response */ - SET_PAGE(ti->arb_page); -#if TR_VERBOSE - DPRINTK("ARB resp: cmd=%02X rsp=%02X\n", - isa_readb(ti->arb), - isa_readb(ti->arb + offsetof(struct arb_dlc_status, status))); + DPRINTK("ARB resp: cmd=%02X\n", readb(ti->arb)); #endif - switch (isa_readb(ti->arb)) { /* ARB command check */ - - case DLC_STATUS: - DPRINTK("DLC_STATUS new status: %02X on station %02X\n", - ntohs(isa_readw(ti->arb + offsetof(struct arb_dlc_status, status))), - ntohs(isa_readw(ti->arb - +offsetof(struct arb_dlc_status, station_id)))); - break; - - case REC_DATA: - tr_rx(dev); - break; - - case RING_STAT_CHANGE: { - unsigned short ring_status; - - ring_status=ntohs(isa_readw(ti->arb - +offsetof(struct arb_ring_stat_change, ring_status))); - - if (ring_status & (SIGNAL_LOSS | LOBE_FAULT)) { - - DPRINTK("Signal loss/Lobe fault\n"); - DPRINTK("We try to reopen the adapter.\n"); - ibmtr_reset_timer(&(ti->tr_timer), dev); - } else if (ring_status & (HARD_ERROR | XMIT_BEACON - | AUTO_REMOVAL | REMOVE_RECV | RING_RECOVER)) - DPRINTK("New ring status: %02X\n", ring_status); - - if (ring_status & LOG_OVERFLOW) { - if (netif_queue_stopped(dev)) - ti->readlog_pending = 1; - else - ibmtr_readlog(dev); - } - } - break; - - case XMIT_DATA_REQ: - tr_tx(dev); - break; - - default: - DPRINTK("Unknown command %02X in arb\n", - (int)isa_readb(ti->arb)); - break; - - } /* ARB command check */ - - isa_writeb(~ARB_CMD_INT, ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD); - isa_writeb(ARB_FREE, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); - - } /* ARB response */ - - if (status & SSB_RESP_INT) { /* SSB response */ - unsigned char retcode; - SET_PAGE(ti->ssb_page); + switch (readb(ti->arb)) { /* ARB command check */ + case DLC_STATUS: + DPRINTK("DLC_STATUS new status: %02X on station %02X\n", + ntohs(readw(ti->arb + STATUS_OFST)), + ntohs(readw(ti->arb+ STATION_ID_OFST))); + break; + case REC_DATA: + tr_rx(dev); + break; + case RING_STAT_CHANGE:{ + unsigned short ring_status; + ring_status= ntohs(readw(ti->arb + NETW_STATUS_OFST)); + if (ibmtr_debug_trace & TRC_INIT) + DPRINTK("Ring Status Change...(0x%x)\n", + ring_status); + if(ring_status& (REMOVE_RECV|AUTO_REMOVAL|LOBE_FAULT)){ + netif_stop_queue(dev); + dev->flags &= ~IFF_RUNNING;/*not typical Linux*/ + DPRINTK("Remove received, or Auto-removal error" + ", or Lobe fault\n"); + DPRINTK("We'll try to reopen the closed adapter" + " after a %d second delay.\n", + TR_RETRY_INTERVAL/HZ); + /*I was confused: I saw the TR reopening but */ + /*forgot:with an RJ45 in an RJ45/ICS adapter */ + /*but adapter not in the ring, the TR will */ + /* open, and then soon close and come here. */ + ti->open_mode = AUTOMATIC; + ti->open_status = CLOSED; /*12/2000 BMS*/ + ti->open_action = REOPEN; + ibmtr_reset_timer(&(ti->tr_timer), dev); + } else if (ring_status & LOG_OVERFLOW) { + if(netif_queue_stopped(dev)) + ti->readlog_pending = 1; + else + ibmtr_readlog(dev); + } + break; + } + case XMIT_DATA_REQ: + tr_tx(dev); + break; + default: + DPRINTK("Unknown command %02X in arb\n", + (int) readb(ti->arb)); + break; + } /* switch ARB command check */ + writeb(~ARB_CMD_INT, ti->mmio+ ACA_OFFSET+ACA_RESET + ISRP_ODD); + writeb(ARB_FREE, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); + } /* if ARB response */ + if (status & SSB_RESP_INT) { /* SSB response */ + unsigned char retcode; + SET_PAGE(ti->ssb_page); #if TR_VERBOSE - DPRINTK("SSB resp: cmd=%02X rsp=%02X\n", - isa_readb(ti->ssb), isa_readb(ti->ssb+2)); + DPRINTK("SSB resp: cmd=%02X rsp=%02X\n", + readb(ti->ssb), readb(ti->ssb + 2)); #endif - switch (isa_readb(ti->ssb)) { /* SSB command check */ - - case XMIT_DIR_FRAME: - case XMIT_UI_FRAME: - retcode = isa_readb(ti->ssb+2); - if (retcode && (retcode != 0x22)) /* checks ret_code */ - DPRINTK("xmit ret_code: %02X xmit error code: %02X\n", - (int)retcode, (int)isa_readb(ti->ssb+6)); - else ti->tr_stats.tx_packets++; - break; - - case XMIT_XID_CMD: - DPRINTK("xmit xid ret_code: %02X\n", (int)isa_readb(ti->ssb+2)); - break; - - default: - DPRINTK("Unknown command %02X in ssb\n", (int)isa_readb(ti->ssb)); - - } /* SSB command check */ - - isa_writeb(~SSB_RESP_INT, ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD); - isa_writeb(SSB_FREE, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); - - } /* SSB response */ - - } /* SRB, ARB, ASB or SSB response */ - - isa_writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); - break; - case FIRST_INT: - initial_tok_int(dev); - break; - - default: - DPRINTK("Unexpected interrupt from tr adapter\n"); - - } -#ifdef PCMCIA - return_point: -#endif + switch (readb(ti->ssb)) { /* SSB command check */ + case XMIT_DIR_FRAME: + case XMIT_UI_FRAME: + retcode = readb(ti->ssb + 2); + if (retcode && (retcode != 0x22))/* checks ret_code */ + DPRINTK("xmit ret_code: %02X xmit error code: " + "%02X\n", + (int)retcode, (int)readb(ti->ssb + 6)); + else + ti->tr_stats.tx_packets++; + break; + case XMIT_XID_CMD: + DPRINTK("xmit xid ret_code: %02X\n", + (int) readb(ti->ssb + 2)); + default: + DPRINTK("Unknown command %02X in ssb\n", + (int) readb(ti->ssb)); + } /* SSB command check */ + writeb(~SSB_RESP_INT, ti->mmio+ ACA_OFFSET+ACA_RESET+ ISRP_ODD); + writeb(SSB_FREE, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); + } /* if SSB response */ #ifdef ENABLE_PAGING - isa_writeb(save_srpr, ti->mmio+ACA_OFFSET+ACA_RW+SRPR_EVEN); + writeb(save_srpr, ti->mmio + ACA_OFFSET + ACA_RW + SRPR_EVEN); #endif - + writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); spin_unlock(&(ti->lock)); -} +} /*tok_interrupt */ + +/*****************************************************************************/ + +#define INIT_STATUS_OFST 1 +#define INIT_STATUS_2_OFST 2 +#define ENCODED_ADDRESS_OFST 8 static void initial_tok_int(struct net_device *dev) { - __u32 encoded_addr; - __u32 hw_encoded_addr; + __u32 encoded_addr, hw_encoded_addr; struct tok_info *ti; - ti=(struct tok_info *) dev->priv; + unsigned char init_status; /*BMS 12/2000*/ - ti->do_tok_int=NOT_FIRST; + ti = (struct tok_info *) dev->priv; -#ifndef TR_NEWFORMAT - DPRINTK("Initial tok int received\n"); -#endif + ti->do_tok_int = NOT_FIRST; /* we assign the shared-ram address for ISA devices */ - if(!ti->sram) { - isa_writeb(ti->sram_base, ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN); - ti->sram=((__u32)ti->sram_base << 12); - } - ti->init_srb=ntohs((unsigned short)isa_readw(ti->mmio+ ACA_OFFSET + WRBR_EVEN)); + writeb(ti->sram_base, ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN); +#ifndef PCMCIA + ti->sram_virt = (u32)ioremap(((__u32)ti->sram_base << 12), ti->avail_shared_ram); +#endif + ti->init_srb = ntohs(readw(ti->mmio + ACA_OFFSET + WRBR_EVEN)); if (ti->page_mask) { - ti->init_srb_page=(ti->init_srb>>8)&ti->page_mask; - ti->init_srb &= ~(ti->page_mask<<8); + ti->init_srb_page = (ti->init_srb >> 8) & ti->page_mask; + ti->init_srb &= ~(ti->page_mask << 8); + } + ti->init_srb += ti->sram_virt; + if (ti->page_mask && ti->avail_shared_ram == 127) { + int last_512 = 0xfe00, i; + int last_512_page=0; + last_512_page=(last_512>>8)&ti->page_mask; + last_512 &= ~(ti->page_mask << 8); + /* initialize high section of ram (if necessary) */ + SET_PAGE(last_512_page); + for (i = 0; i < 512; i++) + writeb(0, ti->sram_virt + last_512 + i); } - ti->init_srb+=ti->sram; - - if (ti->avail_shared_ram == 127) { - int i; - int last_512=0xfe00; - if (ti->page_mask) { - last_512 &= ~(ti->page_mask<<8); - } - /* initialize high section of ram (if necessary) */ - SET_PAGE(0xc0); - for (i=0; i<512; i++) { - isa_writeb(0,ti->sram+last_512+i); - } - } SET_PAGE(ti->init_srb_page); - dev->mem_start = ti->sram; - dev->mem_end = ti->sram + (ti->mapped_ram_size<<9) - 1; - #if TR_VERBOSE { - int i; - DPRINTK("init_srb(%lx):", (long)ti->init_srb); - for (i=0;i<17;i++) printk("%02X ", (int)isa_readb(ti->init_srb+i)); - printk("\n"); - } -#endif - - hw_encoded_addr = isa_readw(ti->init_srb - + offsetof(struct srb_init_response, encoded_address)); - -#if !TR_NEWFORMAT - DPRINTK("srb_init_response->encoded_address: %04X\n", hw_encoded_addr); - DPRINTK("ntohs(srb_init_response->encoded_address): %04X\n", - ntohs(hw_encoded_addr)); -#endif - - encoded_addr=(ti->sram + ntohs(hw_encoded_addr)); - ti->ring_speed = isa_readb(ti->init_srb+offsetof(struct srb_init_response, init_status)) & 0x01 ? 16 : 4; -#if !TR_NEWFORMAT - DPRINTK("encoded addr (%04X,%04X,%08X): ", hw_encoded_addr, - ntohs(hw_encoded_addr), encoded_addr); -#else - DPRINTK("Initial interrupt : %d Mbps, shared RAM base %08x.\n", - ti->ring_speed, ti->sram); -#endif - - ti->auto_ringspeedsave=isa_readb(ti->init_srb - +offsetof(struct srb_init_response, init_status_2)) & 0x4 ? TRUE : FALSE; - -#if !TR_NEWFORMAT - for(i=0;idev_addr[i]=isa_readb(encoded_addr + i); - printk("%02X%s", dev->dev_addr[i], (i==TR_ALEN-1) ? "" : ":" ); - } - printk("\n"); -#endif - - tok_open_adapter((unsigned long)dev); -} - -static int tok_init_card(struct net_device *dev) -{ - struct tok_info *ti; - short PIOaddr; - unsigned long i; - PIOaddr = dev->base_addr; - ti=(struct tok_info *) dev->priv; - - /* Special processing for first interrupt after reset */ - ti->do_tok_int=FIRST_INT; - - /* Reset adapter */ - netif_stop_queue(dev); - - isa_writeb(~INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN); - -#if !TR_NEWFORMAT - DPRINTK("resetting card\n"); -#endif - - outb(0, PIOaddr+ADAPTRESET); - for (i=jiffies+TR_RESET_INTERVAL; time_before_eq(jiffies, i);); /* wait 50ms */ - outb(0,PIOaddr+ADAPTRESETREL); -#ifdef ENABLE_PAGING - if(ti->page_mask) - isa_writeb(SRPR_ENABLE_PAGING, ti->mmio + ACA_OFFSET + ACA_RW + SRPR_EVEN); -#endif - -#if !TR_NEWFORMAT - DPRINTK("card reset\n"); -#endif - - ti->open_status=IN_PROGRESS; - isa_writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); - return 0; -} - -static void open_sap(unsigned char type,struct net_device *dev) -{ int i; - struct tok_info *ti=(struct tok_info *) dev->priv; - - SET_PAGE(ti->srb_page); - for (i=0; isrb+i); - isa_writeb(DLC_OPEN_SAP, ti->srb + offsetof(struct dlc_open_sap, command)); - isa_writew(htons(MAX_I_FIELD), - ti->srb + offsetof(struct dlc_open_sap, max_i_field)); - isa_writeb(SAP_OPEN_IND_SAP | SAP_OPEN_PRIORITY, - ti->srb + offsetof(struct dlc_open_sap, sap_options)); - isa_writeb(SAP_OPEN_STATION_CNT, - ti->srb + offsetof(struct dlc_open_sap, station_count)); - isa_writeb(type, ti->srb + offsetof(struct dlc_open_sap, sap_value)); - - isa_writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); - -} - -void tok_open_adapter(unsigned long dev_addr) -{ - - struct net_device *dev=(struct net_device *)dev_addr; - struct tok_info *ti; - int i; - - ti=(struct tok_info *) dev->priv; - -#if !TR_NEWFORMAT - DPRINTK("now opening the board...\n"); + DPRINTK("ti->init_srb_page=0x%x\n", ti->init_srb_page); + DPRINTK("init_srb(%x):", (ti->init_srb) ); + for (i = 0; i < 20; i++) + printk("%02X ", (int) readb(ti->init_srb + i)); + printk("\n"); + } #endif - isa_writeb(~SRB_RESP_INT, ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD); - isa_writeb(~CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_RESET + ISRA_ODD); + hw_encoded_addr = readw(ti->init_srb + ENCODED_ADDRESS_OFST); + encoded_addr = ntohs(hw_encoded_addr); + init_status= /*BMS 12/2000 check for shallow mode possibility (Turbo)*/ + readb(ti->init_srb+offsetof(struct srb_init_response,init_status)); + /*printk("Initial interrupt: init_status= 0x%02x\n",init_status);*/ + ti->ring_speed = init_status & 0x01 ? 16 : 4; + DPRINTK("Initial interrupt : %d Mbps, shared RAM base %08x.\n", + ti->ring_speed, (unsigned int)dev->mem_start); + ti->auto_speedsave=readb(ti->init_srb+INIT_STATUS_2_OFST)&4?TRUE:FALSE; - for (i=0; iinit_srb+i); + if (ti->open_mode == MANUAL) wake_up(&ti->wait_for_reset); + else tok_open_adapter((unsigned long)dev); + +} /*initial_tok_int() */ - isa_writeb(DIR_OPEN_ADAPTER, - ti->init_srb + offsetof(struct dir_open_adapter, command)); - isa_writew(htons(OPEN_PASS_BCON_MAC), - ti->init_srb + offsetof(struct dir_open_adapter, open_options)); - if (ti->ring_speed == 16) { - isa_writew(htons(ti->dhb_size16mb), - ti->init_srb + offsetof(struct dir_open_adapter, dhb_length)); - isa_writew(htons(ti->rbuf_cnt16), - ti->init_srb + offsetof(struct dir_open_adapter, num_rcv_buf)); - isa_writew(htons(ti->rbuf_len16), - ti->init_srb + offsetof(struct dir_open_adapter, rcv_buf_len)); - } else { - isa_writew(htons(ti->dhb_size4mb), - ti->init_srb + offsetof(struct dir_open_adapter, dhb_length)); - isa_writew(htons(ti->rbuf_cnt4), - ti->init_srb + offsetof(struct dir_open_adapter, num_rcv_buf)); - isa_writew(htons(ti->rbuf_len4), - ti->init_srb + offsetof(struct dir_open_adapter, rcv_buf_len)); - } - isa_writeb(NUM_DHB, /* always 2 */ - ti->init_srb + offsetof(struct dir_open_adapter, num_dhb)); - isa_writeb(DLC_MAX_SAP, - ti->init_srb + offsetof(struct dir_open_adapter, dlc_max_sap)); - isa_writeb(DLC_MAX_STA, - ti->init_srb + offsetof(struct dir_open_adapter, dlc_max_sta)); - - ti->srb=ti->init_srb; /* We use this one in the interrupt handler */ - ti->srb_page=ti->init_srb_page; - DPRINTK("Opend adapter: Xmit bfrs: %d X %d, Rcv bfrs: %d X %d\n", - isa_readb(ti->init_srb+offsetof(struct dir_open_adapter,num_dhb)), - ntohs(isa_readw(ti->init_srb+offsetof(struct dir_open_adapter,dhb_length))), - ntohs(isa_readw(ti->init_srb+offsetof(struct dir_open_adapter,num_rcv_buf))), - ntohs(isa_readw(ti->init_srb+offsetof(struct dir_open_adapter,rcv_buf_len))) ); +/*****************************************************************************/ - isa_writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); - isa_writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); +#define CMD_CORRELATE_OFST 1 +#define DHB_ADDRESS_OFST 6 -} +#define FRAME_LENGTH_OFST 6 +#define HEADER_LENGTH_OFST 8 +#define RSAP_VALUE_OFST 9 static void tr_tx(struct net_device *dev) { - struct tok_info *ti=(struct tok_info *) dev->priv; - struct trh_hdr *trhdr=(struct trh_hdr *)ti->current_skb->data; + struct tok_info *ti = (struct tok_info *) dev->priv; + struct trh_hdr *trhdr = (struct trh_hdr *) ti->current_skb->data; unsigned int hdr_len; - __u32 dhb; + __u32 dhb=0,dhb_base; unsigned char xmit_command; - int i; - struct trllc *llc; - struct srb_xmit xsrb; - __u8 dhb_page=0; - __u8 llc_ssap; + int i,dhb_len=0x4000,src_len,src_offset; + struct trllc *llc; + struct srb_xmit xsrb; + __u8 dhb_page = 0; + __u8 llc_ssap; SET_PAGE(ti->asb_page); - if (isa_readb(ti->asb + offsetof(struct asb_xmit_resp, ret_code))!=0xFF) - DPRINTK("ASB not free !!!\n"); - /* in providing the transmit interrupts, - is telling us it is ready for data and - providing a shared memory address for us - to stuff with data. Here we compute the - effective address where we will place data.*/ - SET_PAGE(ti->arb_page); - dhb=ntohs(isa_readw(ti->arb + offsetof(struct arb_xmit_req, dhb_address))); - if (ti->page_mask) { - dhb_page=(dhb >> 8) & ti->page_mask; - dhb &= ~(ti->page_mask << 8); - } - dhb+=ti->sram; - + if (readb(ti->asb+RETCODE_OFST) != 0xFF) DPRINTK("ASB not free !!!\n"); + + /* in providing the transmit interrupts, is telling us it is ready for + data and providing a shared memory address for us to stuff with data. + Here we compute the effective address where we will place data. + */ + SET_PAGE(ti->arb_page); + dhb=dhb_base=ntohs(readw(ti->arb + DHB_ADDRESS_OFST)); + if (ti->page_mask) { + dhb_page = (dhb_base >> 8) & ti->page_mask; + dhb=dhb_base & ~(ti->page_mask << 8); + } + dhb += ti->sram_virt; + /* Figure out the size of the 802.5 header */ - if (!(trhdr->saddr[0] & 0x80)) /* RIF present? */ - hdr_len=sizeof(struct trh_hdr)-TR_MAXRIFLEN; - else - hdr_len=((ntohs(trhdr->rcf) & TR_RCF_LEN_MASK)>>8) - +sizeof(struct trh_hdr)-TR_MAXRIFLEN; - - llc = (struct trllc *)(ti->current_skb->data + hdr_len); - - llc_ssap=llc->ssap; - SET_PAGE(ti->srb_page); - isa_memcpy_fromio(&xsrb, ti->srb, sizeof(xsrb)); - SET_PAGE(ti->asb_page); - xmit_command=xsrb.command; - - isa_writeb(xmit_command, ti->asb + offsetof(struct asb_xmit_resp, command)); - isa_writew(xsrb.station_id, - ti->asb + offsetof(struct asb_xmit_resp, station_id)); - isa_writeb(llc_ssap, ti->asb + offsetof(struct asb_xmit_resp, rsap_value)); - isa_writeb(xsrb.cmd_corr, - ti->asb + offsetof(struct asb_xmit_resp, cmd_corr)); - isa_writeb(0, ti->asb + offsetof(struct asb_xmit_resp, ret_code)); - - if ((xmit_command==XMIT_XID_CMD) || (xmit_command==XMIT_TEST_CMD)) { - - isa_writew(htons(0x11), - ti->asb + offsetof(struct asb_xmit_resp, frame_length)); - isa_writeb(0x0e, ti->asb + offsetof(struct asb_xmit_resp, hdr_length)); - SET_PAGE(dhb_page); - isa_writeb(AC, dhb); - isa_writeb(LLC_FRAME, dhb+1); + if (!(trhdr->saddr[0] & 0x80)) /* RIF present? */ + hdr_len = sizeof(struct trh_hdr) - TR_MAXRIFLEN; + else + hdr_len = ((ntohs(trhdr->rcf) & TR_RCF_LEN_MASK) >> 8) + + sizeof(struct trh_hdr) - TR_MAXRIFLEN; - for (i=0; icurrent_skb->data + hdr_len); - isa_writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); - return; + llc_ssap = llc->ssap; + SET_PAGE(ti->srb_page); + memcpy_fromio(&xsrb, ti->srb, sizeof(xsrb)); + SET_PAGE(ti->asb_page); + xmit_command = xsrb.command; + writeb(xmit_command, ti->asb + COMMAND_OFST); + writew(xsrb.station_id, ti->asb + STATION_ID_OFST); + writeb(llc_ssap, ti->asb + RSAP_VALUE_OFST); + writeb(xsrb.cmd_corr, ti->asb + CMD_CORRELATE_OFST); + writeb(0, ti->asb + RETCODE_OFST); + if ((xmit_command == XMIT_XID_CMD) || (xmit_command == XMIT_TEST_CMD)) { + writew(htons(0x11), ti->asb + FRAME_LENGTH_OFST); + writeb(0x0e, ti->asb + HEADER_LENGTH_OFST); + SET_PAGE(dhb_page); + writeb(AC, dhb); + writeb(LLC_FRAME, dhb + 1); + for (i = 0; i < TR_ALEN; i++) + writeb((int) 0x0FF, dhb + i + 2); + for (i = 0; i < TR_ALEN; i++) + writeb(0, dhb + i + TR_ALEN + 2); + writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); + return; } - /* - * the token ring packet is copied from sk_buff to the adapter - * buffer identified in the command data received with the interrupt. + * the token ring packet is copied from sk_buff to the adapter + * buffer identified in the command data received with the interrupt. */ - isa_writeb(hdr_len, ti->asb + offsetof(struct asb_xmit_resp, hdr_length)); - isa_writew(htons(ti->current_skb->len), - ti->asb + offsetof(struct asb_xmit_resp, frame_length)); - - SET_PAGE(dhb_page); - isa_memcpy_toio(dhb, ti->current_skb->data, ti->current_skb->len); - - isa_writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); - ti->tr_stats.tx_bytes+=ti->current_skb->len; + writeb(hdr_len, ti->asb + HEADER_LENGTH_OFST); + writew(htons(ti->current_skb->len), ti->asb + FRAME_LENGTH_OFST); + src_len=ti->current_skb->len; + src_offset=0; + dhb=dhb_base; + while(1) { + if (ti->page_mask) { + dhb_page=(dhb >> 8) & ti->page_mask; + dhb=dhb & ~(ti->page_mask << 8); + dhb_len=0x4000-dhb; /* remaining size of this page */ + } + dhb+=ti->sram_virt; + SET_PAGE(dhb_page); + if (src_len > dhb_len) { + memcpy_toio(dhb,&ti->current_skb->data[src_offset], + dhb_len); + src_len -= dhb_len; + src_offset += dhb_len; + dhb_base+=dhb_len; + dhb=dhb_base; + continue; + } + memcpy_toio(dhb, &ti->current_skb->data[src_offset], src_len); + break; + } + writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); + ti->tr_stats.tx_bytes += ti->current_skb->len; dev_kfree_skb_irq(ti->current_skb); - ti->current_skb=NULL; + ti->current_skb = NULL; netif_wake_queue(dev); - if (ti->readlog_pending) ibmtr_readlog(dev); -} + if (ti->readlog_pending) + ibmtr_readlog(dev); +} /*tr_tx */ + +/*****************************************************************************/ + + +#define RECEIVE_BUFFER_OFST 6 +#define LAN_HDR_LENGTH_OFST 8 +#define DLC_HDR_LENGTH_OFST 9 + +#define DSAP_OFST 0 +#define SSAP_OFST 1 +#define LLC_OFST 2 +#define PROTID_OFST 3 +#define ETHERTYPE_OFST 6 static void tr_rx(struct net_device *dev) { - struct tok_info *ti=(struct tok_info *) dev->priv; + struct tok_info *ti = (struct tok_info *) dev->priv; __u32 rbuffer, rbufdata; - __u8 rbuffer_page=0; + __u8 rbuffer_page = 0; __u32 llc; unsigned char *data; unsigned int rbuffer_len, lan_hdr_len, hdr_len, ip_len, length; + unsigned char dlc_hdr_len; struct sk_buff *skb; unsigned int skb_size = 0; - int IPv4_p = 0; + int IPv4_p = 0; unsigned int chksum = 0; struct iphdr *iph; struct arb_rec_req rarb; SET_PAGE(ti->arb_page); - isa_memcpy_fromio(&rarb, ti->arb, sizeof(rarb)); - rbuffer=ntohs(rarb.rec_buf_addr)+2; + memcpy_fromio(&rarb, ti->arb, sizeof(rarb)); + rbuffer = ntohs(rarb.rec_buf_addr) ; if (ti->page_mask) { - rbuffer_page=(rbuffer >> 8) & ti->page_mask; - rbuffer &= ~(ti->page_mask<<8); + rbuffer_page = (rbuffer >> 8) & ti->page_mask; + rbuffer &= ~(ti->page_mask << 8); } - rbuffer += ti->sram; - + rbuffer += ti->sram_virt; + SET_PAGE(ti->asb_page); - if(isa_readb(ti->asb + offsetof(struct asb_rec, ret_code))!=0xFF) - DPRINTK("ASB not free !!!\n"); - isa_writeb(REC_DATA, - ti->asb + offsetof(struct asb_rec, command)); - isa_writew(rarb.station_id, - ti->asb + offsetof(struct asb_rec, station_id)); - isa_writew(rarb.rec_buf_addr, - ti->asb + offsetof(struct asb_rec, rec_buf_addr)); + if (readb(ti->asb + RETCODE_OFST) !=0xFF) DPRINTK("ASB not free !!!\n"); - lan_hdr_len=rarb.lan_hdr_len; + writeb(REC_DATA, ti->asb + COMMAND_OFST); + writew(rarb.station_id, ti->asb + STATION_ID_OFST); + writew(rarb.rec_buf_addr, ti->asb + RECEIVE_BUFFER_OFST); + + lan_hdr_len = rarb.lan_hdr_len; + if (lan_hdr_len > sizeof(struct trh_hdr)) { + DPRINTK("Linux cannot handle greater than 18 bytes RIF\n"); + return; + } /*BMS I added this above just to be very safe */ + dlc_hdr_len = readb(ti->arb + DLC_HDR_LENGTH_OFST); hdr_len = lan_hdr_len + sizeof(struct trllc) + sizeof(struct iphdr); SET_PAGE(rbuffer_page); - llc=(rbuffer + offsetof(struct rec_buf, data) + lan_hdr_len); + llc = (rbuffer + offsetof(struct rec_buf, data) + lan_hdr_len); #if TR_VERBOSE DPRINTK("offsetof data: %02X lan_hdr_len: %02X\n", - (unsigned int)offsetof(struct rec_buf,data), (unsigned int)lan_hdr_len); - DPRINTK("llc: %08X rec_buf_addr: %04X ti->sram: %lx\n", llc, - ntohs(rarb.rec_buf_addr), - (long)ti->sram); + (__u32) offsetof(struct rec_buf, data), (unsigned int) lan_hdr_len); + DPRINTK("llc: %08X rec_buf_addr: %04X dev->mem_start: %lX\n", + llc, ntohs(rarb.rec_buf_addr), dev->mem_start); DPRINTK("dsap: %02X, ssap: %02X, llc: %02X, protid: %02X%02X%02X, " "ethertype: %04X\n", - (int)isa_readb(llc + offsetof(struct trllc, dsap)), - (int)isa_readb(llc + offsetof(struct trllc, ssap)), - (int)isa_readb(llc + offsetof(struct trllc, llc)), - (int)isa_readb(llc + offsetof(struct trllc, protid)), - (int)isa_readb(llc + offsetof(struct trllc, protid)+1), - (int)isa_readb(llc + offsetof(struct trllc, protid)+2), - (int)isa_readw(llc + offsetof(struct trllc, ethertype))); -#endif - if (isa_readb(llc + offsetof(struct trllc, llc))!=UI_CMD) { - SET_PAGE(ti->asb_page); - isa_writeb(DATA_LOST, ti->asb + offsetof(struct asb_rec, ret_code)); + (int) readb(llc + DSAP_OFST), (int) readb(llc + SSAP_OFST), + (int) readb(llc + LLC_OFST), (int) readb(llc + PROTID_OFST), + (int) readb(llc+PROTID_OFST+1),(int)readb(llc+PROTID_OFST + 2), + (int) ntohs(readw(llc + ETHERTYPE_OFST))); +#endif + if (readb(llc + offsetof(struct trllc, llc)) != UI_CMD) { + SET_PAGE(ti->asb_page); + writeb(DATA_LOST, ti->asb + RETCODE_OFST); ti->tr_stats.rx_dropped++; - isa_writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); + writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); return; } - length = ntohs(rarb.frame_len); - if ((isa_readb(llc + offsetof(struct trllc, dsap))==EXTENDED_SAP) && - (isa_readb(llc + offsetof(struct trllc, ssap))==EXTENDED_SAP) && - (length>=hdr_len)) { - IPv4_p = 1; - } - + if (readb(llc + DSAP_OFST) == EXTENDED_SAP && + readb(llc + SSAP_OFST) == EXTENDED_SAP && + length >= hdr_len) IPv4_p = 1; #if TR_VERBOSE - if (!IPv4_p){ +#define SADDR_OFST 8 +#define DADDR_OFST 2 - __u32 trhhdr; + if (!IPv4_p) { - trhhdr=(rbuffer+offsetof(struct rec_buf,data)); + __u32 trhhdr; - DPRINTK("Probably non-IP frame received.\n"); - DPRINTK("ssap: %02X dsap: %02X saddr: %02X:%02X:%02X:%02X:%02X:%02X " - "daddr: %02X:%02X:%02X:%02X:%02X:%02X\n", - (int)isa_readb(llc + offsetof(struct trllc, ssap)), - (int)isa_readb(llc + offsetof(struct trllc, dsap)), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, saddr)), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, saddr)+1), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, saddr)+2), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, saddr)+3), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, saddr)+4), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, saddr)+5), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, daddr)), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, daddr)+1), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, daddr)+2), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, daddr)+3), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, daddr)+4), - (int)isa_readb(trhhdr + offsetof(struct trh_hdr, daddr)+5)); - } -#endif - - skb_size = length; - - if (!(skb=dev_alloc_skb(skb_size))) { - DPRINTK("out of memory. frame dropped.\n"); - ti->tr_stats.rx_dropped++; + trhhdr = (rbuffer + offsetof(struct rec_buf, data)); + + DPRINTK("Probably non-IP frame received.\n"); + DPRINTK("ssap: %02X dsap: %02X " + "saddr: %02X:%02X:%02X:%02X:%02X:%02X " + "daddr: %02X:%02X:%02X:%02X:%02X:%02X\n", + readb(llc + SSAP_OFST), readb(llc + DSAP_OFST), + readb(trhhdr+SADDR_OFST), readb(trhhdr+ SADDR_OFST+1), + readb(trhhdr+SADDR_OFST+2), readb(trhhdr+SADDR_OFST+3), + readb(trhhdr+SADDR_OFST+4), readb(trhhdr+SADDR_OFST+5), + readb(trhhdr+DADDR_OFST), readb(trhhdr+DADDR_OFST + 1), + readb(trhhdr+DADDR_OFST+2), readb(trhhdr+DADDR_OFST+3), + readb(trhhdr+DADDR_OFST+4), readb(trhhdr+DADDR_OFST+5)); + } +#endif + + /*BMS handle the case she comes in with few hops but leaves with many */ + skb_size=length-lan_hdr_len+sizeof(struct trh_hdr)+sizeof(struct trllc); + + if (!(skb = dev_alloc_skb(skb_size))) { + DPRINTK("out of memory. frame dropped.\n"); + ti->tr_stats.rx_dropped++; SET_PAGE(ti->asb_page); - isa_writeb(DATA_LOST, ti->asb + offsetof(struct asb_rec, ret_code)); - isa_writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); - return; - } - - skb_put(skb, length); - skb_reserve(skb, sizeof(struct trh_hdr)-lan_hdr_len+sizeof(struct trllc)); - skb->dev=dev; - data=skb->data; - rbuffer_len=ntohs(isa_readw(rbuffer + offsetof(struct rec_buf, buf_len))); - rbufdata = rbuffer + offsetof(struct rec_buf,data); + writeb(DATA_LOST, ti->asb + offsetof(struct asb_rec, ret_code)); + writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); + return; + } + /*BMS again, if she comes in with few but leaves with many */ + skb_reserve(skb, sizeof(struct trh_hdr) - lan_hdr_len); + skb_put(skb, length); + skb->dev = dev; + data = skb->data; + rbuffer_len = ntohs(readw(rbuffer + offsetof(struct rec_buf, buf_len))); + rbufdata = rbuffer + offsetof(struct rec_buf, data); if (IPv4_p) { - /* Copy the headers without checksumming */ - isa_memcpy_fromio(data, rbufdata, hdr_len); + /* Copy the headers without checksumming */ + memcpy_fromio(data, rbufdata, hdr_len); /* Watch for padded packets and bogons */ - iph=(struct iphdr*)(data + lan_hdr_len + sizeof(struct trllc)); + iph= (struct iphdr *)(data+ lan_hdr_len + sizeof(struct trllc)); ip_len = ntohs(iph->tot_len) - sizeof(struct iphdr); length -= hdr_len; if ((ip_len <= length) && (ip_len > 7)) @@ -1751,107 +1769,123 @@ data += hdr_len; rbuffer_len -= hdr_len; rbufdata += hdr_len; - } - + } /* Copy the payload... */ +#define BUFFER_POINTER_OFST 2 +#define BUFFER_LENGTH_OFST 6 for (;;) { + if (ibmtr_debug_trace&TRC_INITV && length < rbuffer_len) + DPRINTK("CURIOUS, length=%d < rbuffer_len=%d\n", + length,rbuffer_len); if (IPv4_p) - chksum = csum_partial_copy_nocheck(bus_to_virt(rbufdata), data, - length < rbuffer_len ? length : rbuffer_len, - chksum); + chksum=csum_partial_copy_nocheck((void*)rbufdata, + data,lengthpage_mask) { - rbuffer_page=(rbuffer>>8) & ti->page_mask; - rbuffer &= ~(ti->page_mask << 8); + rbuffer_page = (rbuffer >> 8) & ti->page_mask; + rbuffer &= ~(ti->page_mask << 8); } - rbuffer += ti->sram; + rbuffer += ti->sram_virt; SET_PAGE(rbuffer_page); - rbuffer_len = ntohs(isa_readw(rbuffer + offsetof(struct rec_buf, buf_len))); + rbuffer_len = ntohs(readw(rbuffer + BUFFER_LENGTH_OFST)); rbufdata = rbuffer + offsetof(struct rec_buf, data); } SET_PAGE(ti->asb_page); - isa_writeb(0, ti->asb + offsetof(struct asb_rec, ret_code)); + writeb(0, ti->asb + offsetof(struct asb_rec, ret_code)); - isa_writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); + writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); ti->tr_stats.rx_bytes += skb->len; - ti->tr_stats.rx_packets++; + ti->tr_stats.rx_packets++; - skb->protocol = tr_type_trans(skb,dev); - if (IPv4_p && (skb->protocol == ETH_P_IP)) { - skb->csum = chksum; + skb->protocol = tr_type_trans(skb, dev); + if (IPv4_p) { + skb->csum = chksum; skb->ip_summed = 1; } netif_rx(skb); dev->last_rx = jiffies; +} /*tr_rx */ + +/*****************************************************************************/ + +void ibmtr_reset_timer(struct timer_list *tmr, struct net_device *dev) +{ + tmr->expires = jiffies + TR_RETRY_INTERVAL; + tmr->data = (unsigned long) dev; + tmr->function = tok_rerun; + init_timer(tmr); + add_timer(tmr); } -static int tok_send_packet(struct sk_buff *skb, struct net_device *dev) +/*****************************************************************************/ + +void tok_rerun(unsigned long dev_addr){ + + struct net_device *dev = (struct net_device *)dev_addr; + struct tok_info *ti = (struct tok_info *) dev->priv; + + if ( ti->open_action == RESTART){ + ti->do_tok_int = FIRST_INT; + outb(0, dev->base_addr + ADAPTRESETREL); +#ifdef ENABLE_PAGING + if (ti->page_mask) + writeb(SRPR_ENABLE_PAGING, + ti->mmio + ACA_OFFSET + ACA_RW + SRPR_EVEN); +#endif + + writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); + } else + tok_open_adapter(dev_addr); +} + +/*****************************************************************************/ + +void ibmtr_readlog(struct net_device *dev) { struct tok_info *ti; - unsigned long flags; - ti=(struct tok_info *) dev->priv; - netif_stop_queue(dev); - - /* lock against other CPUs */ - spin_lock_irqsave(&(ti->lock), flags); + ti = (struct tok_info *) dev->priv; - /* Save skb; we'll need it when the adapter asks for the data */ - ti->current_skb=skb; + ti->readlog_pending = 0; SET_PAGE(ti->srb_page); - isa_writeb(XMIT_UI_FRAME, ti->srb + offsetof(struct srb_xmit, command)); - isa_writew(ti->exsap_station_id, ti->srb - +offsetof(struct srb_xmit, station_id)); - isa_writeb(CMD_IN_SRB, (ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)); - spin_unlock_irqrestore(&(ti->lock), flags); - dev->trans_start=jiffies; - return 0; -} + writeb(DIR_READ_LOG, ti->srb); + writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); + writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); -void ibmtr_reset_timer(struct timer_list *tmr, struct net_device *dev) { - tmr->expires = jiffies + TR_RETRY_INTERVAL; - tmr->data = (unsigned long) dev; - tmr->function = tok_open_adapter; - init_timer(tmr); - add_timer(tmr); -} + netif_stop_queue(dev); -void ibmtr_readlog(struct net_device *dev) { - struct tok_info *ti; - ti=(struct tok_info *) dev->priv; - - ti->readlog_pending = 0; - SET_PAGE(ti->srb_page); - isa_writeb(DIR_READ_LOG, ti->srb); - isa_writeb(INT_ENABLE, ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN); - isa_writeb(CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); - - netif_stop_queue(dev); } +/*****************************************************************************/ + /* tok_get_stats(): Basically a scaffold routine which will return the address of the tr_statistics structure associated with this device -- the tr.... structure is an ethnet look-alike so at least for this iteration may suffice. */ -static struct net_device_stats * tok_get_stats(struct net_device *dev) { +static struct net_device_stats *tok_get_stats(struct net_device *dev) +{ struct tok_info *toki; - toki=(struct tok_info *) dev->priv; + toki = (struct tok_info *) dev->priv; return (struct net_device_stats *) &toki->tr_stats; } -int ibmtr_change_mtu(struct net_device *dev, int mtu) { +/*****************************************************************************/ + +int ibmtr_change_mtu(struct net_device *dev, int mtu) +{ struct tok_info *ti = (struct tok_info *) dev->priv; - + if (ti->ring_speed == 16 && mtu > ti->maxmtu16) return -EINVAL; if (ti->ring_speed == 4 && mtu > ti->maxmtu4) @@ -1860,12 +1894,12 @@ return 0; } -#ifndef PCMCIA +/*****************************************************************************/ #ifdef MODULE /* 3COM 3C619C supports 8 interrupts, 32 I/O ports */ -static struct net_device* dev_ibmtr[IBMTR_MAX_ADAPTERS]; -static int io[IBMTR_MAX_ADAPTERS] = {0xa20,0xa24}; +static struct net_device *dev_ibmtr[IBMTR_MAX_ADAPTERS]; +static int io[IBMTR_MAX_ADAPTERS] = { 0xa20, 0xa24 }; static int irq[IBMTR_MAX_ADAPTERS]; static int mem[IBMTR_MAX_ADAPTERS]; @@ -1875,50 +1909,61 @@ int init_module(void) { - int i; - for (i = 0; io[i] && (ibase_addr = io[i]; - dev_ibmtr[i]->irq = irq[i]; + dev_ibmtr[i]->base_addr = io[i]; + dev_ibmtr[i]->irq = irq[i]; dev_ibmtr[i]->mem_start = mem[i]; - dev_ibmtr[i]->init = &ibmtr_probe; - - if (register_trdev(dev_ibmtr[i]) != 0) { + dev_ibmtr[i]->init = &ibmtr_probe; + if (register_trdev(dev_ibmtr[i]) != 0) { kfree(dev_ibmtr[i]); dev_ibmtr[i] = NULL; - if (i == 0) { - printk("ibmtr: register_trdev() returned non-zero.\n"); - return -EIO; - } else { - return 0; - } - } + continue; + } + count++; } - return 0; -} + if (count) return 0; + printk("ibmtr: register_trdev() returned non-zero.\n"); + return -EIO; +} /*init_module */ void cleanup_module(void) { - int i; + int i,j; - for (i = 0; i < IBMTR_MAX_ADAPTERS; i++) - if (dev_ibmtr[i]) { - unregister_trdev(dev_ibmtr[i]); - free_irq(dev_ibmtr[i]->irq, dev_ibmtr[i]); - release_region(dev_ibmtr[i]->base_addr, IBMTR_IO_EXTENT); - kfree(dev_ibmtr[i]->priv); - kfree(dev_ibmtr[i]); - dev_ibmtr[i] = NULL; + for (i = 0; i < IBMTR_MAX_ADAPTERS; i++){ + if(!dev_ibmtr[i]) continue; + if (dev_ibmtr[i]->base_addr) { + outb(0,dev_ibmtr[i]->base_addr+ADAPTRESET); + for(j=jiffies+TR_RST_TIME; + time_before_eq(jiffies,j);) ; + outb(0,dev_ibmtr[i]->base_addr+ADAPTRESETREL); } + unregister_trdev(dev_ibmtr[i]); + free_irq(dev_ibmtr[i]->irq, dev_ibmtr[i]); + release_region(dev_ibmtr[i]->base_addr, IBMTR_IO_EXTENT); +#ifndef PCMCIA + { + struct tok_info *ti = (struct tok_info *)dev_ibmtr[i]->priv ; + iounmap((u32 *)ti->mmio) ; + iounmap((u32 *)ti->sram_virt) ; + } +#endif + kfree(dev_ibmtr[i]->priv); + kfree(dev_ibmtr[i]); + dev_ibmtr[i] = NULL; + } } -#endif /* MODULE */ -#endif +#endif /* MODULE */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/veth.c linux.ac/drivers/net/veth.c --- linux.vanilla/drivers/net/veth.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/net/veth.c Sat May 26 00:24:54 2001 @@ -0,0 +1,1719 @@ +/* File veth.c created by Kyle A. Lucke on Mon Aug 7 2000. */ + +/**************************************************************************/ +/* */ +/* IBM eServer iSeries Virtual Ethernet Device Driver */ +/* Copyright (C) 2001 Kyle A. Lucke (klucke@raleigh.ibm.com), IBM Corp. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ +/* USA */ +/* */ +/* This module contains the implementation of a virtual ethernet device */ +/* for use with iSeries LPAR Linux. It utilizes low-level message passing*/ +/* provided by the hypervisor to enable an ethernet-like network device */ +/* that can be used to enable inter-partition communications on the same */ +/* physical iSeries. */ +/* */ +/* The iSeries LPAR hypervisor has currently defined the ability for a */ +/* partition to communicate on up to 16 different virtual ethernets, all */ +/* dynamically configurable, at least for an OS/400 partition. The */ +/* dynamic nature is not supported for Linux yet. */ +/* */ +/* Each virtual ethernet a given Linux partition participates in will */ +/* cause a network device with the form vethXX to be created, where XX is */ +/* a decimal number from 0 to 15, corresponding to the virtual ethernet */ +/* the given netdevice talks on. This is slightly different from the */ +/* standard eth0, eth1, etc. way of naming network devices, but without */ +/* this little naming convention, it would not be as easy to configure */ +/* the tcpip interfaces to a given veth device, and if the partition */ +/* was configured to use a new virtual ethernet at some point, the devices*/ +/* would most likely get renumbered. */ +/* */ +/* This driver (and others like it on other partitions) is responsible */ +/* routing packets to and from other partitions. The MAC addresses used */ +/* by the virtual ethernets contain meaning, and should not be modified. */ +/* Doing so could disable the ability of your Linux partition to */ +/* communicate with the other OS/400 partitions on your physical iSeries. */ +/* Similarly, setting the MAC address to something other than the */ +/* "virtual burned-in" address is not allowed, for the same reason. */ +/* */ +/* Notes: */ +/* */ +/* 1. Although there is the capability to talk on multiple shared */ +/* ethernets to communicate to the same partition, each shared */ +/* ethernet to a given partition X will use a finite, shared amount */ +/* of hypervisor messages to do the communication. So having 2 shared */ +/* ethernets to the same remote partition DOES NOT double the */ +/* available bandwidth. Each of the 2 shared ethernets will share the */ +/* same bandwidth available to another. */ +/* */ +/* 2. It is allowed to have a virtual ethernet that does not communicate */ +/* with any other partition. It won't do anything, but it's allowed. */ +/* */ +/* 3. There is no "loopback" mode for a virtual ethernet device. If you */ +/* send a packet to your own mac address, it will just be dropped, you */ +/* won't get it on the receive side. Such a thing could be done, */ +/* but my default driver DOES NOT do so. */ +/* */ +/* 4. Multicast addressing is implemented via broadcasting the multicast */ +/* frames to other partitions. It is the responsibility of the */ +/* receiving partition to filter the addresses desired. */ +/* */ +/* 5. This module utilizes several different bottom half handlers for */ +/* non-high-use path function (setup, error handling, etc.). Multiple */ +/* bottom halves were used because only one would not keep up to the */ +/* much faster iSeries device drivers this Linux driver is talking to. */ +/* All hi-priority work (receiving frames, handling frame acks) is done*/ +/* in the interrupt handler for maximum performance. */ +/* */ +/* Tunable parameters: */ +/* */ +/* VethBuffersToAllocate: This compile time option defaults to 120. It can*/ +/* be safely changed to something greater or less than the default. It */ +/* controls how much memory Linux will allocate per remote partition it is*/ +/* communicating with. The user can play with this to see how it affects */ +/* performance, packets dropped, etc. Without trying to understand the */ +/* complete driver, it can be thought of as the maximum number of packets */ +/* outstanding to a remote partition at a time. */ +/* */ +/**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VETH_H +#include "veth.h" +#endif +#ifndef _HVLPCONFIG_H +#include +#endif +#ifndef _VETH_PROC_H +#include +#endif +#ifndef _HVTYPES_H +#include +#endif +#ifndef _ISERIES_PROC_H +#include +#endif +#include +#include + + +#define veth_printk(fmt, args...) \ +printk(KERN_INFO "%s: " fmt, __FILE__, ## args) + +#define veth_error_printk(fmt, args...) \ +printk(KERN_ERR "(%s:%3.3d) ERROR: " fmt, __FILE__, __LINE__ , ## args) + +static const char __initdata *version = +"v0.9 02/15/2001 Kyle Lucke, klucke@raleigh.ibm.com, klucke@us.ibm.com\n"; + +static int probed __initdata = 0; +#define VethBuffersToAllocate 120 + +static struct VethFabricMgr *mFabricMgr = NULL; +static struct proc_dir_entry * veth_proc_root = NULL; + +DECLARE_MUTEX_LOCKED(VethProcSemaphore); + +static int veth_open(struct net_device *dev); +static int veth_close(struct net_device *dev); +static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static void veth_handleEvent(struct HvLpEvent *, struct pt_regs *); +static void veth_handleAck(struct HvLpEvent *); +static void veth_handleInt(struct HvLpEvent *); +static void veth_openConnections(void); +static void veth_openConnection(u8, int lockMe); +static void veth_closeConnection(u8, int lockMe); +static void veth_intFinishOpeningConnections(void *, int number); +static void veth_finishOpeningConnections(void *); +static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *); +static int veth_multicast_wanted(struct VethPort *port, u64 dest); +static void veth_set_multicast_list(struct net_device *dev); + +static void veth_sendCap(struct VethLpConnection *); +static void veth_sendMonitor(struct VethLpConnection *); +static void veth_takeCap(struct VethLpConnection *, struct VethLpEvent *); +static void veth_takeCapAck(struct VethLpConnection *, struct VethLpEvent *); +static void veth_takeMonitorAck(struct VethLpConnection *, struct VethLpEvent *); +static void veth_msgsInit(struct VethLpConnection *connection); +static void veth_recycleMsg(struct VethLpConnection *, u16); +static void veth_capBh(struct VethLpConnection *); +static void veth_capAckBh(struct VethLpConnection *); +static void veth_monitorAckBh(struct VethLpConnection *); +static void veth_takeFrames(struct VethLpConnection *, struct VethLpEvent *); +static void veth_pTransmit(struct sk_buff *skb, HvLpIndex remoteLp, struct net_device *dev); +static struct net_device_stats *veth_get_stats(struct net_device *dev); +static void veth_intFinishMsgsInit(void *, int); +static void veth_finishMsgsInit(struct VethLpConnection *connection); +static void veth_intFinishCapBh(void *, int); +static void veth_finishCapBh(struct VethLpConnection *connection); +static void veth_finishCapBhLocked(struct VethLpConnection *connection); +static void veth_finishSendCap(struct VethLpConnection *connection); +static void veth_timedAck(unsigned long connectionPtr); +#ifdef MODULE +static void veth_waitForEnd(void); +#endif +static void veth_failMe(struct VethLpConnection *connection); + +int __init veth_probe(void) +{ + struct net_device *dev= NULL; + struct VethPort *port = NULL; + int vlansFound = 0; + int displayVersion = 0; + + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + int vlanIndex = 0; + + if (probed) + return -ENODEV; + probed = 1; + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + vlansFound++; + + dev = init_vethdev(NULL, sizeof(struct VethPort), vlanIndex); + + if (dev == NULL) { + veth_error_printk("Unable to allocate net_device structure!\n"); + break; + } + + if (!dev->priv) + dev->priv = kmalloc(sizeof(struct VethPort), GFP_KERNEL); + if (!dev->priv) { + veth_error_printk("Unable to allocate memory\n"); + return -ENOMEM; + } + + veth_printk("Found an ethernet device %s (veth=%d) (addr=%p)\n", dev->name, vlanIndex, dev); + port = mFabricMgr->mPorts[vlanIndex] = (struct VethPort *)dev->priv; + memset(port, 0, sizeof(struct VethPort)); + rwlock_init(&(port->mMcastGate)); + mFabricMgr->mPorts[vlanIndex]->mDev = dev; + + dev->dev_addr[0] = 0x02; + dev->dev_addr[1] = 0x01; + dev->dev_addr[2] = 0xFF; + dev->dev_addr[3] = vlanIndex; + dev->dev_addr[4] = 0xFF; + dev->dev_addr[5] = HvLpConfig_getLpIndex_outline(); + dev->mtu = 9000; + + memcpy(&(port->mMyAddress), dev->dev_addr, 6); + + dev->open = &veth_open; + dev->hard_start_xmit = &veth_start_xmit; + dev->stop = &veth_close; + dev->get_stats = veth_get_stats; + dev->set_multicast_list = &veth_set_multicast_list; + dev->do_ioctl = &veth_ioctl; + + /* display version info if adapter is found */ + if (!displayVersion) + { + /* set display flag to TRUE so that */ + /* we only display this string ONCE */ + displayVersion = 1; + veth_printk("%s", version); + } + + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + if (vlansFound > 0) + return 0; + else + return -ENODEV; +} + +#ifdef MODULE +MODULE_AUTHOR("Kyle Lucke , "); +MODULE_DESCRIPTION("iSeries Virtual ethernet driver"); + +DECLARE_MUTEX_LOCKED(VethModuleBhDone); +int VethModuleReopen = 1; + +void veth_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + int i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex_outline(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + int vlanIndex = 0; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + char name[10] = ""; + sprintf(name, "lp%d", i); + remove_proc_entry(name, veth_proc_root); + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + char name[10] = ""; + sprintf(name, "veth%d", vlanIndex); + remove_proc_entry(name, veth_proc_root); + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + remove_proc_entry("veth", iSeries_proc); + + up(&VethProcSemaphore); +} + +void veth_waitForEnd(void) +{ + up(&VethModuleBhDone); +} + +void __exit veth_module_cleanup(void) +{ + int i; + struct VethFabricMgr *myFm = mFabricMgr; + struct tq_struct myBottomHalf; + struct net_device *thisOne = NULL; + + VethModuleReopen = 0; + + for (i = 0; i < HvMaxArchitectedLps; ++i) + { + veth_closeConnection(i, 1); + } + + myBottomHalf.routine = (void *)(void *)veth_waitForEnd; + + queue_task(&myBottomHalf, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + down(&VethModuleBhDone); + + HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); + + mb(); + mFabricMgr = NULL; + mb(); + + down(&VethProcSemaphore); + + iSeries_proc_callback(&veth_proc_delete); + + down(&VethProcSemaphore); + + for (i = 0; i < HvMaxArchitectedLps; ++i) + { + if (myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs > 0) + { + mf_deallocateLpEvents(myFm->mConnection[i].mRemoteLp, + HvLpEvent_Type_VirtualLan, + myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs, + NULL, + NULL); + } + + if (myFm->mConnection[i].mMsgs != NULL) + { + kfree(myFm->mConnection[i].mMsgs); + } + } + + for (i = 0; i < HvMaxArchitectedVirtualLans; ++i) + { + if (myFm->mPorts[i] != NULL) + { + thisOne = myFm->mPorts[i]->mDev; + myFm->mPorts[i] = NULL; + + mb(); + + if (thisOne != NULL) + { + veth_printk("Unregistering %s (veth=%d)\n", thisOne->name, i); + unregister_netdev(thisOne); + } + } + } + + kfree(myFm); +} + +module_exit(veth_module_cleanup); +#endif + + +void veth_proc_init(struct proc_dir_entry *iSeries_proc) +{ + int i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex_outline(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + int vlanIndex = 0; + + + veth_proc_root = proc_mkdir("veth", iSeries_proc); + if (!veth_proc_root) return; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "lpar%d", i); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)i; + ent->read_proc = proc_veth_dump_connection; + ent->write_proc = NULL; + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "veth%d", vlanIndex); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)vlanIndex; + ent->read_proc = proc_veth_dump_port; + ent->write_proc = NULL; + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + up(&VethProcSemaphore); +} + +int __init veth_module_init(void) +{ + int status; + int i; + + mFabricMgr = kmalloc(sizeof(struct VethFabricMgr), GFP_KERNEL); + memset(mFabricMgr, 0, sizeof(struct VethFabricMgr)); + veth_printk("Initializing veth module, fabric mgr (address=%p)\n", mFabricMgr); + + mFabricMgr->mEyecatcher = 0x56455448464D4752ULL; + mFabricMgr->mThisLp = HvLpConfig_getLpIndex_outline(); + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + mFabricMgr->mConnection[i].mEyecatcher = 0x564554484C50434EULL; + veth_failMe(mFabricMgr->mConnection+i); + spin_lock_init(&mFabricMgr->mConnection[i].mAckGate); + spin_lock_init(&mFabricMgr->mConnection[i].mStatusGate); + } + + status = veth_probe(); + + if (status == 0) + { + veth_openConnections(); + } + + iSeries_proc_callback(&veth_proc_init); + + return status; +} + +module_init(veth_module_init); + +static void veth_failMe(struct VethLpConnection *connection) +{ + connection->mConnectionStatus.mSentCap = 0; + connection->mConnectionStatus.mCapAcked = 0; + connection->mConnectionStatus.mGotCap = 0; + connection->mConnectionStatus.mGotCapAcked = 0; + connection->mConnectionStatus.mSentMonitor = 0; + connection->mConnectionStatus.mFailed = 1; +} + +static int veth_open(struct net_device *dev) +{ + struct VethPort *port = (struct VethPort *)dev->priv; + + memset(&port->mStats, 0, sizeof(port->mStats)); + MOD_INC_USE_COUNT; + + netif_start_queue(dev); + + return 0; +} + +static int veth_close(struct net_device *dev) +{ + netif_stop_queue(dev); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct net_device_stats *veth_get_stats(struct net_device *dev) +{ + struct VethPort *port = (struct VethPort *)dev->priv; + + return(&port->mStats); +} + + +static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned char *frame = skb->data; + HvLpIndex remoteLp = frame[5]; + int i = 0; + int clone = 0; + + if (mFabricMgr == NULL) + { + veth_error_printk("NULL fabric manager with active ports!\n"); + netif_stop_queue(dev); + return 1; + } + + mb(); + + if ((*frame & 0x01) != 0x01) /* broadcast or multicast */ + { + if ((remoteLp != mFabricMgr->mThisLp) && + (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, remoteLp))) + veth_pTransmit(skb, remoteLp, dev); + } + else + { + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != mFabricMgr->mThisLp) + { + if (clone) + skb = skb_clone(skb, GFP_ATOMIC); + else + clone = 1; + + if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) + { + /* the ack handles deleting the skb */ + veth_pTransmit(skb, i, dev); + } + } + } + } + + return 0; +} + +static void veth_pTransmit(struct sk_buff *skb, HvLpIndex remoteLp, struct net_device *dev) +{ + struct VethLpConnection *connection = mFabricMgr->mConnection + remoteLp; + HvLpEvent_Rc returnCode; + + if (connection->mConnectionStatus.mFailed != 1) + { + struct VethMsg *msg = NULL; + VETHSTACKPOP(&(connection->mMsgStack), msg); + + if (msg != NULL) + { + if ((skb->len > 14) && + (skb->len <= 9018)) + { + dma_addr_t dma_addr = pci_map_single(NULL, + skb->data, + skb->len, + PCI_DMA_TODEVICE); + + if (dma_addr != -1) + { + msg->mSkb = skb; + msg->mEvent.mSendData.mAddress[0] = dma_addr; + msg->mEvent.mSendData.mLength[0] = skb->len; + msg->mEvent.mSendData.mEofMask = 0xFFFFFFFFUL; + + test_and_set_bit(0, &(msg->mInUse)); + + returnCode = HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFrames, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + msg->mIndex, + msg->mEvent.mFpData.mData1, + msg->mEvent.mFpData.mData2, + msg->mEvent.mFpData.mData3, + msg->mEvent.mFpData.mData4, + msg->mEvent.mFpData.mData5); + } + else + { + returnCode = -1; /* Bad return code */ + } + + if (returnCode != HvLpEvent_Rc_Good) + { + struct VethPort *port = (struct VethPort *)dev->priv; + + if (msg->mEvent.mSendData.mAddress[0]) + { + pci_unmap_single(NULL, dma_addr, skb->len, PCI_DMA_TODEVICE); + } + + dev_kfree_skb_irq(skb); + + msg->mSkb = NULL; + memset(&(msg->mEvent.mSendData), 0, sizeof(struct VethFramesData)); + VETHSTACKPUSH(&(connection->mMsgStack), msg); + port->mStats.tx_dropped++; + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_packets++; + port->mStats.tx_bytes += skb->len; + } + } + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_dropped++; + } + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_dropped++; + } +} + +static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + + return -EOPNOTSUPP; +} + +static void veth_set_multicast_list(struct net_device *dev) +{ + char *addrs; + struct VethPort *port = (struct VethPort *)dev->priv; + u64 newAddress = 0; + unsigned long flags; + + write_lock_irqsave(&port->mMcastGate, flags); + + if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ + port->mPromiscuous = 1; + } else { + struct dev_mc_list *dmi = dev->mc_list; + + if (dev->flags & IFF_ALLMULTI) { + port->mAllMcast = 1; + } else { + int i; + /* Update table */ + port->mNumAddrs = 0; + + for (i = 0; ((i < dev->mc_count) && (i < 12)); i++) { /* for each address in the list */ + addrs = dmi->dmi_addr; + dmi = dmi->next; + if ((*addrs & 0x01) == 1) { /* multicast address? */ + memcpy(&newAddress, addrs, 6); + newAddress &= 0xFFFFFFFFFFFF0000; + + port->mMcasts[port->mNumAddrs] = newAddress; + mb(); + port->mNumAddrs = port->mNumAddrs + 1; + } + } + } + } + + write_unlock_irqrestore(&port->mMcastGate, flags); +} + + +static void veth_handleEvent(struct HvLpEvent *event, struct pt_regs *regs) +{ + if (event->xFlags.xFunction == HvLpEvent_Function_Ack) + { + veth_handleAck(event); + } + else if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + veth_handleInt(event); + } +} + +static void veth_handleAck(struct HvLpEvent *event) +{ + struct VethLpConnection *connection = &(mFabricMgr->mConnection[event->xTargetLp]); + struct VethLpEvent *vethEvent = (struct VethLpEvent *)event; + + switch(event->xSubtype) + { + case VethEventTypeCap: + { + veth_takeCapAck(connection, vethEvent); + break; + } + case VethEventTypeMonitor: + { + veth_takeMonitorAck(connection, vethEvent); + break; + } + default: + { + veth_error_printk("Unknown ack type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp); + } + }; +} + +static void veth_handleInt(struct HvLpEvent *event) +{ + int i=0; + struct VethLpConnection *connection = &(mFabricMgr->mConnection[event->xSourceLp]); + struct VethLpEvent *vethEvent = (struct VethLpEvent *)event; + + switch(event->xSubtype) + { + case VethEventTypeCap: + { + veth_takeCap(connection, vethEvent); + break; + } + case VethEventTypeMonitor: + { + /* do nothing... this'll hang out here til we're dead, and the hypervisor will return it for us. */ + break; + } + case VethEventTypeFramesAck: + { + for (i=0; i < VethMaxFramesMsgsAcked; ++i) + { + u16 msg = vethEvent->mDerivedData.mFramesAckData.mToken[i]; + veth_recycleMsg(connection, msg); + } + break; + } + case VethEventTypeFrames: + { + veth_takeFrames(connection, vethEvent); + break; + } + default: + { + veth_error_printk("Unknown interrupt type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp); + } + }; +} + +static void veth_openConnections() +{ + int i=0; + + HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan, &veth_handleEvent); + + /* Now I need to run through the active lps and open connections to the ones I'm supposed to + open to. */ + + for (i=HvMaxArchitectedLps-1; i >=0; --i) + { + if (i != mFabricMgr->mThisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) + { + veth_openConnection(i, 1); + } + else + { + veth_closeConnection(i, 1); + } + } + } +} + +static void veth_intFinishOpeningConnections(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + connection->mNumberAllocated = number; + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_finishOpeningConnections(void *parm) +{ + unsigned long flags; + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + spin_lock_irqsave(&connection->mStatusGate, flags); + veth_finishOpeningConnectionsLocked(connection); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *connection) +{ + if (connection->mNumberAllocated >= 2) + { + connection->mConnectionStatus.mCapMonAlloced = 1; + veth_sendCap(connection); + } + else + { + veth_error_printk("Couldn't allocate base msgs for lpar %d, only got %d\n", connection->mRemoteLp, connection->mNumberAllocated); + veth_failMe(connection); + } +} + +static void veth_openConnection(u8 remoteLp, int lockMe) +{ + unsigned long flags; + unsigned long flags2; + HvLpInstanceId source; + HvLpInstanceId target; + u64 i = 0; + struct VethLpConnection *connection = &(mFabricMgr->mConnection[remoteLp]); + + memset(&connection->mCapBhTq, 0, sizeof(connection->mCapBhTq)); + connection->mCapBhTq.routine = (void *)(void *)veth_capBh; + + memset(&connection->mCapAckBhTq, 0, sizeof(connection->mCapAckBhTq)); + connection->mCapAckBhTq.routine = (void *)(void *)veth_capAckBh; + + memset(&connection->mMonitorAckBhTq, 0, sizeof(connection->mMonitorAckBhTq)); + connection->mMonitorAckBhTq.routine = (void *)(void *)veth_monitorAckBh; + + memset(&connection->mAllocBhTq, 0, sizeof(connection->mAllocBhTq)); + connection->mAllocBhTq.routine = (void *)(void *)veth_finishOpeningConnections; + + if (lockMe) + spin_lock_irqsave(&connection->mStatusGate, flags); + + connection->mRemoteLp = remoteLp; + + spin_lock_irqsave(&connection->mAckGate, flags2); + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + connection->mNumAcks = 0; + + HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan); + + /* clean up non-acked msgs */ + for (i=0; i < connection->mNumMsgs; ++i) + { + veth_recycleMsg(connection, i); + } + + connection->mConnectionStatus.mOpen = 1; + + source = connection->mSourceInst = HvCallEvent_getSourceLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan); + target = connection->mTargetInst = HvCallEvent_getTargetLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan); + + if (connection->mConnectionStatus.mCapMonAlloced != 1) + { + connection->mAllocBhTq.routine = (void *)(void *)veth_finishOpeningConnections; + mf_allocateLpEvents(remoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + 2, + &veth_intFinishOpeningConnections, + connection); + } + else + { + veth_finishOpeningConnectionsLocked(connection); + } + + spin_unlock_irqrestore(&connection->mAckGate, flags2); + + if (lockMe) + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_closeConnection(u8 remoteLp, int lockMe) +{ + struct VethLpConnection *connection = &(mFabricMgr->mConnection[remoteLp]); + unsigned long flags; + unsigned long flags2; + if (lockMe) + spin_lock_irqsave(&connection->mStatusGate, flags); + + if (connection->mConnectionStatus.mOpen == 1) + { + HvCallEvent_closeLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan); + connection->mConnectionStatus.mOpen = 0; + veth_failMe(connection); + + /* reset ack data */ + spin_lock_irqsave(&connection->mAckGate, flags2); + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + connection->mNumAcks = 0; + + spin_unlock_irqrestore(&connection->mAckGate, flags2); + } + + if (lockMe) + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_msgsInit(struct VethLpConnection *connection) +{ + connection->mAllocBhTq.routine = (void *)(void *)veth_finishMsgsInit; + mf_allocateLpEvents(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + connection->mMyCap.mUnionData.mFields.mNumberBuffers, + &veth_intFinishMsgsInit, + connection); +} + +static void veth_intFinishMsgsInit(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + connection->mNumberRcvMsgs = number; + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_intFinishCapBh(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + if (number > 0) + connection->mNumberLpAcksAlloced += number; + + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_finishMsgsInit(struct VethLpConnection *connection) +{ + int i=0; + unsigned int numberGotten = 0; + u64 amountOfHeapToGet = connection->mMyCap.mUnionData.mFields.mNumberBuffers * sizeof(struct VethMsg); + char *msgs = NULL; + unsigned long flags; + spin_lock_irqsave(&connection->mStatusGate, flags); + + if (connection->mNumberRcvMsgs >= connection->mMyCap.mUnionData.mFields.mNumberBuffers) + { + msgs = kmalloc(amountOfHeapToGet, GFP_ATOMIC); + + connection->mMsgs = (struct VethMsg *)msgs; + + if (msgs != NULL) + { + memset(msgs, 0, amountOfHeapToGet); + + for (i=0; i < connection->mMyCap.mUnionData.mFields.mNumberBuffers; ++i) + { + connection->mMsgs[i].mIndex = i; + ++numberGotten; + VETHSTACKPUSH(&(connection->mMsgStack), (connection->mMsgs+i)); + } + if (numberGotten > 0) + { + connection->mNumMsgs = numberGotten; + } + } + else + { + kfree(msgs); + connection->mMsgs = NULL; + } + } + + connection->mMyCap.mUnionData.mFields.mNumberBuffers = connection->mNumMsgs; + + if (connection->mNumMsgs < 10) + connection->mMyCap.mUnionData.mFields.mThreshold = 1; + else if (connection->mNumMsgs < 20) + connection->mMyCap.mUnionData.mFields.mThreshold = 4; + else if (connection->mNumMsgs < 40) + connection->mMyCap.mUnionData.mFields.mThreshold = 10; + else + connection->mMyCap.mUnionData.mFields.mThreshold = 20; + + connection->mMyCap.mUnionData.mFields.mTimer = VethAckTimeoutUsec; + + veth_finishSendCap(connection); + + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_sendCap(struct VethLpConnection *connection) +{ + if (connection->mMsgs == NULL) + { + connection->mMyCap.mUnionData.mFields.mNumberBuffers = VethBuffersToAllocate; + veth_msgsInit(connection); + } + else + { + veth_finishSendCap(connection); + } +} + +static void veth_finishSendCap(struct VethLpConnection *connection) +{ + HvLpEvent_Rc returnCode = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeCap, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mMyCap.mUnionData.mNoFields.mReserved1, + connection->mMyCap.mUnionData.mNoFields.mReserved2, + connection->mMyCap.mUnionData.mNoFields.mReserved3, + connection->mMyCap.mUnionData.mNoFields.mReserved4, + connection->mMyCap.mUnionData.mNoFields.mReserved5); + + if ((returnCode == HvLpEvent_Rc_PartitionDead) || + (returnCode == HvLpEvent_Rc_PathClosed)) + { + connection->mConnectionStatus.mSentCap = 0; + } + else if (returnCode != HvLpEvent_Rc_Good) + { + veth_error_printk("Couldn't send cap to lpar %d, rc %Lx\n", connection->mRemoteLp, returnCode); + veth_failMe(connection); + } + else + { + connection->mConnectionStatus.mSentCap = 1; + } +} + +static void veth_takeCap(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mCapBhPending))) + { + connection->mCapBhTq.data = connection; + memcpy(&connection->mCapEvent, event, sizeof(connection->mCapEvent)); + queue_task(&connection->mCapBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a capabilities from lpar %d while already processing one\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } +} + +static void veth_takeCapAck(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mCapAckBhPending))) + { + connection->mCapAckBhTq.data = connection; + memcpy(&connection->mCapAckEvent, event, sizeof(connection->mCapAckEvent)); + queue_task(&connection->mCapAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a capabilities ack from lpar %d while already processing one\n", connection->mRemoteLp); + } +} + +static void veth_takeMonitorAck(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mMonitorAckBhPending))) + { + connection->mMonitorAckBhTq.data = connection; + memcpy(&connection->mMonitorAckEvent, event, sizeof(connection->mMonitorAckEvent)); + queue_task(&connection->mMonitorAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a monitor ack from lpar %d while already processing one\n", connection->mRemoteLp); + } +} + +static void veth_recycleMsg(struct VethLpConnection *connection, u16 msg) +{ + if (msg < connection->mNumMsgs) + { + struct VethMsg *myMsg = connection->mMsgs + msg; + if (test_and_clear_bit(0, &(myMsg->mInUse))) + { + pci_unmap_single(NULL, + myMsg->mEvent.mSendData.mAddress[0], + myMsg->mEvent.mSendData.mLength[0], + PCI_DMA_TODEVICE); + dev_kfree_skb_irq(myMsg->mSkb); + + myMsg->mSkb = NULL; + memset(&(myMsg->mEvent.mSendData), 0, sizeof(struct VethFramesData)); + VETHSTACKPUSH(&connection->mMsgStack, myMsg); + } + else + { + if (connection->mConnectionStatus.mOpen) + { + veth_error_printk("Received a frames ack for msg %d from lpar %d while not outstanding\n", msg, connection->mRemoteLp); + } + } + } +} + +static void veth_capBh(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapEvent; + unsigned long flags; + struct VethCapData *remoteCap = &(connection->mRemoteCap); + u64 numAcks = 0; + spin_lock_irqsave(&connection->mStatusGate, flags); + connection->mConnectionStatus.mGotCap = 1; + + memcpy(remoteCap, &(event->mDerivedData.mCapabilitiesData), sizeof(connection->mRemoteCap)); + + if ((remoteCap->mUnionData.mFields.mNumberBuffers <= VethMaxFramesMsgs) && + (remoteCap->mUnionData.mFields.mNumberBuffers != 0) && + (remoteCap->mUnionData.mFields.mThreshold <= VethMaxFramesMsgsAcked) && + (remoteCap->mUnionData.mFields.mThreshold != 0)) + { + numAcks = (remoteCap->mUnionData.mFields.mNumberBuffers / remoteCap->mUnionData.mFields.mThreshold) + 1; + + if (connection->mNumberLpAcksAlloced < numAcks) + { + numAcks = numAcks - connection->mNumberLpAcksAlloced; + connection->mAllocBhTq.routine = (void *)(void *)veth_finishCapBh; + mf_allocateLpEvents(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + numAcks, + &veth_intFinishCapBh, + connection); + } + else + veth_finishCapBhLocked(connection); + } + else + { + veth_error_printk("Received incompatible capabilities from lpar %d\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_InvalidSubtypeData; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } + + clear_bit(0,&(connection->mCapBhPending)); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_capAckBh(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapAckEvent; + unsigned long flags; + + spin_lock_irqsave(&connection->mStatusGate, flags); + + if (event->mBaseEvent.xRc == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mCapAcked = 1; + + if ((connection->mConnectionStatus.mGotCap == 1) && + (connection->mConnectionStatus.mGotCapAcked == 1)) + { + if (connection->mConnectionStatus.mSentMonitor != 1) + veth_sendMonitor(connection); + } + } + else + { + veth_error_printk("Bad rc(%d) from lpar %d on capabilities\n", event->mBaseEvent.xRc, connection->mRemoteLp); + veth_failMe(connection); + } + + clear_bit(0,&(connection->mCapAckBhPending)); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_monitorAckBh(struct VethLpConnection *connection) +{ + unsigned long flags; + + spin_lock_irqsave(&connection->mStatusGate, flags); + + veth_failMe(connection); + + veth_printk("Monitor ack returned for lpar %d\n", connection->mRemoteLp); + del_timer(&connection->mAckTimer); + + if (connection->mConnectionStatus.mOpen) + { + veth_closeConnection(connection->mRemoteLp, 0); + + udelay(100); + + queue_task(&connection->mMonitorAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { +#ifdef MODULE + if (VethModuleReopen) +#endif + veth_openConnection(connection->mRemoteLp, 0); +#ifdef MODULE + else + { + int i=0; + + for (i=0; i < connection->mNumMsgs; ++i) + { + veth_recycleMsg(connection, i); + } + } +#endif + clear_bit(0,&(connection->mMonitorAckBhPending)); + } + + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_takeFrames(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + int i; + struct VethPort *port = NULL; + + for (i=0; i < VethMaxFramesPerMsg; ++i) + { + u16 length = event->mDerivedData.mSendData.mLength[i]; + u64 address = event->mDerivedData.mSendData.mAddress[i]; + if ((address != 0) && + (length <= 9018) && + (length > 14)) + { + struct sk_buff *skb = alloc_skb(event->mDerivedData.mSendData.mLength[i], GFP_ATOMIC); + + if (skb != NULL) + { + dma_addr_t toAddress = -1; + HvLpDma_Rc returnCode = HvLpDma_Rc_Good; + toAddress = pci_map_single(NULL, skb->data, + length, + PCI_DMA_FROMDEVICE); + + if (toAddress != -1) + { + returnCode = HvCallEvent_dmaSingle(HvLpEvent_Type_VirtualLan, + event->mBaseEvent.xSourceLp, + HvLpDma_Direction_RemoteToLocal, + connection->mSourceInst, + connection->mTargetInst, + HvLpDma_AddressType_TceIndex, + HvLpDma_AddressType_TceIndex, + toAddress, + address, + length); + + if (returnCode == HvLpDma_Rc_Good) + { + HvLpVirtualLanIndex vlan = skb->data[9]; + u64 dest = *((u64 *)skb->data) & 0xFFFFFFFFFFFF0000; + port = mFabricMgr->mPorts[vlan]; + + if (((vlan < HvMaxArchitectedVirtualLans) && + (port != NULL)) && + ((dest == port->mMyAddress) || /* it's for me */ + (dest == 0xFFFFFFFFFFFF0000) || /* it's a broadcast */ + (veth_multicast_wanted(port, dest)) || /* it's one of my multicasts */ + (port->mPromiscuous == 1))) /* I'm promiscuous */ + { + skb_put(skb, length); + skb->dev = mFabricMgr->mPorts[vlan]->mDev; + skb->protocol = eth_type_trans(skb, mFabricMgr->mPorts[vlan]->mDev); + skb->ip_summed = CHECKSUM_NONE; + netif_rx(skb); /* send it up */ + port->mStats.rx_packets++; + port->mStats.rx_bytes += length; + + } + else + { + dev_kfree_skb_irq(skb); + } + } + else + { + dev_kfree_skb_irq(skb); + } + + pci_unmap_single(NULL, + toAddress, + length, + PCI_DMA_FROMDEVICE); + } + else + { + dev_kfree_skb_irq(skb); + } + } + } + } + /* Ack it */ + + { + unsigned long flags; + spin_lock_irqsave(&connection->mAckGate, flags); + + if (connection->mNumAcks < VethMaxFramesMsgsAcked) + { + connection->mEventData.mAckData.mToken[connection->mNumAcks] = event->mBaseEvent.xCorrelationToken; + ++connection->mNumAcks; + + if (connection->mNumAcks == connection->mRemoteCap.mUnionData.mFields.mThreshold) + { + HvLpEvent_Rc rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFramesAck, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mEventData.mFpData.mData1, + connection->mEventData.mFpData.mData2, + connection->mEventData.mFpData.mData3, + connection->mEventData.mFpData.mData4, + connection->mEventData.mFpData.mData5); + + if (rc != HvLpEvent_Rc_Good) + { + veth_error_printk("Bad lp event return code(%Lx) acking frames from lpar %d\n", rc, connection->mRemoteLp); + } + + connection->mNumAcks = 0; + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + } + + } + + spin_unlock_irqrestore(&connection->mAckGate, flags); + } +} + +static void veth_timedAck(unsigned long connectionPtr) +{ + unsigned long flags; + HvLpEvent_Rc rc; + struct VethLpConnection *connection = (struct VethLpConnection *) connectionPtr; + /* Ack all the events */ + spin_lock_irqsave(&connection->mAckGate, flags); + + if (connection->mNumAcks > 0) + { + rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFramesAck, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mEventData.mFpData.mData1, + connection->mEventData.mFpData.mData2, + connection->mEventData.mFpData.mData3, + connection->mEventData.mFpData.mData4, + connection->mEventData.mFpData.mData5); + + if (rc != HvLpEvent_Rc_Good) + { + veth_error_printk("Bad lp event return code(%Lx) acking frames from lpar %d!\n", rc, connection->mRemoteLp); + } + + connection->mNumAcks = 0; + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + } + + spin_unlock_irqrestore(&connection->mAckGate, flags); + + /* Reschedule the timer */ + connection->mAckTimer.expires = jiffies + connection->mTimeout; + add_timer(&connection->mAckTimer); +} + +static int veth_multicast_wanted(struct VethPort *port, u64 thatAddr) +{ + int returnParm = 0; + int i; + unsigned long flags; + + if ((*((char *)&thatAddr) & 0x01) != 1) + return 0; + + read_lock_irqsave(&port->mMcastGate, flags); + if (port->mAllMcast) + return 1; + + for (i=0; i < port->mNumAddrs; ++i) + { + u64 thisAddr = port->mMcasts[i]; + + if (thisAddr == thatAddr) + { + returnParm = 1; + break; + } + } + read_unlock_irqrestore(&port->mMcastGate, flags); + + return returnParm; +} + +static void veth_sendMonitor(struct VethLpConnection *connection) +{ + HvLpEvent_Rc returnCode = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeMonitor, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_DeferredAck, + connection->mSourceInst, + connection->mTargetInst, + 0, 0, 0, 0, 0, 0); + + if (returnCode == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mSentMonitor = 1; + connection->mConnectionStatus.mFailed = 0; + + /* Start the ACK timer */ + init_timer(&connection->mAckTimer); + connection->mAckTimer.function = veth_timedAck; + connection->mAckTimer.data = (unsigned long) connection; + connection->mAckTimer.expires = jiffies + connection->mTimeout; + add_timer(&connection->mAckTimer); + + } + else + { + veth_error_printk("Monitor send to lpar %d failed with rc %Lx\n", connection->mRemoteLp, returnCode); + veth_failMe(connection); + } +} + +static void veth_finishCapBh(struct VethLpConnection *connection) +{ + unsigned long flags; + spin_lock_irqsave(&connection->mStatusGate, flags); + veth_finishCapBhLocked(connection); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_finishCapBhLocked(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapEvent; + struct VethCapData *remoteCap = &(connection->mRemoteCap); + int numAcks = (remoteCap->mUnionData.mFields.mNumberBuffers / remoteCap->mUnionData.mFields.mThreshold) + 1; + + /* Convert timer to jiffies */ + if (connection->mMyCap.mUnionData.mFields.mTimer) + connection->mTimeout = remoteCap->mUnionData.mFields.mTimer * HZ / 1000000; + else + connection->mTimeout = VethAckTimeoutUsec * HZ / 1000000; + + if (connection->mNumberLpAcksAlloced >= numAcks) + { + HvLpEvent_Rc returnCode = HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + + if (returnCode == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mGotCapAcked = 1; + + if (connection->mConnectionStatus.mSentCap != 1) + { + connection->mTargetInst = HvCallEvent_getTargetLpInstanceId(connection->mRemoteLp, HvLpEvent_Type_VirtualLan); + + veth_sendCap(connection); + } + else if (connection->mConnectionStatus.mCapAcked == 1) + { + if (connection->mConnectionStatus.mSentMonitor != 1) + veth_sendMonitor(connection); + } + } + else + { + veth_error_printk("Failed to ack remote cap for lpar %d with rc %Lx\n", connection->mRemoteLp, returnCode); + veth_failMe(connection); + } + } + else + { + veth_error_printk("Couldn't allocate all the frames ack events for lpar %d\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } +} + +int proc_veth_dump_connection +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *out = page; + int whichConnection = (int) data; + int len = 0; + struct VethLpConnection *connection = NULL; + + if ((whichConnection < 0) || (whichConnection > HvMaxArchitectedLps) || (mFabricMgr == NULL)) + { + veth_error_printk("Got bad data from /proc file system\n"); + len = sprintf(page, "ERROR\n"); + } + else + { + int thereWasStuffBefore = 0; + connection = &(mFabricMgr->mConnection[whichConnection]); + + out += sprintf(out, "Remote Lp:\t%d\n", connection->mRemoteLp); + out += sprintf(out, "Source Inst:\t%04X\n", connection->mSourceInst); + out += sprintf(out, "Target Inst:\t%04X\n", connection->mTargetInst); + out += sprintf(out, "Num Msgs:\t%d\n", connection->mNumMsgs); + out += sprintf(out, "Num Lp Acks:\t%d\n", connection->mNumberLpAcksAlloced); + out += sprintf(out, "Num Acks:\t%d\n", connection->mNumAcks); + + if (connection->mConnectionStatus.mOpen) + { + out += sprintf(out, "mConnectionStatus.mCapMonAlloced) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "CapMonAlloced"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mBaseMsgsAlloced) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "BaseMsgsAlloced"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mSentCap) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "SentCap"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mCapAcked) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "CapAcked"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mGotCap) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "GotCap"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mGotCapAcked) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "GotCapAcked"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mSentMonitor) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "SentMonitor"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mPopulatedRings) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "PopulatedRings"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mFailed) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "Failed"); + thereWasStuffBefore = 1; + } + + if (thereWasStuffBefore) + out += sprintf(out, ">"); + + out += sprintf(out, "\n"); + + out += sprintf(out, "Capabilities (System:):\n"); + out += sprintf(out, "\tLocal:<"); + out += sprintf(out, "%d/%d/%d/%d>\n", + connection->mMyCap.mUnionData.mFields.mVersion, + connection->mMyCap.mUnionData.mFields.mNumberBuffers, + connection->mMyCap.mUnionData.mFields.mThreshold, + connection->mMyCap.mUnionData.mFields.mTimer); + out += sprintf(out, "\tRemote:<"); + out += sprintf(out, "%d/%d/%d/%d>\n", + connection->mRemoteCap.mUnionData.mFields.mVersion, + connection->mRemoteCap.mUnionData.mFields.mNumberBuffers, + connection->mRemoteCap.mUnionData.mFields.mThreshold, + connection->mRemoteCap.mUnionData.mFields.mTimer); + len = out - page; + } + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_veth_dump_port +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *out = page; + int whichPort = (int) data; + int len = 0; + struct VethPort *port = NULL; + + if ((whichPort < 0) || (whichPort > HvMaxArchitectedVirtualLans) || (mFabricMgr == NULL)) + len = sprintf(page, "Virtual ethernet is not configured.\n"); + else + { + int i=0; + u32 *myAddr; + u16 *myEndAddr; + port = mFabricMgr->mPorts[whichPort]; + + if (port != NULL) + { + myAddr = (u32 *)&(port->mMyAddress); + myEndAddr = (u16 *)(myAddr + 1); + out += sprintf(out, "Net device:\t%p\n", port->mDev); + out += sprintf(out, "Address:\t%08X%04X\n", myAddr[0], myEndAddr[0]); + out += sprintf(out, "Promiscuous:\t%d\n", port->mPromiscuous); + out += sprintf(out, "All multicast:\t%d\n", port->mAllMcast); + out += sprintf(out, "Number multicast:\t%d\n", port->mNumAddrs); + + for (i=0; i < port->mNumAddrs; ++i) + { + u32 *multi = (u32 *)&(port->mMcasts[i]); + u16 *multiEnd = (u16 *)(multi + 1); + out += sprintf(out, " %08X%04X\n", multi[0], multiEnd[0]); + } + } + else + { + out += sprintf(page, "veth%d is not configured.\n", whichPort); + } + + len = out - page; + } + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/veth.h linux.ac/drivers/net/veth.h --- linux.vanilla/drivers/net/veth.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/net/veth.h Sat May 26 00:24:54 2001 @@ -0,0 +1,255 @@ +/* File veth.h created by Kyle A. Lucke on Mon Aug 7 2000. */ + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _VETH_H +#define _VETH_H + +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPEVENT_H +#include +#endif +#include + +#define VethEventNumTypes (4) +#define VethEventTypeCap (0) +#define VethEventTypeFrames (1) +#define VethEventTypeMonitor (2) +#define VethEventTypeFramesAck (3) + +#define VethMaxFramesMsgsAcked (20) +#define VethMaxFramesMsgs (0xFFFF) +#define VethMaxFramesPerMsg (6) +#define VethAckTimeoutUsec (1000000) + +#define VETHSTACKTYPE(T) struct VethStack##T +#define VETHSTACK(T) \ +VETHSTACKTYPE(T) \ +{ \ +struct T *head; \ +spinlock_t lock; \ +} +#define VETHSTACKCTOR(s) do { (s)->head = NULL; spin_lock_init(&(s)->lock); } while(0) +#define VETHSTACKPUSH(s, p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(s)->lock,flags); \ +(p)->next = (s)->head; \ +(s)->head = (p); \ +spin_unlock_irqrestore(&(s)->lock, flags); \ +} while(0) + +#define VETHSTACKPOP(s,p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(s)->lock,flags); \ +(p) = (s)->head; \ +if ((s)->head != NULL) \ +{ \ +(s)->head = (s)->head->next; \ +} \ +spin_unlock_irqrestore(&(s)->lock, flags); \ +} while(0) + +#define VETHQUEUE(T) \ +struct VethQueue##T \ +{ \ +T *head; \ +T *tail; \ +spinlock_t lock; \ +} +#define VETHQUEUECTOR(q) do { (q)->head = NULL; (q)->tail = NULL; spin_lock_init(&(q)->lock); } while(0) +#define VETHQUEUEENQ(q, p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(q)->lock,flags); \ +(p)->next = NULL; \ +if ((q)->head != NULL) \ +{ \ +(q)->head->next = (p); \ +(q)->head = (p); \ +} \ +else \ +{ \ +(q)->tail = (q)->head = (p); \ +} \ +spin_unlock_irqrestore(&(q)->lock, flags); \ +} while(0) + +#define VETHQUEUEDEQ(q,p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(q)->lock,flags); \ +(p) = (q)->tail; \ +if ((p) != NULL) \ +{ \ +(q)->tail = (p)->next; \ +(p)->next = NULL; \ +} \ +if ((q)->tail == NULL) \ +(q)->head = NULL; \ +spin_unlock_irqrestore(&(q)->lock, flags); \ +} while(0) + +struct VethFramesData +{ + u32 mAddress[6]; + u16 mLength[6]; + u32 mEofMask; +}; + +struct VethFramesAckData +{ + u16 mToken[VethMaxFramesMsgsAcked]; +}; + +struct VethCapData +{ + union + { + struct Fields + { + u8 mVersion; + u8 mReserved1; + u16 mNumberBuffers; + u16 mThreshold; + u16 mReserved2; + u32 mTimer; + u32 mReserved3; + u64 mReserved4; + u64 mReserved5; + u64 mReserved6; + } mFields; + struct NoFields + { + u64 mReserved1; + u64 mReserved2; + u64 mReserved3; + u64 mReserved4; + u64 mReserved5; + } mNoFields; + } mUnionData; +}; + +struct VethFastPathData +{ + u64 mData1; + u64 mData2; + u64 mData3; + u64 mData4; + u64 mData5; +}; + +struct VethLpEvent +{ + struct HvLpEvent mBaseEvent; + union { + struct VethFramesData mSendData; + struct VethCapData mCapabilitiesData; + struct VethFramesAckData mFramesAckData; + struct VethFastPathData mFastPathData; + } mDerivedData; + +}; + +struct VethMsg +{ + struct VethMsg *next; + union { + struct VethFramesData mSendData; + struct VethFastPathData mFpData; + } mEvent; + int mIndex; + unsigned long mInUse; + struct sk_buff *mSkb; +}; + + +struct VethControlBlock +{ + struct net_device *mDev; + struct VethControlBlock *mNext; + HvLpVirtualLanIndex mVlanId; +}; + +struct VethLpConnection +{ + u64 mEyecatcher; + HvLpIndex mRemoteLp; + HvLpInstanceId mSourceInst; + HvLpInstanceId mTargetInst; + u32 mNumMsgs; + struct VethMsg *mMsgs; + int mNumberRcvMsgs; + int mNumberLpAcksAlloced; + union + { + struct VethFramesAckData mAckData; + struct VethFastPathData mFpData; + } mEventData; + spinlock_t mAckGate; + u32 mNumAcks; + spinlock_t mStatusGate; + struct + { + u64 mOpen : 1; + u64 mCapMonAlloced : 1; + u64 mBaseMsgsAlloced : 1; + u64 mSentCap : 1; + u64 mCapAcked : 1; + u64 mGotCap : 1; + u64 mGotCapAcked : 1; + u64 mSentMonitor : 1; + u64 mPopulatedRings : 1; + u64 mReserved : 54; + u64 mFailed : 1; + } mConnectionStatus; + struct VethCapData mMyCap; + struct VethCapData mRemoteCap; + unsigned long mCapAckBhPending; + struct tq_struct mCapAckBhTq; + struct VethLpEvent mCapAckEvent; + unsigned long mCapBhPending; + struct tq_struct mCapBhTq; + struct VethLpEvent mCapEvent; + unsigned long mMonitorAckBhPending; + struct tq_struct mMonitorAckBhTq; + struct VethLpEvent mMonitorAckEvent; + unsigned long mAllocBhPending; + struct tq_struct mAllocBhTq; + int mNumberAllocated; + struct timer_list mAckTimer; + u32 mTimeout; + VETHSTACK(VethMsg) mMsgStack; +}; +#define HVMAXARCHITECTEDVIRTUALLANS 16 +struct VethPort +{ + struct net_device *mDev; + struct net_device_stats mStats; + int mLock; + u64 mMyAddress; + int mPromiscuous; + int mAllMcast; + rwlock_t mMcastGate; + int mNumAddrs; + u64 mMcasts[12]; +}; + +struct VethFabricMgr +{ + u64 mEyecatcher; + HvLpIndex mThisLp; + struct VethLpConnection mConnection[HVMAXARCHITECTEDLPS]; + spinlock_t mPortListGate; + u64 mNumPorts; + struct VethPort *mPorts[HVMAXARCHITECTEDVIRTUALLANS]; +}; + +int proc_veth_dump_connection(char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_veth_dump_port(char *page, char **start, off_t off, int count, int *eof, void *data); + +#endif /* _VETH_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/wan/Config.in linux.ac/drivers/net/wan/Config.in --- linux.vanilla/drivers/net/wan/Config.in Mon Apr 30 15:13:19 2001 +++ linux.ac/drivers/net/wan/Config.in Fri May 11 15:48:38 2001 @@ -49,7 +49,7 @@ dep_tristate ' Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m - dep_tristate ' SyncLink HDLC/SYNCPPP support' CONFIG_SYNCLINK_SYNCPPP m + tristate ' SyncLink HDLC/SYNCPPP support' CONFIG_SYNCLINK_SYNCPPP tristate ' Generic HDLC driver' CONFIG_HDLC if [ "$CONFIG_HDLC" != "n" ]; then diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/wan/lapbether.c linux.ac/drivers/net/wan/lapbether.c --- linux.vanilla/drivers/net/wan/lapbether.c Mon Apr 30 15:13:19 2001 +++ linux.ac/drivers/net/wan/lapbether.c Thu May 24 23:16:25 2001 @@ -112,8 +112,8 @@ dev_put(lapbeth->ethdev); kfree(lapbeth); } - - lapbeth_prev = lapbeth; + else + lapbeth_prev = lapbeth; } restore_flags(flags); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/wan/lmc/lmc_main.c linux.ac/drivers/net/wan/lmc/lmc_main.c --- linux.vanilla/drivers/net/wan/lmc/lmc_main.c Tue Apr 3 17:32:16 2001 +++ linux.ac/drivers/net/wan/lmc/lmc_main.c Thu May 24 23:37:16 2001 @@ -507,7 +507,12 @@ break; } - LMC_COPY_FROM_USER(data, xc.data, xc.len); + if(copy_from_user(data, xc.data, xc.len)) + { + kfree(data); + ret = -ENOMEM; + break; + } printk("%s: Starting load of data Len: %d at 0x%p == 0x%p\n", dev->name, xc.len, xc.data, data); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/wan/sdla_x25.c linux.ac/drivers/net/wan/sdla_x25.c --- linux.vanilla/drivers/net/wan/sdla_x25.c Mon Apr 30 15:13:20 2001 +++ linux.ac/drivers/net/wan/sdla_x25.c Tue May 22 12:25:48 2001 @@ -3108,7 +3108,7 @@ case 0x08: /* modem failure */ #ifndef MODEM_NOT_LOG printk(KERN_INFO "%s: modem failure!\n", card->devname); -#endif MODEM_NOT_LOG +#endif api_oob_event(card,mb); break; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/wan/syncppp.c linux.ac/drivers/net/wan/syncppp.c --- linux.vanilla/drivers/net/wan/syncppp.c Sat May 26 16:53:09 2001 +++ linux.ac/drivers/net/wan/syncppp.c Sat May 26 00:27:35 2001 @@ -518,8 +518,10 @@ } /* Send Configure-Ack packet. */ sp->pp_loopcnt = 0; - sppp_cp_send (sp, PPP_LCP, LCP_CONF_ACK, - h->ident, len-4, h+1); + if (sp->lcp.state != LCP_STATE_OPENED) { + sppp_cp_send (sp, PPP_LCP, LCP_CONF_ACK, + h->ident, len-4, h+1); + } /* Change the state. */ switch (sp->lcp.state) { case LCP_STATE_CLOSED: @@ -535,7 +537,9 @@ sp->ipcp.state = IPCP_STATE_CLOSED; /* Initiate renegotiation. */ sppp_lcp_open (sp); - /* An ACK has already been sent. */ + /* Send ACK after our REQ in attempt to break loop */ + sppp_cp_send (sp, PPP_LCP, LCP_CONF_ACK, + h->ident, len-4, h+1); sp->lcp.state = LCP_STATE_ACK_SENT; break; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/net/wavelan.c linux.ac/drivers/net/wavelan.c --- linux.vanilla/drivers/net/wavelan.c Mon Apr 30 15:13:20 2001 +++ linux.ac/drivers/net/wavelan.c Mon Apr 30 15:22:27 2001 @@ -1545,8 +1545,7 @@ /* Setting by channel (same as wfreqsel) */ /* Warning: each channel is 22 MHz wide, so some of the channels * will interfere. */ - if ((frequency->e == 0) && - (frequency->m >= 0) && (frequency->m < BAND_NUM)) { + if ((frequency->e == 0) && (frequency->m < BAND_NUM)) { /* Get frequency offset. */ freq = channel_bands[frequency->m] >> 1; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/parport/Config.in linux.ac/drivers/parport/Config.in --- linux.vanilla/drivers/parport/Config.in Sat Feb 3 20:43:52 2001 +++ linux.ac/drivers/parport/Config.in Tue Apr 3 17:54:57 2001 @@ -17,12 +17,8 @@ bool ' SuperIO chipset support (EXPERIMENTAL)' CONFIG_PARPORT_PC_SUPERIO fi fi - if [ "$CONFIG_PARPORT_PC" = "y" ]; then - # Don't bother with this if parport_pc is a module; it only affects - # the presence or not of some __init's, which are no-ops for modules. - if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then - bool ' Support for PCMCIA management for PC-style ports' CONFIG_PARPORT_PC_PCMCIA - fi + if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then + dep_tristate ' Support for PCMCIA management for PC-style ports' CONFIG_PARPORT_PC_PCMCIA $CONFIG_PCMCIA fi if [ "$CONFIG_ARM" = "y" ]; then dep_tristate ' Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT @@ -41,6 +37,12 @@ else define_tristate CONFIG_PARPORT_ATARI n fi + if [ "$CONFIG_GSC_LASI" = "y" ]; then + dep_tristate ' LASI/ASP builtin parallel-port' CONFIG_PARPORT_GSC $CONFIG_PARPORT + else + define_tristate CONFIG_PARPORT_GSC n + fi + if [ "$CONFIG_SBUS" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' Sparc hardware (EXPERIMENTAL)' CONFIG_PARPORT_SUNBPP $CONFIG_PARPORT else diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/parport/Makefile linux.ac/drivers/parport/Makefile --- linux.vanilla/drivers/parport/Makefile Fri Dec 29 22:07:22 2000 +++ linux.ac/drivers/parport/Makefile Tue Apr 3 17:54:57 2001 @@ -22,6 +22,7 @@ obj-$(CONFIG_PARPORT) += parport.o obj-$(CONFIG_PARPORT_PC) += parport_pc.o +obj-$(CONFIG_PARPORT_PC_PCMCIA)+= parport_cs.o obj-$(CONFIG_PARPORT_AMIGA) += parport_amiga.o obj-$(CONFIG_PARPORT_MFC3) += parport_mfc3.o obj-$(CONFIG_PARPORT_ATARI) += parport_atari.o diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/parport/parport_cs.c linux.ac/drivers/parport/parport_cs.c --- linux.vanilla/drivers/parport/parport_cs.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/parport/parport_cs.c Tue Apr 3 17:54:57 2001 @@ -0,0 +1,482 @@ +/*====================================================================== + + A driver for PCMCIA parallel port adapters + + (specifically, for the Quatech SPP-100 EPP card: other cards will + probably require driver tweaks) + + parport_cs.c 1.20 2000/11/02 23:15:05 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + . Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"parport_cs.c 1.20 2000/11/02 23:15:05 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +#ifndef VERSION +#define VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; +static int epp_mode = 1; + +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM(epp_mode, "i"); + +/*====================================================================*/ + +#define FORCE_EPP_MODE 0x08 + +typedef struct parport_info_t { + dev_link_t link; + int ndev; + dev_node_t node; + struct parport *port; +} parport_info_t; + +static dev_link_t *parport_attach(void); +static void parport_detach(dev_link_t *); +static void parport_config(dev_link_t *link); +static void parport_cs_release(u_long arg); +static int parport_event(event_t event, int priority, + event_callback_args_t *args); + +static dev_info_t dev_info = "parport_cs"; +static dev_link_t *dev_list = NULL; + +extern struct parport_operations parport_pc_ops; +static struct parport_operations parport_cs_ops; + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================== + + parport_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + +======================================================================*/ + +static dev_link_t *parport_attach(void) +{ + parport_info_t *info; + dev_link_t *link; + client_reg_t client_reg; + int i, ret; + + DEBUG(0, "parport_attach()\n"); + + /* Create new parport device */ + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) return NULL; + memset(info, 0, sizeof(*info)); + link = &info->link; link->priv = info; + + link->release.function = &parport_cs_release; + link->release.data = (u_long)link; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &parport_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + parport_detach(link); + return NULL; + } + + return link; +} /* parport_attach */ + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void parport_detach(dev_link_t *link) +{ + dev_link_t **linkp; + int ret; + + DEBUG(0, "parport_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + del_timer(&link->release); + if (link->state & DEV_CONFIG) + parport_cs_release((u_long)link); + + if (link->handle) { + ret = CardServices(DeregisterClient, link->handle); + if (ret != CS_SUCCESS) + cs_error(link->handle, DeregisterClient, ret); + } + + /* Unlink, free device structure */ + *linkp = link->next; + kfree(link->priv); + +} /* parport_detach */ + +/*====================================================================== + + parport_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + parport device available to the system. + +======================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +#define CFG_CHECK(fn, args...) \ +if (CardServices(fn, args) != 0) goto next_entry + +static struct { u_int flag; char *name; } mode[] = { + { PARPORT_MODE_TRISTATE, "PS2" }, + { PARPORT_MODE_EPP, "EPP" }, + { PARPORT_MODE_ECP, "ECP" }, +}; + +void parport_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + parport_info_t *info = link->priv; + tuple_t tuple; + u_short buf[128]; + cisparse_t parse; + config_info_t conf; + cistpl_cftable_entry_t *cfg = &parse.cftable_entry; + cistpl_cftable_entry_t dflt = { 0 }; + struct parport *p; + int i, last_ret, last_fn; + + DEBUG(0, "parport_config(0x%p)\n", link); + + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleOffset = 0; tuple.TupleDataMax = 255; + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + /* Not sure if this is right... look up the current Vcc */ + CS_CHECK(GetConfigurationInfo, handle, &conf); + + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + tuple.Attributes = 0; + CS_CHECK(GetFirstTuple, handle, &tuple); + while (1) { + CFG_CHECK(GetTupleData, handle, &tuple); + CFG_CHECK(ParseTuple, handle, &tuple, &parse); + + if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; + link->conf.ConfigIndex = cfg->index; + if (epp_mode) + link->conf.ConfigIndex |= FORCE_EPP_MODE; + link->io.BasePort1 = io->win[0].base; + link->io.NumPorts1 = io->win[0].len; + link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; + if (io->nwin == 2) { + link->io.BasePort2 = io->win[1].base; + link->io.NumPorts2 = io->win[1].len; + } + CFG_CHECK(RequestIO, link->handle, &link->io); + /* If we've got this far, we're done */ + break; + } + + next_entry: + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; + CS_CHECK(GetNextTuple, handle, &tuple); + } + + CS_CHECK(RequestIRQ, handle, &link->irq); + CS_CHECK(RequestConfiguration, handle, &link->conf); + + p = parport_pc_probe_port(link->io.BasePort1, link->io.BasePort2, + link->irq.AssignedIRQ, PARPORT_DMA_NONE, + NULL); + if (p == NULL) { + printk(KERN_NOTICE "parport_cs: parport_pc_probe_port() at " + "0x%3x, irq %u failed\n", link->io.BasePort1, + link->irq.AssignedIRQ); + goto failed; + } + +#if (LINUX_VERSION_CODE < VERSION(2,3,6)) +#if (LINUX_VERSION_CODE >= VERSION(2,2,8)) + p->private_data = kmalloc(sizeof(struct parport_pc_private), + GFP_KERNEL); + ((struct parport_pc_private *)(p->private_data))->ctr = 0x0c; +#endif + parport_proc_register(p); + p->flags |= PARPORT_FLAG_COMA; + parport_pc_write_econtrol(p, 0x00); + parport_pc_write_control(p, 0x0c); + parport_pc_write_data(p, 0x00); +#endif + + p->modes |= PARPORT_MODE_PCSPP; + if (epp_mode) + p->modes |= PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP; + info->ndev = 1; + info->node.major = LP_MAJOR; + info->node.minor = p->number; + info->port = p; + strcpy(info->node.dev_name, p->name); + link->dev = &info->node; + printk(KERN_INFO "%s: PC-style PCMCIA at %#x", p->name, + link->io.BasePort1); + if (link->io.NumPorts2) + printk(" & %#x", link->io.BasePort2); + printk(", irq %u [SPP", link->irq.AssignedIRQ); + for (i = 0; i < 5; i++) + if (p->modes & mode[i].flag) printk(",%s", mode[i].name); + printk("]\n"); + + link->state &= ~DEV_CONFIG_PENDING; + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + parport_cs_release((u_long)link); + +} /* parport_config */ + +/*====================================================================== + + After a card is removed, parport_cs_release() will unregister the + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. + +======================================================================*/ + +void parport_cs_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + parport_info_t *info = link->priv; + + DEBUG(0, "parport_release(0x%p)\n", link); + + if (info->ndev) { + struct parport *p = info->port; +#if (LINUX_VERSION_CODE < VERSION(2,3,6)) + if (!(p->flags & PARPORT_FLAG_COMA)) + parport_quiesce(p); +#endif + parport_proc_unregister(p); +#if (LINUX_VERSION_CODE >= VERSION(2,2,8)) + kfree(p->private_data); +#endif + parport_unregister_port(p); + } + info->ndev = 0; + link->dev = NULL; + + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; + +} /* parport_cs_release */ + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. + +======================================================================*/ + +int parport_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + + DEBUG(1, "parport_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) + mod_timer(&link->release, jiffies + HZ/20); + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + parport_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) + CardServices(ReleaseConfiguration, link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (DEV_OK(link)) + CardServices(RequestConfiguration, link->handle, &link->conf); + break; + } + return 0; +} /* parport_event */ + +/*====================================================================*/ + +#if (LINUX_VERSION_CODE < VERSION(2,3,6)) + +static void inc_use_count(void) +{ + MOD_INC_USE_COUNT; + parport_pc_ops.inc_use_count(); +} + +static void dec_use_count(void) +{ + MOD_DEC_USE_COUNT; + parport_pc_ops.dec_use_count(); +} + +#endif + +/*====================================================================*/ + +static int __init init_parport_cs(void) +{ + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "parport_cs: Card Services release " + "does not match!\n"); + return -1; + } + +#if (LINUX_VERSION_CODE < VERSION(2,3,6)) + /* This is to protect against unloading modules out of order */ + parport_cs_ops = parport_pc_ops; + parport_cs_ops.inc_use_count = &inc_use_count; + parport_cs_ops.dec_use_count = &dec_use_count; +#endif + + register_pccard_driver(&dev_info, &parport_attach, &parport_detach); + return 0; +} + +static void __exit exit_parport_cs(void) +{ + DEBUG(0, "parport_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + parport_detach(dev_list); +} + +module_init(init_parport_cs); +module_exit(exit_parport_cs); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/parport/parport_pc.c linux.ac/drivers/parport/parport_pc.c --- linux.vanilla/drivers/parport/parport_pc.c Sat May 26 16:53:10 2001 +++ linux.ac/drivers/parport/parport_pc.c Sat May 26 17:06:22 2001 @@ -62,6 +62,10 @@ #include #include +#if defined (CONFIG_PNPBIOS) || defined (CONFIG_PNPBIOS_MODULE) +#include +#endif + #define PARPORT_PC_MAX_PORTS PARPORT_MAX /* ECR modes */ @@ -2760,6 +2764,27 @@ return count; } +#if defined (CONFIG_PNPBIOS) || defined (CONFIG_PNPBIOS_MODULE) + +int init_pnp040x(struct pci_dev *dev) +{ int io,iohi,irq,dma; + + io=dev->resource[0].start; + iohi=dev->resource[1].start; + irq=dev->irq_resource[0].start; + dma=dev->dma_resource[0].start; + + if(dma==0) dma=-1; + + printk(KERN_INFO "PnPBIOS: Parport found %s %s at io=%04x,%04x irq=%d dma=%d\n", + dev->name,dev->slot_name,io,iohi,irq,dma); + if (parport_pc_probe_port(io,iohi,irq,dma,NULL)) + return 1; + return 0; +} + +#endif + /* This function is called by parport_pc_init if the user didn't * specify any ports to probe. Its job is to find some ports. Order * is important here -- we want ISA ports to be registered first, @@ -2773,10 +2798,20 @@ static int __init parport_pc_find_ports (int autoirq, int autodma) { int count = 0, r; + struct pci_dev *dev; #ifdef CONFIG_PARPORT_PC_SUPERIO detect_and_report_winbond (); detect_and_report_smsc (); +#endif + +#if defined(CONFIG_PNPBIOS) || defined(CONFIG_PNPBIOS_MODULE) + dev=NULL; + while ((dev=pnpbios_find_device("PNP0400",dev))) + count+=init_pnp040x(dev); + dev=NULL; + while ((dev=pnpbios_find_device("PNP0401",dev))) + count+=init_pnp040x(dev); #endif /* Onboard SuperIO chipsets that show themselves on the PCI bus. */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pci/classlist.h linux.ac/drivers/pci/classlist.h --- linux.vanilla/drivers/pci/classlist.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pci/classlist.h Sat May 26 00:54:19 2001 @@ -0,0 +1,88 @@ +CLASS(0000, "Non-VGA unclassified device") +CLASS(0001, "VGA compatible unclassified device") +CLASS(0100, "SCSI storage controller") +CLASS(0101, "IDE interface") +CLASS(0102, "Floppy disk controller") +CLASS(0103, "IPI bus controller") +CLASS(0104, "RAID bus controller") +CLASS(0180, "Unknown mass storage controller") +CLASS(0200, "Ethernet controller") +CLASS(0201, "Token ring network controller") +CLASS(0202, "FDDI network controller") +CLASS(0203, "ATM network controller") +CLASS(0204, "ISDN controller") +CLASS(0280, "Network controller") +CLASS(0300, "VGA compatible controller") +CLASS(0301, "XGA compatible controller") +CLASS(0302, "3D controller") +CLASS(0380, "Display controller") +CLASS(0400, "Multimedia video controller") +CLASS(0401, "Multimedia audio controller") +CLASS(0402, "Computer telephony device") +CLASS(0480, "Multimedia controller") +CLASS(0500, "RAM memory") +CLASS(0501, "FLASH memory") +CLASS(0580, "Memory controller") +CLASS(0600, "Host bridge") +CLASS(0601, "ISA bridge") +CLASS(0602, "EISA bridge") +CLASS(0603, "MicroChannel bridge") +CLASS(0604, "PCI bridge") +CLASS(0605, "PCMCIA bridge") +CLASS(0606, "NuBus bridge") +CLASS(0607, "CardBus bridge") +CLASS(0608, "RACEway bridge") +CLASS(0609, "Semi-transparent PCI-to-PCI bridge") +CLASS(060a, "InfiniBand to PCI host bridge") +CLASS(0680, "Bridge") +CLASS(0700, "Serial controller") +CLASS(0701, "Parallel controller") +CLASS(0702, "Multiport serial controller") +CLASS(0703, "Modem") +CLASS(0780, "Communication controller") +CLASS(0800, "PIC") +CLASS(0801, "DMA controller") +CLASS(0802, "Timer") +CLASS(0803, "RTC") +CLASS(0804, "PCI Hot-plug controller") +CLASS(0880, "System peripheral") +CLASS(0900, "Keyboard controller") +CLASS(0901, "Digitizer Pen") +CLASS(0902, "Mouse controller") +CLASS(0903, "Scanner controller") +CLASS(0904, "Gameport controller") +CLASS(0980, "Input device controller") +CLASS(0a00, "Generic Docking Station") +CLASS(0a80, "Docking Station") +CLASS(0b00, "386") +CLASS(0b01, "486") +CLASS(0b02, "Pentium") +CLASS(0b10, "Alpha") +CLASS(0b20, "Power PC") +CLASS(0b30, "MIPS") +CLASS(0b40, "Co-processor") +CLASS(0c00, "FireWire (IEEE 1394)") +CLASS(0c01, "ACCESS Bus") +CLASS(0c02, "SSA") +CLASS(0c03, "USB Controller") +CLASS(0c04, "Fiber Channel") +CLASS(0c05, "SMBus") +CLASS(0c06, "InfiniBand") +CLASS(0d00, "IRDA controller") +CLASS(0d01, "Consumer IR controller") +CLASS(0d10, "RF controller") +CLASS(0d80, "Wireless controller") +CLASS(0e00, "I2O") +CLASS(0f00, "Satellite TV controller") +CLASS(0f01, "Satellite audio communication controller") +CLASS(0f03, "Satellite voice communication controller") +CLASS(0f04, "Satellite data communication controller") +CLASS(1000, "Network and computing encryption device") +CLASS(1010, "Entertainment encryption device") +CLASS(1080, "Encryption controller") +CLASS(1100, "DPIO module") +CLASS(1101, "Performance counters") +CLASS(1110, "Communication synchronizer") +CLASS(1180, "Signal processing controller") + +#undef CLASS diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pci/devlist.h linux.ac/drivers/pci/devlist.h --- linux.vanilla/drivers/pci/devlist.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pci/devlist.h Sat May 26 00:54:19 2001 @@ -0,0 +1,6992 @@ +VENDOR(0000,"Gammagraphx, Inc.") +ENDVENDOR() + +VENDOR(001a,"Ascend Communications, Inc.") +ENDVENDOR() + +VENDOR(0033,"Paradyne corp.") +ENDVENDOR() + +VENDOR(003d,"Lockheed Martin-Marietta Corp") +ENDVENDOR() + +VENDOR(0070,"Hauppauge computer works Inc.") +ENDVENDOR() + +VENDOR(0100,"Ncipher Corp Ltd") +ENDVENDOR() + +VENDOR(0675,"Dynalink") + DEVICE(0675,1700,"IS64PH ISDN Adapter") + DEVICE(0675,1702,"IS64PH ISDN Adapter") +ENDVENDOR() + +VENDOR(0a89,"BREA Technologies Inc") +ENDVENDOR() + +VENDOR(0e11,"Compaq Computer Corporation") + DEVICE(0e11,0001,"PCI to EISA Bridge") + DEVICE(0e11,0002,"PCI to ISA Bridge") + DEVICE(0e11,0508,"Netelligent 4/16 Token Ring") + DEVICE(0e11,1000,"Triflex/Pentium Bridge, Model 1000") + DEVICE(0e11,2000,"Triflex/Pentium Bridge, Model 2000") + DEVICE(0e11,3032,"QVision 1280/p") + DEVICE(0e11,3033,"QVision 1280/p") + DEVICE(0e11,3034,"QVision 1280/p") + DEVICE(0e11,4000,"4000 [Triflex]") + DEVICE(0e11,6010,"HotPlug PCI Bridge 6010") + DEVICE(0e11,7020,"USB Controller") + DEVICE(0e11,a0ec,"Fibre Channel Host Controller") + DEVICE(0e11,a0f0,"Advanced System Management Controller") + DEVICE(0e11,a0f3,"Triflex PCI to ISA Bridge") + DEVICE(0e11,a0f7,"PCI Hotplug Controller") + DEVICE(0e11,a0f8,"USB Open Host Controller") + DEVICE(0e11,ae10,"Smart-2/P RAID Controller") + DEVICE(0e11,ae29,"MIS-L") + DEVICE(0e11,ae2a,"MPC") + DEVICE(0e11,ae2b,"MIS-E") + DEVICE(0e11,ae31,"System Management Controller") + DEVICE(0e11,ae32,"Netelligent 10/100") + DEVICE(0e11,ae33,"Triflex Dual EIDE Controller") + DEVICE(0e11,ae34,"Netelligent 10") + DEVICE(0e11,ae35,"Integrated NetFlex-3/P") + DEVICE(0e11,ae40,"Netelligent 10/100 Dual") + DEVICE(0e11,ae43,"ProLiant Integrated Netelligent 10/100") + DEVICE(0e11,ae69,"CETUS-L") + DEVICE(0e11,ae6c,"Northstar") + DEVICE(0e11,ae6d,"NorthStar CPU to PCI Bridge") + DEVICE(0e11,b011,"Integrated Netelligent 10/100") + DEVICE(0e11,b012,"Netelligent 10 T/2") + DEVICE(0e11,b030,"Netelligent WS 5100") + DEVICE(0e11,b04a,"10/100TX WOL UTP Controller") + DEVICE(0e11,b0c6,"10/100TX Embedded WOL UTP Controller") + DEVICE(0e11,b0d7,"NC3121 (Rev A & B)") + DEVICE(0e11,f130,"NetFlex-3/P ThunderLAN 1.0") + DEVICE(0e11,f150,"NetFlex-3/P ThunderLAN 2.3") +ENDVENDOR() + +VENDOR(1000,"Symbios Logic Inc. (formerly NCR)") + DEVICE(1000,0001,"53c810") + DEVICE(1000,0002,"53c820") + DEVICE(1000,0003,"53c825") + DEVICE(1000,0004,"53c815") + DEVICE(1000,0005,"53c810AP") + DEVICE(1000,0006,"53c860") + DEVICE(1000,000a,"53c1510") + DEVICE(1000,000b,"53c896") + DEVICE(1000,000c,"53c895") + DEVICE(1000,000d,"53c885") + DEVICE(1000,000f,"53c875") + DEVICE(1000,0012,"53c895a") + DEVICE(1000,0020,"53c1010 Ultra3 SCSI Adapter") + DEVICE(1000,0021,"53c1010 66MHz Ultra3 SCSI Adapter") + DEVICE(1000,008f,"53c875J") + DEVICE(1000,0701,"83C885") + DEVICE(1000,0702,"Yellowfin G-NIC gigabit ethernet") + DEVICE(1000,0901,"61C102") + DEVICE(1000,1000,"63C815") +ENDVENDOR() + +VENDOR(1001,"Initio") + DEVICE(1001,0010,"PCI 1616 Measurement card with 32 digital I/O lines") + DEVICE(1001,0011,"OPTO-PCI Opto-Isolated digital I/O board") + DEVICE(1001,0012,"PCI-AD/DA Analogue I/O board") + DEVICE(1001,0013,"PCI-OPTO-RELAIS Digital I/O board with relay outputs") + DEVICE(1001,0014,"PCI-Counter/Timer Counter Timer board") + DEVICE(1001,0015,"PCI-DAC416 Analogue output board") + DEVICE(1001,0016,"PCI-MFB Analogue I/O board") + DEVICE(1001,0017,"PROTO-3 PCI Prototyping board") + DEVICE(1001,9100,"INI-9100/9100W SCSI Host") +ENDVENDOR() + +VENDOR(1002,"ATI Technologies Inc") + DEVICE(1002,4158,"68800AX [Mach32]") + DEVICE(1002,4354,"215CT [Mach64 CT]") + DEVICE(1002,4358,"210888CX [Mach64 CX]") + DEVICE(1002,4554,"210888ET [Mach64 ET]") + DEVICE(1002,4654,"Mach64 VT") + DEVICE(1002,4742,"3D Rage Pro AGP 1X/2X") + DEVICE(1002,4744,"3D Rage Pro AGP 1X") + DEVICE(1002,4747,"3D Rage Pro") + DEVICE(1002,4749,"3D Rage Pro") + DEVICE(1002,474c,"Rage XC") + DEVICE(1002,474d,"Rage XL AGP") + DEVICE(1002,474e,"Rage XC AGP") + DEVICE(1002,474f,"Rage XL") + DEVICE(1002,4750,"3D Rage Pro 215GP") + DEVICE(1002,4751,"3D Rage Pro 215GQ") + DEVICE(1002,4752,"Rage XL") + DEVICE(1002,4753,"Rage XC") + DEVICE(1002,4754,"3D Rage I/II 215GT [Mach64 GT]") + DEVICE(1002,4755,"3D Rage II+ 215GTB [Mach64 GTB]") + DEVICE(1002,4756,"3D Rage IIC 215IIC [Mach64 GT IIC]") + DEVICE(1002,4757,"3D Rage IIC AGP") + DEVICE(1002,4758,"210888GX [Mach64 GX]") + DEVICE(1002,4759,"3D Rage IIC") + DEVICE(1002,475a,"3D Rage IIC AGP") + DEVICE(1002,4c42,"3D Rage LT Pro AGP-133") + DEVICE(1002,4c44,"3D Rage LT Pro AGP-66") + DEVICE(1002,4c46,"Mobility M3 AGP 2x") + DEVICE(1002,4c47,"3D Rage LT-G 215LG") + DEVICE(1002,4c49,"3D Rage LT Pro") + DEVICE(1002,4c4d,"3D Rage P/M Mobility AGP 2x") + DEVICE(1002,4c4e,"3D Rage L Mobility AGP 2x") + DEVICE(1002,4c50,"3D Rage LT Pro") + DEVICE(1002,4c51,"3D Rage LT Pro") + DEVICE(1002,4c52,"3D Rage P/M Mobility") + DEVICE(1002,4c53,"3D Rage L Mobility") + DEVICE(1002,4c54,"264LT [Mach64 LT]") + DEVICE(1002,5041,"Rage 128 PA") + DEVICE(1002,5042,"Rage 128 PB") + DEVICE(1002,5043,"Rage 128 PC") + DEVICE(1002,5044,"Rage 128 PD") + DEVICE(1002,5045,"Rage 128 PE") + DEVICE(1002,5046,"Rage 128 PF") + DEVICE(1002,5047,"Rage 128 PG") + DEVICE(1002,5048,"Rage 128 PH") + DEVICE(1002,5049,"Rage 128 PI") + DEVICE(1002,504a,"Rage 128 PJ") + DEVICE(1002,504b,"Rage 128 PK") + DEVICE(1002,504c,"Rage 128 PL") + DEVICE(1002,504d,"Rage 128 PM") + DEVICE(1002,504e,"Rage 128 PN") + DEVICE(1002,504f,"Rage 128 PO") + DEVICE(1002,5050,"Rage 128 PP") + DEVICE(1002,5051,"Rage 128 PQ") + DEVICE(1002,5052,"Rage 128 PR") + DEVICE(1002,5053,"Rage 128 PS") + DEVICE(1002,5054,"Rage 128 PT") + DEVICE(1002,5055,"Rage 128 PU") + DEVICE(1002,5056,"Rage 128 PV") + DEVICE(1002,5057,"Rage 128 PW") + DEVICE(1002,5058,"Rage 128 PX") + DEVICE(1002,5245,"Rage 128 RE") + DEVICE(1002,5246,"Rage 128 RF") + DEVICE(1002,524b,"Rage 128 RK") + DEVICE(1002,524c,"Rage 128 RL") + DEVICE(1002,5345,"Rage 128 SE") + DEVICE(1002,5346,"Rage 128 SF") + DEVICE(1002,5347,"Rage 128 SG") + DEVICE(1002,5348,"Rage 128 4x") + DEVICE(1002,534b,"Rage 128 SK") + DEVICE(1002,534c,"Rage 128 SL") + DEVICE(1002,534d,"Rage 128 SM") + DEVICE(1002,534e,"Rage 128 4x") + DEVICE(1002,5354,"Mach 64 VT") + DEVICE(1002,5654,"264VT [Mach64 VT]") + DEVICE(1002,5655,"264VT3 [Mach64 VT3]") + DEVICE(1002,5656,"264VT4 [Mach64 VT4]") +ENDVENDOR() + +VENDOR(1003,"ULSI Systems") + DEVICE(1003,0201,"US201") +ENDVENDOR() + +VENDOR(1004,"VLSI Technology Inc") + DEVICE(1004,0005,"82C592-FC1") + DEVICE(1004,0006,"82C593-FC1") + DEVICE(1004,0007,"82C594-AFC2") + DEVICE(1004,0008,"82C596/7 [Wildcat]") + DEVICE(1004,0009,"82C597-AFC2") + DEVICE(1004,000c,"82C541 [Lynx]") + DEVICE(1004,000d,"82C543 [Lynx]") + DEVICE(1004,0101,"82C532") + DEVICE(1004,0102,"82C534") + DEVICE(1004,0103,"82C538") + DEVICE(1004,0104,"82C535") + DEVICE(1004,0105,"82C147") + DEVICE(1004,0200,"82C975") + DEVICE(1004,0280,"82C925") + DEVICE(1004,0304,"QSound ThunderBird PCI Audio") + DEVICE(1004,0305,"QSound ThunderBird PCI Audio Gameport") + DEVICE(1004,0306,"QSound ThunderBird PCI Audio Support Registers") + DEVICE(1004,0702,"VAS96011 [Golden Gate II]") +ENDVENDOR() + +VENDOR(1005,"Avance Logic Inc. [ALI]") + DEVICE(1005,2064,"ALG2032/2064") + DEVICE(1005,2128,"ALG2364A") + DEVICE(1005,2301,"ALG2301") + DEVICE(1005,2302,"ALG2302") + DEVICE(1005,2364,"ALG2364") + DEVICE(1005,2464,"ALG2364A") + DEVICE(1005,2501,"ALG2564A/25128A") +ENDVENDOR() + +VENDOR(1006,"Reply Group") +ENDVENDOR() + +VENDOR(1007,"NetFrame Systems Inc") +ENDVENDOR() + +VENDOR(1008,"Epson") +ENDVENDOR() + +VENDOR(100a,"Phoenix Technologies") +ENDVENDOR() + +VENDOR(100b,"National Semiconductor Corporation") + DEVICE(100b,0001,"DP83810") + DEVICE(100b,0002,"87415/87560 IDE") + DEVICE(100b,000e,"87560 Legacy I/O") + DEVICE(100b,000f,"OHCI Compliant FireWire Controller") + DEVICE(100b,0011,"National PCI System I/O") + DEVICE(100b,0012,"USB Controller") + DEVICE(100b,d001,"87410 IDE") +ENDVENDOR() + +VENDOR(100c,"Tseng Labs Inc") + DEVICE(100c,3202,"ET4000/W32p rev A") + DEVICE(100c,3205,"ET4000/W32p rev B") + DEVICE(100c,3206,"ET4000/W32p rev C") + DEVICE(100c,3207,"ET4000/W32p rev D") + DEVICE(100c,3208,"ET6000") + DEVICE(100c,4702,"ET6300") +ENDVENDOR() + +VENDOR(100d,"AST Research Inc") +ENDVENDOR() + +VENDOR(100e,"Weitek") + DEVICE(100e,9000,"P9000") + DEVICE(100e,9001,"P9000") + DEVICE(100e,9100,"P9100") +ENDVENDOR() + +VENDOR(1010,"Video Logic, Ltd.") +ENDVENDOR() + +VENDOR(1011,"Digital Equipment Corporation") + DEVICE(1011,0001,"DECchip 21050") + DEVICE(1011,0002,"DECchip 21040 [Tulip]") + DEVICE(1011,0004,"DECchip 21030 [TGA]") + DEVICE(1011,0007,"NVRAM [Zephyr NVRAM]") + DEVICE(1011,0008,"KZPSA [KZPSA]") + DEVICE(1011,0009,"DECchip 21140 [FasterNet]") + DEVICE(1011,000a,"21230 Video Codec") + DEVICE(1011,000d,"PBXGB [TGA2]") + DEVICE(1011,000f,"DEFPA") + DEVICE(1011,0014,"DECchip 21041 [Tulip Pass 3]") + DEVICE(1011,0016,"DGLPB [OPPO]") + DEVICE(1011,0019,"DECchip 21142/43") + DEVICE(1011,0021,"DECchip 21052") + DEVICE(1011,0022,"DECchip 21150") + DEVICE(1011,0023,"DECchip 21150") + DEVICE(1011,0024,"DECchip 21152") + DEVICE(1011,0025,"DECchip 21153") + DEVICE(1011,0026,"DECchip 21154") + DEVICE(1011,0045,"DECchip 21553") + DEVICE(1011,0046,"DECchip 21554") + DEVICE(1011,1065,"StrongARM DC21285") +ENDVENDOR() + +VENDOR(1012,"Micronics Computers Inc") +ENDVENDOR() + +VENDOR(1013,"Cirrus Logic") + DEVICE(1013,0038,"GD 7548") + DEVICE(1013,0040,"GD 7555 Flat Panel GUI Accelerator") + DEVICE(1013,004c,"GD 7556 Video/Graphics LCD/CRT Ctrlr") + DEVICE(1013,00a0,"GD 5430/40 [Alpine]") + DEVICE(1013,00a2,"GD 5432 [Alpine]") + DEVICE(1013,00a4,"GD 5434-4 [Alpine]") + DEVICE(1013,00a8,"GD 5434-8 [Alpine]") + DEVICE(1013,00ac,"GD 5436 [Alpine]") + DEVICE(1013,00b0,"GD 5440") + DEVICE(1013,00b8,"GD 5446") + DEVICE(1013,00bc,"GD 5480") + DEVICE(1013,00d0,"GD 5462") + DEVICE(1013,00d2,"GD 5462 [Laguna I]") + DEVICE(1013,00d4,"GD 5464 [Laguna]") + DEVICE(1013,00d6,"GD 5465 [Laguna]") + DEVICE(1013,00e8,"GD 5436U") + DEVICE(1013,1100,"CL 6729") + DEVICE(1013,1110,"PD 6832") + DEVICE(1013,1112,"PD 6834 PCMCIA/CardBus Ctrlr") + DEVICE(1013,1113,"PD 6833 PCMCIA/CardBus Ctrlr") + DEVICE(1013,1200,"GD 7542 [Nordic]") + DEVICE(1013,1202,"GD 7543 [Viking]") + DEVICE(1013,1204,"GD 7541 [Nordic Light]") + DEVICE(1013,4400,"CD 4400") + DEVICE(1013,6001,"CS 4610/11 [CrystalClear SoundFusion Audio Accelerator]") + DEVICE(1013,6003,"CS 4614/22/24 [CrystalClear SoundFusion Audio Accelerator]") + DEVICE(1013,6005,"Crystal CS4281 PCI Audio") +ENDVENDOR() + +VENDOR(1014,"IBM") + DEVICE(1014,0002,"PCI to MCA Bridge") + DEVICE(1014,0005,"Alta Lite") + DEVICE(1014,0007,"Alta MP") + DEVICE(1014,000a,"Fire Coral") + DEVICE(1014,0017,"CPU to PCI Bridge") + DEVICE(1014,0018,"TR Auto LANstreamer") + DEVICE(1014,001b,"GXT-150P") + DEVICE(1014,001d,"82G2675") + DEVICE(1014,0020,"MCA") + DEVICE(1014,0022,"IBM27-82351") + DEVICE(1014,002d,"Python") + DEVICE(1014,002e,"ServeRAID controller") + DEVICE(1014,0036,"Miami") + DEVICE(1014,003a,"CPU to PCI Bridge") + DEVICE(1014,003e,"16/4 Token ring UTP/STP controller") + DEVICE(1014,0045,"SSA Adapter") + DEVICE(1014,0046,"MPIC interrupt controller") + DEVICE(1014,0047,"PCI to PCI Bridge") + DEVICE(1014,0048,"PCI to PCI Bridge") + DEVICE(1014,0049,"Warhead SCSI Controller") + DEVICE(1014,004e,"ATM Controller (14104e00)") + DEVICE(1014,004f,"ATM Controller (14104f00)") + DEVICE(1014,0050,"ATM Controller (14105000)") + DEVICE(1014,0053,"25 MBit ATM Controller") + DEVICE(1014,0057,"MPEG PCI Bridge") + DEVICE(1014,005c,"i82557B 10/100") + DEVICE(1014,007c,"ATM Controller (14107c00)") + DEVICE(1014,007d,"3780IDSP [MWave]") + DEVICE(1014,0090,"GXT 3000P") + DEVICE(1014,0095,"20H2999 PCI Docking Bridge") + DEVICE(1014,0096,"Chukar chipset SCSI controller") + DEVICE(1014,00a5,"ATM Controller (1410a500)") + DEVICE(1014,00a6,"ATM 155MBPS MM Controller (1410a600)") + DEVICE(1014,00b7,"256-bit Graphics Rasterizer [Fire GL1]") + DEVICE(1014,00be,"ATM 622MBPS Controller (1410be00)") + DEVICE(1014,0142,"Yotta Video Compositor Input") + DEVICE(1014,0144,"Yotta Video Compositor Output") + DEVICE(1014,0156,"405GP PLB to PCI Bridge") + DEVICE(1014,ffff,"MPIC-2 interrupt controller") +ENDVENDOR() + +VENDOR(1015,"LSI Logic Corp of Canada") +ENDVENDOR() + +VENDOR(1016,"ICL Personal Systems") +ENDVENDOR() + +VENDOR(1017,"SPEA Software AG") + DEVICE(1017,5343,"SPEA 3D Accelerator") +ENDVENDOR() + +VENDOR(1018,"Unisys Systems") +ENDVENDOR() + +VENDOR(1019,"Elitegroup Computer Systems") +ENDVENDOR() + +VENDOR(101a,"AT&T GIS (NCR)") + DEVICE(101a,0005,"100VG ethernet") +ENDVENDOR() + +VENDOR(101b,"Vitesse Semiconductor") +ENDVENDOR() + +VENDOR(101c,"Western Digital") + DEVICE(101c,0193,"33C193A") + DEVICE(101c,0196,"33C196A") + DEVICE(101c,0197,"33C197A") + DEVICE(101c,0296,"33C296A") + DEVICE(101c,3193,"7193") + DEVICE(101c,3197,"7197") + DEVICE(101c,3296,"33C296A") + DEVICE(101c,4296,"34C296") + DEVICE(101c,9710,"Pipeline 9710") + DEVICE(101c,9712,"Pipeline 9712") + DEVICE(101c,c24a,"90C") +ENDVENDOR() + +VENDOR(101e,"American Megatrends Inc.") + DEVICE(101e,9010,"MegaRAID") + DEVICE(101e,9030,"EIDE Controller") + DEVICE(101e,9031,"EIDE Controller") + DEVICE(101e,9032,"EIDE & SCSI Controller") + DEVICE(101e,9033,"SCSI Controller") + DEVICE(101e,9040,"Multimedia card") + DEVICE(101e,9060,"MegaRAID") +ENDVENDOR() + +VENDOR(101f,"PictureTel") +ENDVENDOR() + +VENDOR(1020,"Hitachi Computer Products") +ENDVENDOR() + +VENDOR(1021,"OKI Electric Industry Co. Ltd.") +ENDVENDOR() + +VENDOR(1022,"Advanced Micro Devices [AMD]") + DEVICE(1022,2000,"79c970 [PCnet LANCE]") + DEVICE(1022,2001,"79c978 [HomePNA]") + DEVICE(1022,2020,"53c974 [PCscsi]") + DEVICE(1022,2040,"79c974") + DEVICE(1022,7006,"AMD-751 [Irongate] System Controller") + DEVICE(1022,7007,"AMD-751 [Irongate] AGP Bridge") + DEVICE(1022,7400,"AMD-755 [Cobra] ISA") + DEVICE(1022,7401,"AMD-755 [Cobra] IDE") + DEVICE(1022,7403,"AMD-755 [Cobra] ACPI") + DEVICE(1022,7404,"AMD-755 [Cobra] USB") + DEVICE(1022,7408,"AMD-756 [Viper] ISA") + DEVICE(1022,7409,"AMD-756 [Viper] IDE") + DEVICE(1022,740b,"AMD-756 [Viper] ACPI") + DEVICE(1022,740c,"AMD-756 [Viper] USB") +ENDVENDOR() + +VENDOR(1023,"Trident Microsystems") + DEVICE(1023,0194,"82C194") + DEVICE(1023,2000,"4DWave DX") + DEVICE(1023,2001,"4DWave NX") + DEVICE(1023,8400,"CyberBlade/i7") + DEVICE(1023,8420,"CyberBlade/i7d") + DEVICE(1023,8500,"CyberBlade/i1") + DEVICE(1023,8520,"CyberBlade i1") + DEVICE(1023,9320,"TGUI 9320") + DEVICE(1023,9350,"GUI Accelerator") + DEVICE(1023,9360,"Flat panel GUI Accelerator") + DEVICE(1023,9382,"Cyber 9382 [Reference design]") + DEVICE(1023,9383,"Cyber 9383 [Reference design]") + DEVICE(1023,9385,"Cyber 9385 [Reference design]") + DEVICE(1023,9386,"Cyber 9386") + DEVICE(1023,9388,"Cyber 9388") + DEVICE(1023,9397,"Cyber 9397") + DEVICE(1023,939a,"Cyber 9397DVD") + DEVICE(1023,9420,"TGUI 9420") + DEVICE(1023,9430,"TGUI 9430") + DEVICE(1023,9440,"TGUI 9440") + DEVICE(1023,9460,"TGUI 9460") + DEVICE(1023,9470,"TGUI 9470") + DEVICE(1023,9520,"Cyber 9520") + DEVICE(1023,9525,"Cyber 9525") + DEVICE(1023,9540,"Cyber 9540") + DEVICE(1023,9660,"TGUI 9660/968x/968x") + DEVICE(1023,9680,"TGUI 9680") + DEVICE(1023,9682,"TGUI 9682") + DEVICE(1023,9683,"TGUI 9683") + DEVICE(1023,9685,"ProVIDIA 9685") + DEVICE(1023,9750,"3DIm`age 975") + DEVICE(1023,9753,"TGUI 9753") + DEVICE(1023,9754,"TGUI 9754") + DEVICE(1023,9759,"TGUI 975") + DEVICE(1023,9783,"TGUI 9783") + DEVICE(1023,9785,"TGUI 9785") + DEVICE(1023,9850,"3DImage 9850") + DEVICE(1023,9880,"Blade 3D PCI/AGP") +ENDVENDOR() + +VENDOR(1024,"Zenith Data Systems") +ENDVENDOR() + +VENDOR(1025,"Acer Incorporated [ALI]") + DEVICE(1025,1435,"M1435") + DEVICE(1025,1445,"M1445") + DEVICE(1025,1449,"M1449") + DEVICE(1025,1451,"M1451") + DEVICE(1025,1461,"M1461") + DEVICE(1025,1489,"M1489") + DEVICE(1025,1511,"M1511") + DEVICE(1025,1512,"ALI M1512 Aladdin") + DEVICE(1025,1513,"M1513") + DEVICE(1025,1521,"ALI M1521 Aladdin III CPU Bridge") + DEVICE(1025,1523,"ALI M1523 ISA Bridge") + DEVICE(1025,1531,"M1531 Northbridge [Aladdin IV/IV+]") + DEVICE(1025,1533,"M1533 PCI-to-ISA Bridge") + DEVICE(1025,1535,"M1535 PCI Bridge + Super I/O + FIR") + DEVICE(1025,1541,"M1541 Northbridge [Aladdin V]") + DEVICE(1025,1542,"M1542 Northbridge [Aladdin V]") + DEVICE(1025,1543,"M1543 PCI-to-ISA Bridge + Super I/O + FIR") + DEVICE(1025,1561,"M1561 Northbridge [Aladdin 7]") + DEVICE(1025,1621,"M1621 Northbridge [Aladdin-Pro II]") + DEVICE(1025,1631,"M1631 Northbridge+3D Graphics [Aladdin TNT2]") + DEVICE(1025,1641,"M1641 Northbridge [Aladdin-Pro IV]") + DEVICE(1025,3141,"M3141") + DEVICE(1025,3143,"M3143") + DEVICE(1025,3145,"M3145") + DEVICE(1025,3147,"M3147") + DEVICE(1025,3149,"M3149") + DEVICE(1025,3151,"M3151") + DEVICE(1025,3307,"M3307 MPEG-I Video Controller") + DEVICE(1025,3309,"M3309 MPEG-II Video w/ Software Audio Decoder") + DEVICE(1025,3321,"M3321 MPEG-II Audio/Video Decoder") + DEVICE(1025,5212,"ALI M4803") + DEVICE(1025,5215,"ALI PCI EIDE Controller") + DEVICE(1025,5217,"M5217H") + DEVICE(1025,5219,"M5219") + DEVICE(1025,5225,"M5225") + DEVICE(1025,5229,"M5229") + DEVICE(1025,5235,"M5235") + DEVICE(1025,5237,"ALI M5237 PCI USB Host Controller") + DEVICE(1025,5240,"EIDE Controller") + DEVICE(1025,5241,"PCMCIA Bridge") + DEVICE(1025,5242,"General Purpose Controller") + DEVICE(1025,5243,"PCI to PCI Bridge Controller") + DEVICE(1025,5244,"Floppy Disk Controller") + DEVICE(1025,5247,"ALI M1541 PCI to PCI Bridge") + DEVICE(1025,5251,"M5251 P1394 OHCI Controller") + DEVICE(1025,5427,"ALI PCI to AGP Bridge") + DEVICE(1025,5451,"ALI M5451 PCI AC-Link Controller Audio Device") + DEVICE(1025,5453,"ALI M5453 PCI AC-Link Controller Modem Device") + DEVICE(1025,7101,"ALI M7101 PCI PMU Power Management Controller") +ENDVENDOR() + +VENDOR(1028,"Dell Computer Corporation") + DEVICE(1028,0001,"PowerEdge Expandable RAID Controller 2/Si") + DEVICE(1028,0002,"PowerEdge Expandable RAID Controller 3/Di") + DEVICE(1028,0003,"PowerEdge Expandable RAID Controller 3/Si") + DEVICE(1028,0004,"PowerEdge Expandable RAID Controller 3/Si") + DEVICE(1028,0005,"PowerEdge Expandable RAID Controller 3/Di") + DEVICE(1028,0006,"PowerEdge Expandable RAID Controller 3/Di") + DEVICE(1028,0008,"PowerEdge Expandable RAID Controller 3/Di") +ENDVENDOR() + +VENDOR(1029,"Siemens Nixdorf IS") +ENDVENDOR() + +VENDOR(102a,"LSI Logic") + DEVICE(102a,0000,"HYDRA") + DEVICE(102a,0010,"ASPEN") +ENDVENDOR() + +VENDOR(102b,"Matrox Graphics, Inc.") + DEVICE(102b,0010,"MGA-I [Impression?]") + DEVICE(102b,0518,"MGA-II [Athena]") + DEVICE(102b,0519,"MGA 2064W [Millennium]") + DEVICE(102b,051a,"MGA 1064SG [Mystique]") + DEVICE(102b,051b,"MGA 2164W [Millennium II]") + DEVICE(102b,051e,"MGA 1064SG [Mystique] AGP") + DEVICE(102b,051f,"MGA 2164W [Millennium II] AGP") + DEVICE(102b,0520,"MGA G200") + DEVICE(102b,0521,"MGA G200 AGP") + DEVICE(102b,0525,"MGA G400 AGP") + DEVICE(102b,0d10,"MGA Ultima/Impression") + DEVICE(102b,1000,"MGA G100 [Productiva]") + DEVICE(102b,1001,"MGA G100 [Productiva] AGP") + DEVICE(102b,2007,"MGA Mistral") + DEVICE(102b,4536,"VIA Framegrabber") + DEVICE(102b,6573,"Shark 10/100 Multiport SwitchNIC") +ENDVENDOR() + +VENDOR(102c,"Chips and Technologies") + DEVICE(102c,00b8,"F64310") + DEVICE(102c,00c0,"F69000 HiQVideo") + DEVICE(102c,00d0,"F65545") + DEVICE(102c,00d8,"F65545") + DEVICE(102c,00dc,"F65548") + DEVICE(102c,00e0,"F65550") + DEVICE(102c,00e4,"F65554") + DEVICE(102c,00e5,"F65555 HiQVPro") + DEVICE(102c,00f0,"F68554") + DEVICE(102c,00f4,"F68554 HiQVision") + DEVICE(102c,00f5,"F68555") +ENDVENDOR() + +VENDOR(102d,"Wyse Technology Inc.") + DEVICE(102d,50dc,"3328 Audio") +ENDVENDOR() + +VENDOR(102e,"Olivetti Advanced Technology") +ENDVENDOR() + +VENDOR(102f,"Toshiba America") + DEVICE(102f,0009,"r4x00") + DEVICE(102f,0020,"ATM Meteor 155") +ENDVENDOR() + +VENDOR(1030,"TMC Research") +ENDVENDOR() + +VENDOR(1031,"Miro Computer Products AG") + DEVICE(1031,5601,"DC20 ASIC") + DEVICE(1031,5607,"Video I/O & motion JPEG compressor") + DEVICE(1031,5631,"Media 3D") + DEVICE(1031,6057,"MiroVideo DC10/DC30+") +ENDVENDOR() + +VENDOR(1032,"Compaq") +ENDVENDOR() + +VENDOR(1033,"NEC Corporation") + DEVICE(1033,0001,"PCI to 486-like bus Bridge") + DEVICE(1033,0002,"PCI to VL98 Bridge") + DEVICE(1033,0003,"ATM Controller") + DEVICE(1033,0004,"R4000 PCI Bridge") + DEVICE(1033,0005,"PCI to 486-like bus Bridge") + DEVICE(1033,0006,"GUI Accelerator") + DEVICE(1033,0007,"PCI to UX-Bus Bridge") + DEVICE(1033,0008,"GUI Accelerator") + DEVICE(1033,0009,"GUI Accelerator for W98") + DEVICE(1033,001a,"[Nile II]") + DEVICE(1033,0021,"Vrc4373 [Nile I]") + DEVICE(1033,0029,"PowerVR PCX1") + DEVICE(1033,002a,"PowerVR 3D") + DEVICE(1033,0035,"USB") + DEVICE(1033,003e,"NAPCCARD Cardbus Controller") + DEVICE(1033,0046,"PowerVR PCX2 [midas]") + DEVICE(1033,005a,"Vrc5074 [Nile 4]") + DEVICE(1033,0063,"Firewarden") + DEVICE(1033,0067,"PowerVR Neon 250 Chipset") + DEVICE(1033,0074,"56k Voice Modem") + DEVICE(1033,009b,"Vrc5476") +ENDVENDOR() + +VENDOR(1034,"Framatome Connectors USA Inc.") +ENDVENDOR() + +VENDOR(1035,"Comp. & Comm. Research Lab") +ENDVENDOR() + +VENDOR(1036,"Future Domain Corp.") + DEVICE(1036,0000,"TMC-18C30 [36C70]") +ENDVENDOR() + +VENDOR(1037,"Hitachi Micro Systems") +ENDVENDOR() + +VENDOR(1038,"AMP, Inc") +ENDVENDOR() + +VENDOR(1039,"Silicon Integrated Systems [SiS]") + DEVICE(1039,0001,"5591/5592 AGP") + DEVICE(1039,0002,"SG86C202") + DEVICE(1039,0006,"85C501/2/3") + DEVICE(1039,0008,"85C503/5513") + DEVICE(1039,0009,"ACPI") + DEVICE(1039,0018,"SiS85C503/5513 (LPC Bridge)") + DEVICE(1039,0200,"5597/5598 VGA") + DEVICE(1039,0204,"82C204") + DEVICE(1039,0205,"SG86C205") + DEVICE(1039,0406,"85C501/2") + DEVICE(1039,0496,"85C496") + DEVICE(1039,0530,"530 Host") + DEVICE(1039,0540,"540 Host") + DEVICE(1039,0597,"5513C") + DEVICE(1039,0601,"85C601") + DEVICE(1039,0620,"620 Host") + DEVICE(1039,0630,"630 Host") + DEVICE(1039,0730,"730 Host") + DEVICE(1039,0900,"SiS900 10/100 Ethernet") + DEVICE(1039,3602,"83C602") + DEVICE(1039,5107,"5107") + DEVICE(1039,5300,"SiS540 PCI Display Adapter") + DEVICE(1039,5401,"486 PCI Chipset") + DEVICE(1039,5511,"5511/5512") + DEVICE(1039,5513,"5513 [IDE]") + DEVICE(1039,5517,"5517") + DEVICE(1039,5571,"5571") + DEVICE(1039,5581,"5581 Pentium Chipset") + DEVICE(1039,5582,"5582") + DEVICE(1039,5591,"5591/5592 Host") + DEVICE(1039,5596,"5596 Pentium Chipset") + DEVICE(1039,5597,"5597 [SiS5582]") + DEVICE(1039,5600,"5600 Host") + DEVICE(1039,6204,"Video decoder & MPEG interface") + DEVICE(1039,6205,"VGA Controller") + DEVICE(1039,6236,"6236 3D-AGP") + DEVICE(1039,6300,"SiS630 GUI Accelerator+3D") + DEVICE(1039,6306,"6306 3D-AGP") + DEVICE(1039,6326,"86C326") + DEVICE(1039,7001,"7001") + DEVICE(1039,7007,"OHCI Compliant FireWire Controller") + DEVICE(1039,7016,"SiS7016 10/100 Ethernet Adapter") + DEVICE(1039,7018,"SiS PCI Audio Accelerator") +ENDVENDOR() + +VENDOR(103a,"Seiko Epson Corporation") +ENDVENDOR() + +VENDOR(103b,"Tatung Co. of America") +ENDVENDOR() + +VENDOR(103c,"Hewlett-Packard Company") + DEVICE(103c,1030,"J2585A") + DEVICE(103c,1031,"J2585B") + DEVICE(103c,1040,"J2973A DeskDirect 10BaseT NIC") + DEVICE(103c,1041,"J2585B DeskDirect 10/100 NIC") + DEVICE(103c,1042,"J2970A DeskDirect 10BaseT/2 NIC") + DEVICE(103c,1064,"79C970 PCnet Ethernet Controller") + DEVICE(103c,10c1,"NetServer Smart IRQ Router") + DEVICE(103c,10ed,"TopTools Remote Control") + DEVICE(103c,1200,"82557B 10/100 NIC") + DEVICE(103c,1219,"NetServer PCI Hot-Plug Controller") + DEVICE(103c,121a,"NetServer SMIC Controller") + DEVICE(103c,121b,"NetServer Legacy COM Port Decoder") + DEVICE(103c,121c,"NetServer PCI COM Port Decoder") + DEVICE(103c,2910,"E2910A") + DEVICE(103c,2925,"E2925A") +ENDVENDOR() + +VENDOR(103e,"Solliday Engineering") +ENDVENDOR() + +VENDOR(103f,"Synopsys/Logic Modeling Group") +ENDVENDOR() + +VENDOR(1040,"Accelgraphics Inc.") +ENDVENDOR() + +VENDOR(1041,"Computrend") +ENDVENDOR() + +VENDOR(1042,"Micron") + DEVICE(1042,1000,"FDC 37C665") + DEVICE(1042,1001,"37C922") + DEVICE(1042,3000,"Samurai_0") + DEVICE(1042,3010,"Samurai_1") + DEVICE(1042,3020,"Samurai_IDE") +ENDVENDOR() + +VENDOR(1043,"Asustek Computer, Inc.") +ENDVENDOR() + +VENDOR(1044,"Distributed Processing Technology") + DEVICE(1044,1012,"Domino RAID Engine") + DEVICE(1044,a400,"SmartCache/Raid I-IV Controller") + DEVICE(1044,a500,"PCI Bridge") + DEVICE(1044,a501,"SmartRAID V Controller") +ENDVENDOR() + +VENDOR(1045,"OPTi Inc.") + DEVICE(1045,a0f8,"82C750 [Vendetta] USB Controller") + DEVICE(1045,c101,"92C264") + DEVICE(1045,c178,"92C178") + DEVICE(1045,c556,"82X556 [Viper]") + DEVICE(1045,c557,"82C557 [Viper-M]") + DEVICE(1045,c558,"82C558 [Viper-M ISA+IDE]") + DEVICE(1045,c567,"82C750 [Vendetta], device 0") + DEVICE(1045,c568,"82C750 [Vendetta], device 1") + DEVICE(1045,c569,"82C579 [Viper XPress+ Chipset]") + DEVICE(1045,c621,"82C621") + DEVICE(1045,c700,"82C700") + DEVICE(1045,c701,"82C701 [FireStar Plus]") + DEVICE(1045,c814,"82C814 [Firebridge 1]") + DEVICE(1045,c822,"82C822") + DEVICE(1045,c824,"82C824") + DEVICE(1045,c825,"82C825 [Firebridge 2]") + DEVICE(1045,c832,"82C832") + DEVICE(1045,c861,"82C861") + DEVICE(1045,c895,"82C895") + DEVICE(1045,c935,"EV1935 ECTIVA MachOne PCI Audio") + DEVICE(1045,d568,"82C825 [Firebridge 2]") +ENDVENDOR() + +VENDOR(1046,"IPC Corporation, Ltd.") +ENDVENDOR() + +VENDOR(1047,"Genoa Systems Corp") +ENDVENDOR() + +VENDOR(1048,"Elsa AG") + DEVICE(1048,1000,"QuickStep 1000") + DEVICE(1048,3000,"QuickStep 3000") +ENDVENDOR() + +VENDOR(1049,"Fountain Technologies, Inc.") +ENDVENDOR() + +VENDOR(104a,"SGS Thomson Microelectronics") + DEVICE(104a,0008,"STG 2000X") + DEVICE(104a,0009,"STG 1764X") + DEVICE(104a,1746,"STG 1764X") + DEVICE(104a,3520,"MPEG-II decoder card") +ENDVENDOR() + +VENDOR(104b,"BusLogic") + DEVICE(104b,0140,"BT-946C (old) [multimaster 01]") + DEVICE(104b,1040,"BT-946C (BA80C30) [MultiMaster 10]") + DEVICE(104b,8130,"Flashpoint LT") +ENDVENDOR() + +VENDOR(104c,"Texas Instruments") + DEVICE(104c,0500,"100 MBit LAN Controller") + DEVICE(104c,0508,"TMS380C2X Compressor Interface") + DEVICE(104c,1000,"Eagle i/f AS") + DEVICE(104c,3d04,"TVP4010 [Permedia]") + DEVICE(104c,3d07,"TVP4020 [Permedia 2]") + DEVICE(104c,8000,"PCILynx/PCILynx2 IEEE 1394 Link Layer Controller") + DEVICE(104c,8009,"OHCI Compliant FireWire Controller") + DEVICE(104c,8019,"TSB12LV23 OHCI Compliant IEEE-1394 Controller") + DEVICE(104c,a001,"TDC1570") + DEVICE(104c,a100,"TDC1561") + DEVICE(104c,ac10,"PCI1050") + DEVICE(104c,ac11,"PCI1053") + DEVICE(104c,ac12,"PCI1130") + DEVICE(104c,ac13,"PCI1031") + DEVICE(104c,ac15,"PCI1131") + DEVICE(104c,ac16,"PCI1250") + DEVICE(104c,ac17,"PCI1220") + DEVICE(104c,ac18,"PCI1260") + DEVICE(104c,ac19,"PCI1221") + DEVICE(104c,ac1a,"PCI1210") + DEVICE(104c,ac1b,"PCI1450") + DEVICE(104c,ac1c,"PCI1225") + DEVICE(104c,ac1d,"PCI1251A") + DEVICE(104c,ac1e,"PCI1211") + DEVICE(104c,ac1f,"PCI1251B") + DEVICE(104c,ac20,"TI 2030") + DEVICE(104c,ac30,"PCI1260 PC card Cardbus Controller") + DEVICE(104c,ac40,"PCI4450 PC card Cardbus Controller") + DEVICE(104c,ac41,"PCI4410 PC card Cardbus Controller") + DEVICE(104c,ac42,"PCI4451 PC card Cardbus Controller") + DEVICE(104c,ac50,"PCI1410 PC card Cardbus Controller") + DEVICE(104c,ac51,"PCI1420") + DEVICE(104c,ac52,"PCI1451 PC card Cardbus Controller") + DEVICE(104c,ac53,"PCI1421 PC card Cardbus Controller") + DEVICE(104c,fe00,"FireWire Host Controller") + DEVICE(104c,fe03,"12C01A FireWire Host Controller") +ENDVENDOR() + +VENDOR(104d,"Sony Corporation") + DEVICE(104d,8009,"CXD1947Q i.LINK Controller") + DEVICE(104d,8039,"CXD3222 i.LINK Controller") + DEVICE(104d,8056,"Rockwell HCF 56K modem") + DEVICE(104d,808a,"Memory Stick Controller") +ENDVENDOR() + +VENDOR(104e,"Oak Technology, Inc") + DEVICE(104e,0017,"OTI-64017") + DEVICE(104e,0107,"OTI-107 [Spitfire]") + DEVICE(104e,0109,"Video Adapter") + DEVICE(104e,0111,"OTI-64111 [Spitfire]") + DEVICE(104e,0217,"OTI-64217") + DEVICE(104e,0317,"OTI-64317") +ENDVENDOR() + +VENDOR(104f,"Co-time Computer Ltd") +ENDVENDOR() + +VENDOR(1050,"Winbond Electronics Corp") + DEVICE(1050,0000,"NE2000") + DEVICE(1050,0001,"W83769F") + DEVICE(1050,0105,"W82C105") + DEVICE(1050,0840,"W89C840") + DEVICE(1050,0940,"W89C940") + DEVICE(1050,5a5a,"W89C940F") + DEVICE(1050,9970,"W9970CF") +ENDVENDOR() + +VENDOR(1051,"Anigma, Inc.") +ENDVENDOR() + +VENDOR(1052,"?Young Micro Systems") +ENDVENDOR() + +VENDOR(1053,"Young Micro Systems") +ENDVENDOR() + +VENDOR(1054,"Hitachi, Ltd") +ENDVENDOR() + +VENDOR(1055,"EFAR Microsystems") + DEVICE(1055,9130,"EIDE Controller") + DEVICE(1055,9460,"PCI to ISA Bridge") + DEVICE(1055,9462,"USB Universal Host Controller [OHCI]") + DEVICE(1055,9463,"Power Management Controller [Bridge]") +ENDVENDOR() + +VENDOR(1056,"ICL") +ENDVENDOR() + +VENDOR(1057,"Motorola") + DEVICE(1057,0001,"MPC105 [Eagle]") + DEVICE(1057,0002,"MPC106 [Grackle]") + DEVICE(1057,0100,"MC145575 [HFC-PCI]") + DEVICE(1057,0431,"KTI829c 100VG") + DEVICE(1057,1801,"Audio I/O Controller (MIDI)") + DEVICE(1057,4801,"Raven") + DEVICE(1057,4802,"Falcon") + DEVICE(1057,4803,"Hawk") + DEVICE(1057,4806,"CPX8216") + DEVICE(1057,5600,"SM56 PCI Modem") +ENDVENDOR() + +VENDOR(1058,"Electronics & Telecommunications RSH") +ENDVENDOR() + +VENDOR(1059,"Teknor Industrial Computers Inc") +ENDVENDOR() + +VENDOR(105a,"Promise Technology, Inc.") + DEVICE(105a,0d30,"20265") + DEVICE(105a,4d30,"20267") + DEVICE(105a,4d33,"20246") + DEVICE(105a,4d38,"20262") + DEVICE(105a,5300,"DC5300") +ENDVENDOR() + +VENDOR(105b,"Foxconn International, Inc.") +ENDVENDOR() + +VENDOR(105c,"Wipro Infotech Limited") +ENDVENDOR() + +VENDOR(105d,"Number 9 Computer Company") + DEVICE(105d,2309,"Imagine 128") + DEVICE(105d,2339,"Imagine 128-II") + DEVICE(105d,493d,"Imagine 128 T2R [Ticket to Ride]") + DEVICE(105d,5348,"Revolution 4") +ENDVENDOR() + +VENDOR(105e,"Vtech Computers Ltd") +ENDVENDOR() + +VENDOR(105f,"Infotronic America Inc") +ENDVENDOR() + +VENDOR(1060,"United Microelectronics [UMC]") + DEVICE(1060,0001,"UM82C881") + DEVICE(1060,0002,"UM82C886") + DEVICE(1060,0101,"UM8673F") + DEVICE(1060,0881,"UM8881") + DEVICE(1060,0886,"UM8886F") + DEVICE(1060,0891,"UM8891A") + DEVICE(1060,1001,"UM886A") + DEVICE(1060,673a,"UM8886BF") + DEVICE(1060,673b,"EIDE Master/DMA") + DEVICE(1060,8710,"UM8710") + DEVICE(1060,886a,"UM8886A") + DEVICE(1060,8881,"UM8881F") + DEVICE(1060,8886,"UM8886F") + DEVICE(1060,888a,"UM8886A") + DEVICE(1060,8891,"UM8891A") + DEVICE(1060,9017,"UM9017F") + DEVICE(1060,9018,"UM9018") + DEVICE(1060,9026,"UM9026") + DEVICE(1060,e881,"UM8881N") + DEVICE(1060,e886,"UM8886N") + DEVICE(1060,e88a,"UM8886N") + DEVICE(1060,e891,"UM8891N") +ENDVENDOR() + +VENDOR(1061,"I.I.T.") + DEVICE(1061,0001,"AGX016") + DEVICE(1061,0002,"IIT3204/3501") +ENDVENDOR() + +VENDOR(1062,"Maspar Computer Corp") +ENDVENDOR() + +VENDOR(1063,"Ocean Office Automation") +ENDVENDOR() + +VENDOR(1064,"Alcatel") +ENDVENDOR() + +VENDOR(1065,"Texas Microsystems") +ENDVENDOR() + +VENDOR(1066,"PicoPower Technology") + DEVICE(1066,0000,"PT80C826") + DEVICE(1066,0001,"PT86C52x [Vesuvius]") + DEVICE(1066,0002,"PT80C524 [Nile]") + DEVICE(1066,0005,"National PC87550 System Controller") + DEVICE(1066,8002,"PT80C524 [Nile]") +ENDVENDOR() + +VENDOR(1067,"Mitsubishi Electric") + DEVICE(1067,1002,"VG500 [VolumePro Volume Rendering Accelerator]") +ENDVENDOR() + +VENDOR(1068,"Diversified Technology") +ENDVENDOR() + +VENDOR(1069,"Mylex Corporation") + DEVICE(1069,0001,"DAC960P") + DEVICE(1069,0002,"DAC960PD") + DEVICE(1069,0010,"DAC960PX") + DEVICE(1069,ba55,"eXtremeRAID support Device") +ENDVENDOR() + +VENDOR(106a,"Aten Research Inc") +ENDVENDOR() + +VENDOR(106b,"Apple Computer Inc.") + DEVICE(106b,0001,"Bandit PowerPC host bridge") + DEVICE(106b,0002,"Grand Central I/O") + DEVICE(106b,0003,"Control Video") + DEVICE(106b,0004,"PlanB Video-In") + DEVICE(106b,0007,"O'Hare I/O") + DEVICE(106b,000e,"Hydra Mac I/O") + DEVICE(106b,0010,"Heathrow Mac I/O") + DEVICE(106b,0017,"Paddington Mac I/O") + DEVICE(106b,0018,"UniNorth FireWire") + DEVICE(106b,0019,"KeyLargo USB") + DEVICE(106b,001e,"UniNorth PCI") + DEVICE(106b,001f,"UniNorth PCI") + DEVICE(106b,0020,"UniNorth AGP") + DEVICE(106b,0021,"UniNorth GMAC") + DEVICE(106b,0022,"KeyLargo Mac I/O") +ENDVENDOR() + +VENDOR(106c,"Hyundai Electronics America") + DEVICE(106c,8801,"Dual Pentium ISA/PCI Motherboard") + DEVICE(106c,8802,"PowerPC ISA/PCI Motherboard") + DEVICE(106c,8803,"Dual Window Graphics Accelerator") + DEVICE(106c,8804,"LAN Controller") + DEVICE(106c,8805,"100-BaseT LAN") +ENDVENDOR() + +VENDOR(106d,"Sequent Computer Systems") +ENDVENDOR() + +VENDOR(106e,"DFI, Inc") +ENDVENDOR() + +VENDOR(106f,"City Gate Development Ltd") +ENDVENDOR() + +VENDOR(1070,"Daewoo Telecom Ltd") +ENDVENDOR() + +VENDOR(1071,"Mitac") +ENDVENDOR() + +VENDOR(1072,"GIT Co Ltd") +ENDVENDOR() + +VENDOR(1073,"Yamaha Corporation") + DEVICE(1073,0001,"3D GUI Accelerator") + DEVICE(1073,0002,"YGV615 [RPA3 3D-Graphics Controller]") + DEVICE(1073,0003,"YMF-740") + DEVICE(1073,0004,"YMF-724") + DEVICE(1073,0005,"DS1 Audio") + DEVICE(1073,0006,"DS1 Audio") + DEVICE(1073,0008,"DS1 Audio") + DEVICE(1073,000a,"DS1L Audio") + DEVICE(1073,000c,"YMF-740C [DS-1L Audio Controller]") + DEVICE(1073,000d,"YMF-724F [DS-1 Audio Controller]") + DEVICE(1073,0010,"YMF-744B [DS-1S Audio Controller]") + DEVICE(1073,0012,"YMF-754 [DS-1E Audio Controller]") + DEVICE(1073,0020,"DS-1 Audio") +ENDVENDOR() + +VENDOR(1074,"NexGen Microsystems") + DEVICE(1074,4e78,"82c500/1") +ENDVENDOR() + +VENDOR(1075,"Advanced Integrations Research") +ENDVENDOR() + +VENDOR(1076,"Chaintech Computer Co. Ltd") +ENDVENDOR() + +VENDOR(1077,"Q Logic") + DEVICE(1077,1020,"ISP1020") + DEVICE(1077,1022,"ISP1022") + DEVICE(1077,1080,"ISP1080") + DEVICE(1077,1240,"ISP1240") + DEVICE(1077,1280,"ISP1280") + DEVICE(1077,2020,"ISP2020A") + DEVICE(1077,2100,"ISP2100") + DEVICE(1077,2200,"ISP2200") +ENDVENDOR() + +VENDOR(1078,"Cyrix Corporation") + DEVICE(1078,0000,"5510 [Grappa]") + DEVICE(1078,0001,"PCI Master") + DEVICE(1078,0002,"5520 [Cognac]") + DEVICE(1078,0100,"5530 Legacy [Kahlua]") + DEVICE(1078,0101,"5530 SMI [Kahlua]") + DEVICE(1078,0102,"5530 IDE [Kahlua]") + DEVICE(1078,0103,"5530 Audio [Kahlua]") + DEVICE(1078,0104,"5530 Video [Kahlua]") +ENDVENDOR() + +VENDOR(1079,"I-Bus") +ENDVENDOR() + +VENDOR(107a,"NetWorth") +ENDVENDOR() + +VENDOR(107b,"Gateway 2000") +ENDVENDOR() + +VENDOR(107c,"LG Electronics [Lucky Goldstar Co. Ltd]") +ENDVENDOR() + +VENDOR(107d,"LeadTek Research Inc.") + DEVICE(107d,0000,"P86C850") +ENDVENDOR() + +VENDOR(107e,"Interphase Corporation") + DEVICE(107e,0001,"ATM Interface Card") + DEVICE(107e,0002,"100 VG AnyLan Controller") + DEVICE(107e,0008,"155 Mbit ATM Controller") +ENDVENDOR() + +VENDOR(107f,"Data Technology Corporation") + DEVICE(107f,0802,"SL82C105") +ENDVENDOR() + +VENDOR(1080,"Contaq Microsystems") + DEVICE(1080,0600,"82C599") + DEVICE(1080,c691,"Cypress CY82C691") + DEVICE(1080,c693,"82c693") +ENDVENDOR() + +VENDOR(1081,"Supermac Technology") + DEVICE(1081,0d47,"Radius PCI to NuBUS Bridge") +ENDVENDOR() + +VENDOR(1082,"EFA Corporation of America") +ENDVENDOR() + +VENDOR(1083,"Forex Computer Corporation") + DEVICE(1083,0001,"FR710") +ENDVENDOR() + +VENDOR(1084,"Parador") +ENDVENDOR() + +VENDOR(1085,"Tulip Computers Int.B.V.") +ENDVENDOR() + +VENDOR(1086,"J. Bond Computer Systems") +ENDVENDOR() + +VENDOR(1087,"Cache Computer") +ENDVENDOR() + +VENDOR(1088,"Microcomputer Systems (M) Son") +ENDVENDOR() + +VENDOR(1089,"Data General Corporation") +ENDVENDOR() + +VENDOR(108a,"Bit3 Computer Corp.") + DEVICE(108a,0001,"VME Bridge Model 617") + DEVICE(108a,0010,"VME Bridge Model 618") + DEVICE(108a,3000,"VME Bridge Model 2706") +ENDVENDOR() + +VENDOR(108c,"Oakleigh Systems Inc.") +ENDVENDOR() + +VENDOR(108d,"Olicom") + DEVICE(108d,0001,"Token-Ring 16/4 PCI Adapter (3136/3137)") + DEVICE(108d,0002,"16/4 Token Ring") + DEVICE(108d,0004,"RapidFire 3139 Token-Ring 16/4 PCI Adapter") + DEVICE(108d,0005,"GoCard 3250 Token-Ring 16/4 CardBus PC Card") + DEVICE(108d,0006,"OC-3530 RapidFire Token-Ring 100") + DEVICE(108d,0007,"RapidFire 3141 Token-Ring 16/4 PCI Fiber Adapter") + DEVICE(108d,0008,"RapidFire 3540 HSTR 100/16/4 PCI Adapter") + DEVICE(108d,0011,"OC-2315") + DEVICE(108d,0012,"OC-2325") + DEVICE(108d,0013,"OC-2183/2185") + DEVICE(108d,0014,"OC-2326") + DEVICE(108d,0019,"OC-2327/2250 10/100 Ethernet Adapter") + DEVICE(108d,0021,"OC-6151/6152 [RapidFire ATM 155]") + DEVICE(108d,0022,"ATM Adapter") +ENDVENDOR() + +VENDOR(108e,"Sun Microsystems Computer Corp.") + DEVICE(108e,0001,"EBUS") + DEVICE(108e,1000,"EBUS") + DEVICE(108e,1001,"Happy Meal") + DEVICE(108e,1100,"RIO EBUS") + DEVICE(108e,1101,"RIO GEM") + DEVICE(108e,1102,"RIO 1394") + DEVICE(108e,1103,"RIO USB") + DEVICE(108e,2bad,"GEM") + DEVICE(108e,5000,"Simba Advanced PCI Bridge") + DEVICE(108e,5043,"SunPCI Co-processor") + DEVICE(108e,8000,"Psycho PCI Bus Module") + DEVICE(108e,8001,"Schizo PCI Bus Module") + DEVICE(108e,a000,"Ultra IIi") + DEVICE(108e,a001,"Ultra IIe") +ENDVENDOR() + +VENDOR(108f,"Systemsoft") +ENDVENDOR() + +VENDOR(1090,"Encore Computer Corporation") +ENDVENDOR() + +VENDOR(1091,"Intergraph Corporation") + DEVICE(1091,0020,"3D graphics processor") + DEVICE(1091,0021,"3D graphics processor w/Texturing") + DEVICE(1091,0040,"3D graphics frame buffer") + DEVICE(1091,0041,"3D graphics frame buffer") + DEVICE(1091,0060,"Proprietary bus bridge") + DEVICE(1091,00e4,"Powerstorm 4D50T") + DEVICE(1091,0720,"Motion JPEG codec") +ENDVENDOR() + +VENDOR(1092,"Diamond Multimedia Systems") + DEVICE(1092,00a0,"Speedstar Pro SE") + DEVICE(1092,00a8,"Speedstar 64") + DEVICE(1092,08d4,"Supra 2260 Modem") + DEVICE(1092,1092,"Viper V330") + DEVICE(1092,6120,"Maximum DVD") + DEVICE(1092,8810,"Stealth SE") + DEVICE(1092,8811,"Stealth 64/SE") + DEVICE(1092,8880,"Stealth") + DEVICE(1092,8881,"Stealth") + DEVICE(1092,88b0,"Stealth 64") + DEVICE(1092,88b1,"Stealth 64") + DEVICE(1092,88c0,"Stealth 64") + DEVICE(1092,88c1,"Stealth 64") + DEVICE(1092,88d0,"Stealth 64") + DEVICE(1092,88d1,"Stealth 64") + DEVICE(1092,88f0,"Stealth 64") + DEVICE(1092,88f1,"Stealth 64") + DEVICE(1092,9999,"DMD-I0928-1 \"Monster sound\" sound chip") +ENDVENDOR() + +VENDOR(1093,"National Instruments") + DEVICE(1093,0160,"PCI-DIO-96") + DEVICE(1093,0162,"PCI-MIO-16XE-50") + DEVICE(1093,1170,"PCI-MIO-16XE-10") + DEVICE(1093,1180,"PCI-MIO-16E-1") + DEVICE(1093,1190,"PCI-MIO-16E-4") + DEVICE(1093,1330,"PCI-6031E") + DEVICE(1093,1350,"PCI-6071E") + DEVICE(1093,2a60,"PCI-6023E") + DEVICE(1093,b001,"IMAQ-PCI-1408") + DEVICE(1093,b011,"IMAQ-PXI-1408") + DEVICE(1093,b021,"IMAQ-PCI-1424") + DEVICE(1093,b031,"IMAQ-PCI-1413") + DEVICE(1093,b041,"IMAQ-PCI-1407") + DEVICE(1093,b051,"IMAQ-PXI-1407") + DEVICE(1093,b061,"IMAQ-PCI-1411") + DEVICE(1093,b071,"IMAQ-PCI-1422") + DEVICE(1093,b081,"IMAQ-PXI-1422") + DEVICE(1093,b091,"IMAQ-PXI-1411") + DEVICE(1093,c801,"PCI-GPIB") +ENDVENDOR() + +VENDOR(1094,"First International Computers [FIC]") +ENDVENDOR() + +VENDOR(1095,"CMD Technology Inc") + DEVICE(1095,0640,"PCI0640") + DEVICE(1095,0643,"PCI0643") + DEVICE(1095,0646,"PCI0646") + DEVICE(1095,0647,"PCI0647") + DEVICE(1095,0648,"PCI0648") + DEVICE(1095,0649,"PCI0649") + DEVICE(1095,0650,"PBC0650A") + DEVICE(1095,0670,"USB0670") + DEVICE(1095,0673,"USB0673") +ENDVENDOR() + +VENDOR(1096,"Alacron") +ENDVENDOR() + +VENDOR(1097,"Appian Technology") +ENDVENDOR() + +VENDOR(1098,"Quantum Designs (H.K.) Ltd") + DEVICE(1098,0001,"QD-8500") + DEVICE(1098,0002,"QD-8580") +ENDVENDOR() + +VENDOR(1099,"Samsung Electronics Co., Ltd") +ENDVENDOR() + +VENDOR(109a,"Packard Bell") +ENDVENDOR() + +VENDOR(109b,"Gemlight Computer Ltd.") +ENDVENDOR() + +VENDOR(109c,"Megachips Corporation") +ENDVENDOR() + +VENDOR(109d,"Zida Technologies Ltd.") +ENDVENDOR() + +VENDOR(109e,"Brooktree Corporation") + DEVICE(109e,0350,"Bt848 TV with DMA push") + DEVICE(109e,0351,"Bt849A Video capture") + DEVICE(109e,036c,"Bt879(?) Video Capture") + DEVICE(109e,036e,"Bt878") + DEVICE(109e,036f,"Bt879") + DEVICE(109e,0370,"Bt880 Video Capture") + DEVICE(109e,0878,"Bt878") + DEVICE(109e,0879,"Bt879 Video Capture (Audio Section)") + DEVICE(109e,0880,"Bt880 Video Capture (Audio Section)") + DEVICE(109e,2115,"BtV 2115 Mediastream controller") + DEVICE(109e,2125,"BtV 2125 Mediastream controller") + DEVICE(109e,2164,"BtV 2164") + DEVICE(109e,2165,"BtV 2165") + DEVICE(109e,8230,"Bt8230 ATM Segment/Reassembly Ctrlr (SRC)") + DEVICE(109e,8472,"Bt8472") + DEVICE(109e,8474,"Bt8474") +ENDVENDOR() + +VENDOR(109f,"Trigem Computer Inc.") +ENDVENDOR() + +VENDOR(10a0,"Meidensha Corporation") +ENDVENDOR() + +VENDOR(10a1,"Juko Electronics Ind. Co. Ltd") +ENDVENDOR() + +VENDOR(10a2,"Quantum Corporation") +ENDVENDOR() + +VENDOR(10a3,"Everex Systems Inc") +ENDVENDOR() + +VENDOR(10a4,"Globe Manufacturing Sales") +ENDVENDOR() + +VENDOR(10a5,"Racal Interlan") +ENDVENDOR() + +VENDOR(10a6,"Informtech Industrial Ltd.") +ENDVENDOR() + +VENDOR(10a7,"Benchmarq Microelectronics") +ENDVENDOR() + +VENDOR(10a8,"Sierra Semiconductor") + DEVICE(10a8,0000,"STB Horizon 64") +ENDVENDOR() + +VENDOR(10a9,"Silicon Graphics, Inc.") + DEVICE(10a9,0001,"Crosstalk to PCI Bridge") + DEVICE(10a9,0002,"Linc I/O controller") + DEVICE(10a9,0003,"IOC3 I/O controller") + DEVICE(10a9,0004,"O2 MACE") + DEVICE(10a9,0005,"RAD Audio") + DEVICE(10a9,0006,"HPCEX") + DEVICE(10a9,0007,"RPCEX") + DEVICE(10a9,0008,"DiVO VIP") + DEVICE(10a9,0009,"Alteon Gigabit Ethernet") + DEVICE(10a9,0010,"AMP Video I/O") + DEVICE(10a9,0011,"GRIP") + DEVICE(10a9,0012,"SGH PSHAC GSN") + DEVICE(10a9,1001,"Magic Carpet") + DEVICE(10a9,1002,"Lithium") + DEVICE(10a9,1003,"Dual JPEG 1") + DEVICE(10a9,1004,"Dual JPEG 2") + DEVICE(10a9,1005,"Dual JPEG 3") + DEVICE(10a9,1006,"Dual JPEG 4") + DEVICE(10a9,1007,"Dual JPEG 5") + DEVICE(10a9,1008,"Cesium") + DEVICE(10a9,2001,"Fibre Channel") + DEVICE(10a9,2002,"ASDE") + DEVICE(10a9,8001,"O2 1394") + DEVICE(10a9,8002,"G-net NT") +ENDVENDOR() + +VENDOR(10aa,"ACC Microelectronics") + DEVICE(10aa,0000,"ACCM 2188") +ENDVENDOR() + +VENDOR(10ab,"Digicom") +ENDVENDOR() + +VENDOR(10ac,"Honeywell IAC") +ENDVENDOR() + +VENDOR(10ad,"Symphony Labs") + DEVICE(10ad,0001,"W83769F") + DEVICE(10ad,0003,"SL82C103") + DEVICE(10ad,0005,"SL82C105") + DEVICE(10ad,0103,"SL82c103") + DEVICE(10ad,0105,"SL82c105") + DEVICE(10ad,0565,"W83C553") +ENDVENDOR() + +VENDOR(10ae,"Cornerstone Technology") +ENDVENDOR() + +VENDOR(10af,"Micro Computer Systems Inc") +ENDVENDOR() + +VENDOR(10b0,"CardExpert Technology") +ENDVENDOR() + +VENDOR(10b1,"Cabletron Systems Inc") +ENDVENDOR() + +VENDOR(10b2,"Raytheon Company") +ENDVENDOR() + +VENDOR(10b3,"Databook Inc") + DEVICE(10b3,3106,"DB87144") + DEVICE(10b3,b106,"DB87144") +ENDVENDOR() + +VENDOR(10b4,"STB Systems Inc") + DEVICE(10b4,1b1d,"Velocity 128 3D") +ENDVENDOR() + +VENDOR(10b5,"PLX Technology, Inc.") + DEVICE(10b5,0001,"i960 PCI bus interface") + DEVICE(10b5,1076,"VScom 800 8 port serial adaptor") + DEVICE(10b5,1077,"VScom 400 4 port serial adaptor") + DEVICE(10b5,9036,"9036") + DEVICE(10b5,9050,"PCI <-> IOBus Bridge") + DEVICE(10b5,9060,"9060") + DEVICE(10b5,906d,"9060SD") + DEVICE(10b5,906e,"9060ES") + DEVICE(10b5,9080,"9080") +ENDVENDOR() + +VENDOR(10b6,"Madge Networks") + DEVICE(10b6,0001,"Smart 16/4 PCI Ringnode") + DEVICE(10b6,0002,"Smart 16/4 PCI Ringnode Mk2") + DEVICE(10b6,0003,"Smart 16/4 PCI Ringnode Mk3") + DEVICE(10b6,0004,"Smart 16/4 PCI Ringnode Mk1") + DEVICE(10b6,0006,"16/4 Cardbus Adapter") + DEVICE(10b6,0007,"Presto PCI Adapter") + DEVICE(10b6,0009,"Smart 100/16/4 PCI-HS Ringnode") + DEVICE(10b6,000a,"Smart 100/16/4 PCI Ringnode") + DEVICE(10b6,000b,"16/4 CardBus Adapter Mk2") + DEVICE(10b6,1000,"Collage 25 ATM Adapter") + DEVICE(10b6,1001,"Collage 155 ATM Server Adapter") +ENDVENDOR() + +VENDOR(10b7,"3Com Corporation") + DEVICE(10b7,0001,"3c985 1000BaseSX") + DEVICE(10b7,3390,"Token Link Velocity") + DEVICE(10b7,3590,"3c359 TokenLink Velocity XL") + DEVICE(10b7,4500,"3c450 Cyclone/unknown") + DEVICE(10b7,5055,"3c555 Laptop Hurricane") + DEVICE(10b7,6055,"3c556 Laptop Tornado") + DEVICE(10b7,6056,"3c556B Laptop Hurricane") + DEVICE(10b7,5057,"3c575 [Megahertz] 10/100 LAN CardBus") + DEVICE(10b7,5157,"3CCFE575BT Cyclone CardBus") + DEVICE(10b7,5257,"3CCFE575CT Tornado CardBus") + DEVICE(10b7,5900,"3c590 10BaseT [Vortex]") + DEVICE(10b7,5920,"3c592 EISA 10mbps Demon/Vortex") + DEVICE(10b7,5950,"3c595 100BaseTX [Vortex]") + DEVICE(10b7,5951,"3c595 100BaseT4 [Vortex]") + DEVICE(10b7,5952,"3c595 100Base-MII [Vortex]") + DEVICE(10b7,5970,"3c597 EISA Fast Demon/Vortex") + DEVICE(10b7,5b57,"3c595 [Megahertz] 10/100 LAN CardBus") + DEVICE(10b7,6560,"3CCFE656 Cyclone CardBus") + DEVICE(10b7,6562,"3CCFEM656B Cyclone CardBus") + DEVICE(10b7,6564,"3CXFEM656C Tornado CardBus") + DEVICE(10b7,7646,"3cSOHO100-TX Hurricane") + DEVICE(10b7,8811,"Token ring") + DEVICE(10b7,9000,"3c900 10BaseT [Boomerang]") + DEVICE(10b7,9001,"3c900 Combo [Boomerang]") + DEVICE(10b7,9004,"3c900B-TPO [Etherlink XL TPO]") + DEVICE(10b7,9005,"3c900B-Combo [Etherlink XL Combo]") + DEVICE(10b7,9006,"3c900B-TPC [Etherlink XL TPC]") + DEVICE(10b7,900a,"3c900B-FL [Etherlink XL FL]") + DEVICE(10b7,9050,"3c905 100BaseTX [Boomerang]") + DEVICE(10b7,9051,"3c905 100BaseT4") + DEVICE(10b7,9055,"3c905B 100BaseTX [Cyclone]") + DEVICE(10b7,9056,"3c905B-T4") + DEVICE(10b7,9058,"3c905B-Combo [Deluxe Etherlink XL 10/100]") + DEVICE(10b7,905a,"3c905B-FX [Fast Etherlink XL FX 10/100]") + DEVICE(10b7,9200,"3c905C-TX [Fast Etherlink]") + DEVICE(10b7,9800,"3c980-TX [Fast Etherlink XL Server Adapter]") + DEVICE(10b7,9805,"3c980-TX 10/100baseTX NIC [Python-T]") +ENDVENDOR() + +VENDOR(10b8,"Standard Microsystems Corp [SMC]") + DEVICE(10b8,0005,"83C170QF") + DEVICE(10b8,0006,"LANEPIC") + DEVICE(10b8,1000,"FDC 37c665") + DEVICE(10b8,1001,"FDC 37C922") + DEVICE(10b8,a011,"83C170QF") + DEVICE(10b8,b106,"SMC34C90") +ENDVENDOR() + +VENDOR(10b9,"Acer Laboratories Inc. [ALi]") + DEVICE(10b9,0111,"C-Media CMI8738/C3DX Audio Device (OEM)") + DEVICE(10b9,1435,"M1435") + DEVICE(10b9,1445,"M1445") + DEVICE(10b9,1449,"M1449") + DEVICE(10b9,1451,"M1451") + DEVICE(10b9,1461,"M1461") + DEVICE(10b9,1489,"M1489") + DEVICE(10b9,1511,"M1511 [Aladdin]") + DEVICE(10b9,1512,"M1512 [Aladdin]") + DEVICE(10b9,1513,"M1513 [Aladdin]") + DEVICE(10b9,1521,"M1521 [Aladdin III]") + DEVICE(10b9,1523,"M1523") + DEVICE(10b9,1531,"M1531 [Aladdin IV]") + DEVICE(10b9,1533,"M1533 PCI to ISA Bridge [Aladdin IV]") + DEVICE(10b9,1541,"M1541") + DEVICE(10b9,1543,"M1543") + DEVICE(10b9,1621,"M1621") + DEVICE(10b9,1631,"ALI M1631 PCI North Bridge Aladdin Pro III") + DEVICE(10b9,1641,"ALI M1641 PCI North Bridge Aladdin Pro IV") + DEVICE(10b9,3141,"M3141") + DEVICE(10b9,3143,"M3143") + DEVICE(10b9,3145,"M3145") + DEVICE(10b9,3147,"M3147") + DEVICE(10b9,3149,"M3149") + DEVICE(10b9,3151,"M3151") + DEVICE(10b9,3307,"M3307") + DEVICE(10b9,3309,"M3309") + DEVICE(10b9,5212,"M4803") + DEVICE(10b9,5215,"MS4803") + DEVICE(10b9,5217,"M5217H") + DEVICE(10b9,5219,"M5219") + DEVICE(10b9,5225,"M5225") + DEVICE(10b9,5229,"M5229 IDE") + DEVICE(10b9,5235,"M5225") + DEVICE(10b9,5237,"M5237 USB") + DEVICE(10b9,5243,"M5243") + DEVICE(10b9,5247,"M5247") + DEVICE(10b9,7101,"M7101 PMU") +ENDVENDOR() + +VENDOR(10ba,"Mitsubishi Electric Corp.") + DEVICE(10ba,0301,"AccelGraphics AccelECLIPSE") +ENDVENDOR() + +VENDOR(10bb,"Dapha Electronics Corporation") +ENDVENDOR() + +VENDOR(10bc,"Advanced Logic Research") +ENDVENDOR() + +VENDOR(10bd,"Surecom Technology") + DEVICE(10bd,0e34,"NE-34") +ENDVENDOR() + +VENDOR(10be,"Tseng Labs International Co.") +ENDVENDOR() + +VENDOR(10bf,"Most Inc") +ENDVENDOR() + +VENDOR(10c0,"Boca Research Inc.") +ENDVENDOR() + +VENDOR(10c1,"ICM Co., Ltd.") +ENDVENDOR() + +VENDOR(10c2,"Auspex Systems Inc.") +ENDVENDOR() + +VENDOR(10c3,"Samsung Semiconductors, Inc.") + DEVICE(10c3,1100,"Smartether100 SC1100 LAN Adapter (i82557B)") +ENDVENDOR() + +VENDOR(10c4,"Award Software International Inc.") +ENDVENDOR() + +VENDOR(10c5,"Xerox Corporation") +ENDVENDOR() + +VENDOR(10c6,"Rambus Inc.") +ENDVENDOR() + +VENDOR(10c7,"Media Vision") +ENDVENDOR() + +VENDOR(10c8,"Neomagic Corporation") + DEVICE(10c8,0001,"NM2070 [MagicGraph NM2070]") + DEVICE(10c8,0002,"NM2090 [MagicGraph 128V]") + DEVICE(10c8,0003,"NM2093 [MagicGraph 128ZV]") + DEVICE(10c8,0004,"NM2160 [MagicGraph 128XD]") + DEVICE(10c8,0005,"[MagicMedia 256AV]") + DEVICE(10c8,0006,"NM2360 [MagicMedia 256ZX]") + DEVICE(10c8,0083,"[MagicGraph 128ZV Plus]") + DEVICE(10c8,8005,"[MagicMedia 256AV Audio]") + DEVICE(10c8,8006,"NM2360 [MagicMedia 256ZX Audio]") +ENDVENDOR() + +VENDOR(10c9,"Dataexpert Corporation") +ENDVENDOR() + +VENDOR(10ca,"Fujitsu Microelectr., Inc.") +ENDVENDOR() + +VENDOR(10cb,"Omron Corporation") +ENDVENDOR() + +VENDOR(10cc,"Mentor ARC Inc") +ENDVENDOR() + +VENDOR(10cd,"Advanced System Products, Inc") + DEVICE(10cd,1100,"ASC1100") + DEVICE(10cd,1200,"ASC1200 [(abp940) Fast SCSI-II]") + DEVICE(10cd,1300,"ABP940-U / ABP960-U") + DEVICE(10cd,2300,"ABP940-UW") +ENDVENDOR() + +VENDOR(10ce,"Radius") +ENDVENDOR() + +VENDOR(10cf,"Citicorp TTI") + DEVICE(10cf,2001,"mb86605") +ENDVENDOR() + +VENDOR(10d0,"Fujitsu Limited") +ENDVENDOR() + +VENDOR(10d1,"FuturePlus Systems Corp.") +ENDVENDOR() + +VENDOR(10d2,"Molex Incorporated") +ENDVENDOR() + +VENDOR(10d3,"Jabil Circuit Inc") +ENDVENDOR() + +VENDOR(10d4,"Hualon Microelectronics") +ENDVENDOR() + +VENDOR(10d5,"Autologic Inc.") +ENDVENDOR() + +VENDOR(10d6,"Cetia") +ENDVENDOR() + +VENDOR(10d7,"BCM Advanced Research") +ENDVENDOR() + +VENDOR(10d8,"Advanced Peripherals Labs") +ENDVENDOR() + +VENDOR(10d9,"Macronix, Inc. [MXIC]") + DEVICE(10d9,0512,"MX98713") + DEVICE(10d9,0531,"MX987x5") + DEVICE(10d9,8625,"MX86250") + DEVICE(10d9,8888,"MX86200") +ENDVENDOR() + +VENDOR(10da,"Compaq IPG-Austin") + DEVICE(10da,0508,"TC4048 Token Ring 4/16") + DEVICE(10da,3390,"Tl3c3x9") +ENDVENDOR() + +VENDOR(10db,"Rohm LSI Systems, Inc.") +ENDVENDOR() + +VENDOR(10dc,"CERN/ECP/EDU") + DEVICE(10dc,0001,"STAR/RD24 SCI-PCI (PMC)") + DEVICE(10dc,0002,"TAR/RD24 SCI-PCI (PMC)") + DEVICE(10dc,0021,"HIPPI destination") + DEVICE(10dc,0022,"HIPPI source") + DEVICE(10dc,10dc,"ATT2C15-3 FPGA") +ENDVENDOR() + +VENDOR(10dd,"Evans & Sutherland") +ENDVENDOR() + +VENDOR(10de,"nVidia Corporation") + DEVICE(10de,0008,"EDGE 3D [NV1]") + DEVICE(10de,0009,"EDGE 3D [NV1]") + DEVICE(10de,0010,"Mutara V08 [NV2]") + DEVICE(10de,0020,"Riva TnT 128 [NV04]") + DEVICE(10de,0028,"Riva TnT2 [NV5]") + DEVICE(10de,0029,"Riva TnT2 Ultra [NV5]") + DEVICE(10de,002a,"Riva TnT2 [NV5]") + DEVICE(10de,002b,"Riva TnT2 [NV5]") + DEVICE(10de,002c,"Vanta [NV6]") + DEVICE(10de,002d,"Vanta [NV6]") + DEVICE(10de,002e,"Vanta [NV6]") + DEVICE(10de,002f,"Vanta [NV6]") + DEVICE(10de,00a0,"Riva TNT2") + DEVICE(10de,0100,"GeForce 256") + DEVICE(10de,0101,"GeForce 256 DDR") + DEVICE(10de,0103,"Quadro (GeForce 256 GL)") + DEVICE(10de,0110,"NV11") + DEVICE(10de,0111,"NV11 DDR") + DEVICE(10de,0113,"NV11 GL") + DEVICE(10de,0150,"NV15 (Geforce2 GTS)") + DEVICE(10de,0151,"NV15 DDR (Geforce2 GTS)") + DEVICE(10de,0152,"NV15 Bladerunner (Geforce2 GTS)") + DEVICE(10de,0153,"NV15 GL (Quadro2)") +ENDVENDOR() + +VENDOR(10df,"Emulex Corporation") + DEVICE(10df,10df,"Light Pulse Fibre Channel Adapter") + DEVICE(10df,1ae5,"LP6000 Fibre Channel Host Adapter") + DEVICE(10df,f700,"LP7000 Fibre Channel Host Adapter") +ENDVENDOR() + +VENDOR(10e0,"Integrated Micro Solutions Inc.") + DEVICE(10e0,5026,"IMS5026/27/28") + DEVICE(10e0,5027,"IMS5027") + DEVICE(10e0,5028,"IMS5028") + DEVICE(10e0,8849,"IMS8849") + DEVICE(10e0,8853,"IMS8853") + DEVICE(10e0,9128,"IMS9129 [Twin turbo 128]") +ENDVENDOR() + +VENDOR(10e1,"Tekram Technology Co.,Ltd.") + DEVICE(10e1,0391,"TRM-S1040") + DEVICE(10e1,690c,"DC-690c") + DEVICE(10e1,dc29,"DC-290") +ENDVENDOR() + +VENDOR(10e2,"Aptix Corporation") +ENDVENDOR() + +VENDOR(10e3,"Tundra Semiconductor Corp.") + DEVICE(10e3,0000,"CA91C042 [Universe]") + DEVICE(10e3,0860,"CA91C860 [QSpan]") +ENDVENDOR() + +VENDOR(10e4,"Tandem Computers") +ENDVENDOR() + +VENDOR(10e5,"Micro Industries Corporation") +ENDVENDOR() + +VENDOR(10e6,"Gainbery Computer Products Inc.") +ENDVENDOR() + +VENDOR(10e7,"Vadem") +ENDVENDOR() + +VENDOR(10e8,"Applied Micro Circuits Corporation") + DEVICE(10e8,2011,"Q-Motion Video Capture/Edit board") + DEVICE(10e8,4750,"S5930 [Matchmaker]") + DEVICE(10e8,5920,"S5920") + DEVICE(10e8,8043,"LANai4.x [Myrinet LANai interface chip]") + DEVICE(10e8,8062,"S5933_PARASTATION") + DEVICE(10e8,807d,"S5933 [Matchmaker]") + DEVICE(10e8,8088,"Kingsberg Spacetec Format Synchronizer") + DEVICE(10e8,8089,"Kingsberg Spacetec Serial Output Board") + DEVICE(10e8,809c,"S5933_HEPC3") + DEVICE(10e8,80d7,"PCI-9112") + DEVICE(10e8,80d9,"PCI-9118") + DEVICE(10e8,80da,"PCI-9812") + DEVICE(10e8,811a,"PCI-IEEE1355-DS-DE Interface") + DEVICE(10e8,8170,"S5933 \"Matchmaker\"") +ENDVENDOR() + +VENDOR(10e9,"Alps Electric Co., Ltd.") +ENDVENDOR() + +VENDOR(10ea,"Intergraphics Systems") + DEVICE(10ea,1680,"IGA-1680") + DEVICE(10ea,1682,"IGA-1682") + DEVICE(10ea,1683,"IGA-1683") + DEVICE(10ea,2000,"CyberPro 2000") + DEVICE(10ea,2010,"CyberPro 2000A") + DEVICE(10ea,5000,"CyberPro 5000") + DEVICE(10ea,5050,"CyberPro 5050") +ENDVENDOR() + +VENDOR(10eb,"Artists Graphics") + DEVICE(10eb,0101,"3GA") + DEVICE(10eb,8111,"Twist3 Frame Grabber") +ENDVENDOR() + +VENDOR(10ec,"Realtek Semiconductor Co., Ltd.") + DEVICE(10ec,8029,"RTL-8029(AS)") + DEVICE(10ec,8129,"RTL-8129") + DEVICE(10ec,8138,"RT8139 (B/C) Cardbus Fast Ethernet Adapter") + DEVICE(10ec,8139,"RTL-8139") +ENDVENDOR() + +VENDOR(10ed,"Ascii Corporation") + DEVICE(10ed,7310,"V7310") +ENDVENDOR() + +VENDOR(10ee,"Xilinx, Inc.") + DEVICE(10ee,3fc0,"RME Digi96") + DEVICE(10ee,3fc1,"RME Digi96/8") + DEVICE(10ee,3fc2,"RME Digi96/8 Pro") + DEVICE(10ee,3fc3,"RME Digi96/8 Pad") +ENDVENDOR() + +VENDOR(10ef,"Racore Computer Products, Inc.") + DEVICE(10ef,8154,"M815x Token Ring Adapter") +ENDVENDOR() + +VENDOR(10f0,"Peritek Corporation") +ENDVENDOR() + +VENDOR(10f1,"Tyan Computer") +ENDVENDOR() + +VENDOR(10f2,"Achme Computer, Inc.") +ENDVENDOR() + +VENDOR(10f3,"Alaris, Inc.") +ENDVENDOR() + +VENDOR(10f4,"S-MOS Systems, Inc.") +ENDVENDOR() + +VENDOR(10f5,"NKK Corporation") + DEVICE(10f5,a001,"NDR4000 [NR4600 Bridge]") +ENDVENDOR() + +VENDOR(10f6,"Creative Electronic Systems SA") +ENDVENDOR() + +VENDOR(10f7,"Matsushita Electric Industrial Co., Ltd.") +ENDVENDOR() + +VENDOR(10f8,"Altos India Ltd") +ENDVENDOR() + +VENDOR(10f9,"PC Direct") +ENDVENDOR() + +VENDOR(10fa,"Truevision") + DEVICE(10fa,000c,"TARGA 1000") +ENDVENDOR() + +VENDOR(10fb,"Thesys Gesellschaft für Mikroelektronik mbH") +ENDVENDOR() + +VENDOR(10fc,"I-O Data Device, Inc.") +ENDVENDOR() + +VENDOR(10fd,"Soyo Computer, Inc") +ENDVENDOR() + +VENDOR(10fe,"Fast Multimedia AG") +ENDVENDOR() + +VENDOR(10ff,"NCube") +ENDVENDOR() + +VENDOR(1100,"Jazz Multimedia") +ENDVENDOR() + +VENDOR(1101,"Initio Corporation") + DEVICE(1101,1060,"INI-A100U2W") + DEVICE(1101,9100,"INI-9100/9100W") + DEVICE(1101,9400,"INI-940") + DEVICE(1101,9401,"INI-950") + DEVICE(1101,9500,"360P") +ENDVENDOR() + +VENDOR(1102,"Creative Labs") + DEVICE(1102,0002,"SB Live! EMU10000") + DEVICE(1102,7002,"SB Live!") +ENDVENDOR() + +VENDOR(1103,"Triones Technologies, Inc.") + DEVICE(1103,0003,"HPT343") + DEVICE(1103,0004,"HPT366") +ENDVENDOR() + +VENDOR(1104,"RasterOps Corp.") +ENDVENDOR() + +VENDOR(1105,"Sigma Designs, Inc.") + DEVICE(1105,8300,"REALmagic Hollywood Plus DVD Decoder") +ENDVENDOR() + +VENDOR(1106,"VIA Technologies, Inc.") + DEVICE(1106,0305,"VT8363/8365 [KT133/KM133]") + DEVICE(1106,0391,"VT8371 [KX133]") + DEVICE(1106,0501,"VT8501 [Apollo MVP4]") + DEVICE(1106,0505,"VT82C505") + DEVICE(1106,0561,"VT82C561") + DEVICE(1106,0571,"Bus Master IDE") + DEVICE(1106,0576,"VT82C576 3V [Apollo Master]") + DEVICE(1106,0585,"VT82C585VP [Apollo VP1/VPX]") + DEVICE(1106,0586,"VT82C586/A/B PCI-to-ISA [Apollo VP]") + DEVICE(1106,0595,"VT82C595 [Apollo VP2]") + DEVICE(1106,0596,"VT82C596 ISA [Mobile South]") + DEVICE(1106,0597,"VT82C597 [Apollo VP3]") + DEVICE(1106,0598,"VT82C598 [Apollo MVP3]") + DEVICE(1106,0601,"VT8601 [Apollo ProMedia]") + DEVICE(1106,0605,"VT8605 [ProSavage PM133]") + DEVICE(1106,0680,"VT82C680 [Apollo P6]") + DEVICE(1106,0686,"VT82C686 [Apollo Super South]") + DEVICE(1106,0691,"VT82C693A/694x [Apollo PRO133x]") + DEVICE(1106,0698,"VT82C693A [Apollo Pro133 AGP]") + DEVICE(1106,0693,"VT82C693 [Apollo Pro Plus]") + DEVICE(1106,0926,"VT82C926 [Amazon]") + DEVICE(1106,1000,"VT82C570MV") + DEVICE(1106,1106,"VT82C570MV") + DEVICE(1106,1571,"VT82C416MV") + DEVICE(1106,1595,"VT82C595/97 [Apollo VP2/97]") + DEVICE(1106,3038,"UHCI USB") + DEVICE(1106,3040,"VT82C586B ACPI") + DEVICE(1106,3043,"VT86C100A [Rhine 10/100]") + DEVICE(1106,3044,"OHCI Compliant IEEE 1394 Host Controller") + DEVICE(1106,3050,"VT82C596 Power Management") + DEVICE(1106,3051,"VT82C596 Power Management") + DEVICE(1106,3057,"VT82C686 [Apollo Super ACPI]") + DEVICE(1106,3058,"AC97 Audio Controller") + DEVICE(1106,3059,"AC97 Audio Controller") + DEVICE(1106,3065,"Ethernet Controller") + DEVICE(1106,3068,"AC97 Modem Controller") + DEVICE(1106,3074,"VT8233 PCI to ISA Bridge") + DEVICE(1106,3091,"VT8633 [Apollo Pro266]") + DEVICE(1106,3099,"VT8367 [KT266]") + DEVICE(1106,5030,"VT82C596 ACPI [Apollo PRO]") + DEVICE(1106,6100,"VT85C100A [Rhine II]") + DEVICE(1106,8231,"VT8231 [PCI-to-ISA Bridge]") + DEVICE(1106,8235,"VT8235 Power Management") + DEVICE(1106,8305,"VT8363/8365 [KT133/KM133 AGP]") + DEVICE(1106,8391,"VT8371 [KX133 AGP] ") + DEVICE(1106,8501,"VT8501 [Apollo MVP4 AGP]") + DEVICE(1106,8596,"VT82C596 [Apollo PRO AGP]") + DEVICE(1106,8597,"VT82C597 [Apollo VP3 AGP]") + DEVICE(1106,8598,"VT82C598/694x [Apollo MVP3/Pro133x AGP]") + DEVICE(1106,8601,"VT8601 [Apollo ProMedia AGP]") + DEVICE(1106,8605,"VT8605 [PM133 AGP]") + DEVICE(1106,B091,"VT8633 [Apollo Pro266 AGP]") + DEVICE(1106,B099,"VT8367 [KT266 AGP]") + DEVICE(1106,8691,"VT82C691 [Apollo Pro]") +ENDVENDOR() + +VENDOR(1107,"Stratus Computers") + DEVICE(1107,0576,"VIA VT82C570MV [Apollo] (Wrong vendor ID!)") +ENDVENDOR() + +VENDOR(1108,"Proteon, Inc.") + DEVICE(1108,0100,"p1690plus_AA") + DEVICE(1108,0101,"p1690plus_AB") + DEVICE(1108,0105,"P1690Plus") + DEVICE(1108,0108,"P1690Plus") + DEVICE(1108,0138,"P1690Plus") + DEVICE(1108,0139,"P1690Plus") + DEVICE(1108,013c,"P1690Plus") + DEVICE(1108,013d,"P1690Plus") +ENDVENDOR() + +VENDOR(1109,"Cogent Data Technologies, Inc.") + DEVICE(1109,1400,"EM110TX [EX110TX]") +ENDVENDOR() + +VENDOR(110a,"Siemens Nixdorf AG") + DEVICE(110a,0002,"Pirahna 2-port") + DEVICE(110a,0005,"Tulip controller, power management, switch extender") + DEVICE(110a,4942,"FPGA I-Bus Tracer for MBD") + DEVICE(110a,6120,"SZB6120") +ENDVENDOR() + +VENDOR(110b,"Chromatic Research Inc.") + DEVICE(110b,0001,"Mpact Media Processor") +ENDVENDOR() + +VENDOR(110c,"Mini-Max Technology, Inc.") +ENDVENDOR() + +VENDOR(110d,"Znyx Advanced Systems") +ENDVENDOR() + +VENDOR(110e,"CPU Technology") +ENDVENDOR() + +VENDOR(110f,"Ross Technology") +ENDVENDOR() + +VENDOR(1110,"Powerhouse Systems") + DEVICE(1110,6037,"Firepower Powerized SMP I/O ASIC") + DEVICE(1110,6073,"Firepower Powerized SMP I/O ASIC") +ENDVENDOR() + +VENDOR(1111,"Santa Cruz Operation") +ENDVENDOR() + +VENDOR(1112,"RNS - Div. of Meret Communications Inc") + DEVICE(1112,2200,"FDDI Adapter") + DEVICE(1112,2300,"Fast Ethernet Adapter") + DEVICE(1112,2340,"4 Port Fast Ethernet Adapter") + DEVICE(1112,2400,"ATM Adapter") +ENDVENDOR() + +VENDOR(1113,"Accton Technology Corporation") + DEVICE(1113,1211,"SMC2-1211TX") + DEVICE(1113,1217,"EN-1217 Ethernet Adapter") + DEVICE(1113,5105,"10Mbps Network card") + DEVICE(1113,9211,"EN-1207D Fast Ethernet Adapter") +ENDVENDOR() + +VENDOR(1114,"Atmel Corporation") +ENDVENDOR() + +VENDOR(1115,"3D Labs") +ENDVENDOR() + +VENDOR(1116,"Data Translation") + DEVICE(1116,0022,"DT3001") + DEVICE(1116,0023,"DT3002") + DEVICE(1116,0024,"DT3003") + DEVICE(1116,0025,"DT3004") + DEVICE(1116,0026,"DT3005") + DEVICE(1116,0027,"DT3001-PGL") + DEVICE(1116,0028,"DT3003-PGL") +ENDVENDOR() + +VENDOR(1117,"Datacube, Inc") + DEVICE(1117,9500,"Max-1C SVGA card") + DEVICE(1117,9501,"Max-1C image processing") +ENDVENDOR() + +VENDOR(1118,"Berg Electronics") +ENDVENDOR() + +VENDOR(1119,"ICP Vortex Computersysteme GmbH") + DEVICE(1119,0000,"GDT 6000/6020/6050") + DEVICE(1119,0001,"GDT 6000b/6010") + DEVICE(1119,0002,"GDT 6110/6510") + DEVICE(1119,0003,"GDT 6120/6520") + DEVICE(1119,0004,"GDT 6530") + DEVICE(1119,0005,"GDT 6550") + DEVICE(1119,0006,"GDT 6x17") + DEVICE(1119,0007,"GDT 6x27") + DEVICE(1119,0008,"GDT 6537") + DEVICE(1119,0009,"GDT 5557") + DEVICE(1119,000a,"GDT 6x15") + DEVICE(1119,000b,"GDT 6x25") + DEVICE(1119,000c,"GDT 6535") + DEVICE(1119,000d,"GDT 6555") + DEVICE(1119,0100,"GDT 6117RP/6517RP") + DEVICE(1119,0101,"GDT 6127RP/6527RP") + DEVICE(1119,0102,"GDT 6537RP") + DEVICE(1119,0103,"GDT 6557RP") + DEVICE(1119,0104,"GDT 6111RP/6511RP") + DEVICE(1119,0105,"GDT 6121RP/6521RP") + DEVICE(1119,0110,"GDT 6117RP1/6517RP1") + DEVICE(1119,0111,"GDT 6127RP1/6527RP1") + DEVICE(1119,0112,"GDT 6537RP1") + DEVICE(1119,0113,"GDT 6557RP1") + DEVICE(1119,0114,"GDT 6111RP1/6511RP1") + DEVICE(1119,0115,"GDT 6121RP1/6521RP1") + DEVICE(1119,0118,"GDT 6x18RD") + DEVICE(1119,0119,"GDT 6x28RD") + DEVICE(1119,011a,"GDT 6x38RD") + DEVICE(1119,011b,"GDT 6x58RD") + DEVICE(1119,0120,"GDT 6117RP2/6517RP2") + DEVICE(1119,0121,"GDT 6127RP2/6527RP2") + DEVICE(1119,0122,"GDT 6537RP2") + DEVICE(1119,0123,"GDT 6557RP2") + DEVICE(1119,0124,"GDT 6111RP2/6511RP2") + DEVICE(1119,0125,"GDT 6121RP2/6521RP2") + DEVICE(1119,0168,"GDT 7x18RN") + DEVICE(1119,0169,"GDT 7x28RN") + DEVICE(1119,016a,"GDT 7x38RN") + DEVICE(1119,016b,"GDT 7x58RN") + DEVICE(1119,0210,"GDT 6x19RD") + DEVICE(1119,0211,"GDT 6x29RD") + DEVICE(1119,0260,"GDT 7x19RN") + DEVICE(1119,0261,"GDT 7x29RN") +ENDVENDOR() + +VENDOR(111a,"Efficient Networks, Inc") + DEVICE(111a,0000,"155P-MF1 (FPGA)") + DEVICE(111a,0002,"155P-MF1 (ASIC)") + DEVICE(111a,0003,"ENI-25P ATM") + DEVICE(111a,0005,"SpeedStream (LANAI)") + DEVICE(111a,0007,"SpeedStream ADSL ") +ENDVENDOR() + +VENDOR(111b,"Teledyne Electronic Systems") +ENDVENDOR() + +VENDOR(111c,"Tricord Systems Inc.") + DEVICE(111c,0001,"Powerbis Bridge") +ENDVENDOR() + +VENDOR(111d,"Integrated Device Tech") + DEVICE(111d,0001,"IDT77211 ATM Adapter") +ENDVENDOR() + +VENDOR(111e,"Eldec") +ENDVENDOR() + +VENDOR(111f,"Precision Digital Images") + DEVICE(111f,4a47,"Precision MX Video engine interface") + DEVICE(111f,5243,"Frame capture bus interface") +ENDVENDOR() + +VENDOR(1120,"EMC Corporation") +ENDVENDOR() + +VENDOR(1121,"Zilog") +ENDVENDOR() + +VENDOR(1122,"Multi-tech Systems, Inc.") +ENDVENDOR() + +VENDOR(1123,"Excellent Design, Inc.") +ENDVENDOR() + +VENDOR(1124,"Leutron Vision AG") +ENDVENDOR() + +VENDOR(1125,"Eurocore") +ENDVENDOR() + +VENDOR(1126,"Vigra") +ENDVENDOR() + +VENDOR(1127,"FORE Systems Inc") + DEVICE(1127,0200,"ForeRunner PCA-200 ATM") + DEVICE(1127,0210,"PCA-200PC") + DEVICE(1127,0250,"ATM") + DEVICE(1127,0300,"PCA-200E") + DEVICE(1127,0310,"ATM") + DEVICE(1127,0400,"ForeRunnerHE ATM Adapter") +ENDVENDOR() + +VENDOR(1129,"Firmworks") +ENDVENDOR() + +VENDOR(112a,"Hermes Electronics Company, Ltd.") +ENDVENDOR() + +VENDOR(112b,"Linotype - Hell AG") +ENDVENDOR() + +VENDOR(112c,"Zenith Data Systems") +ENDVENDOR() + +VENDOR(112d,"Ravicad") +ENDVENDOR() + +VENDOR(112e,"Infomedia Microelectronics Inc.") +ENDVENDOR() + +VENDOR(112f,"Imaging Technology Inc") + DEVICE(112f,0000,"MVC IC-PCI") + DEVICE(112f,0001,"MVC IM-PCI Video frame grabber/processor") +ENDVENDOR() + +VENDOR(1130,"Computervision") +ENDVENDOR() + +VENDOR(1131,"Philips Semiconductors") + DEVICE(1131,7145,"SAA7145") + DEVICE(1131,7146,"SAA7146") +ENDVENDOR() + +VENDOR(1132,"Mitel Corp.") +ENDVENDOR() + +VENDOR(1133,"Eicon Technology Corporation") + DEVICE(1133,7901,"EiconCard S90") + DEVICE(1133,7902,"EiconCard S90") + DEVICE(1133,7911,"EiconCard S91") + DEVICE(1133,7912,"EiconCard S91") + DEVICE(1133,7941,"EiconCard S94") + DEVICE(1133,7942,"EiconCard S94") + DEVICE(1133,b921,"EiconCard P92") + DEVICE(1133,b922,"EiconCard P92") + DEVICE(1133,e001,"DIVA 20PRO") + DEVICE(1133,e002,"DIVA 20") + DEVICE(1133,e003,"DIVA 20PRO_U") + DEVICE(1133,e004,"DIVA 20_U") + DEVICE(1133,e010,"DIVA Server BRI-2M") + DEVICE(1133,e014,"DIVA Server PRI-30M") +ENDVENDOR() + +VENDOR(1134,"Mercury Computer Systems") + DEVICE(1134,0001,"Raceway Bridge") +ENDVENDOR() + +VENDOR(1135,"Fuji Xerox Co Ltd") + DEVICE(1135,0001,"Printer controller") +ENDVENDOR() + +VENDOR(1136,"Momentum Data Systems") +ENDVENDOR() + +VENDOR(1137,"Cisco Systems Inc") +ENDVENDOR() + +VENDOR(1138,"Ziatech Corporation") + DEVICE(1138,8905,"8905 [STD 32 Bridge]") +ENDVENDOR() + +VENDOR(1139,"Dynamic Pictures, Inc") + DEVICE(1139,0001,"VGA Compatable 3D Graphics") +ENDVENDOR() + +VENDOR(113a,"FWB Inc") +ENDVENDOR() + +VENDOR(113b,"Network Computing Devices") +ENDVENDOR() + +VENDOR(113c,"Cyclone Microsystems, Inc.") + DEVICE(113c,0000,"PCI-9060 i960 Bridge") + DEVICE(113c,0001,"PCI-SDK [PCI i960 Evaluation Platform]") + DEVICE(113c,0911,"PCI-911") + DEVICE(113c,0912,"PCI-912 [i960CF-based Intelligent I/O Controller]") + DEVICE(113c,0913,"PCI-913") + DEVICE(113c,0914,"PCI-914 [I/O Controller w/ secondary PCI bus]") +ENDVENDOR() + +VENDOR(113d,"Leading Edge Products Inc") +ENDVENDOR() + +VENDOR(113e,"Sanyo Electric Co - Computer Engineering Dept") +ENDVENDOR() + +VENDOR(113f,"Equinox Systems, Inc.") + DEVICE(113f,0808,"SST-64P Adapter") + DEVICE(113f,1010,"SST-128P Adapter") + DEVICE(113f,80c0,"SST-16P Adapter") + DEVICE(113f,80c4,"SST-16P Adapter") + DEVICE(113f,80c8,"SST-16P Adapter") + DEVICE(113f,8888,"SST-4P Adapter") + DEVICE(113f,9090,"SST-8P Adapter") +ENDVENDOR() + +VENDOR(1140,"Intervoice Inc") +ENDVENDOR() + +VENDOR(1141,"Crest Microsystem Inc") +ENDVENDOR() + +VENDOR(1142,"Alliance Semiconductor Corporation") + DEVICE(1142,3210,"AP6410") + DEVICE(1142,6422,"ProVideo 6422") + DEVICE(1142,6424,"ProVideo 6424") + DEVICE(1142,6425,"ProMotion AT25") + DEVICE(1142,643d,"ProMotion AT3D") +ENDVENDOR() + +VENDOR(1143,"NetPower, Inc") +ENDVENDOR() + +VENDOR(1144,"Cincinnati Milacron") + DEVICE(1144,0001,"Noservo controller") +ENDVENDOR() + +VENDOR(1145,"Workbit Corporation") +ENDVENDOR() + +VENDOR(1146,"Force Computers") +ENDVENDOR() + +VENDOR(1147,"Interface Corp") +ENDVENDOR() + +VENDOR(1148,"Syskonnect (Schneider & Koch)") + DEVICE(1148,4000,"FDDI Adapter") + DEVICE(1148,4200,"Token ring adaptor") + DEVICE(1148,4300,"Gigabit Ethernet") +ENDVENDOR() + +VENDOR(1149,"Win System Corporation") +ENDVENDOR() + +VENDOR(114a,"VMIC") + DEVICE(114a,7587,"VMIVME-7587") +ENDVENDOR() + +VENDOR(114b,"Canopus Co., Ltd") +ENDVENDOR() + +VENDOR(114c,"Annabooks") +ENDVENDOR() + +VENDOR(114d,"IC Corporation") +ENDVENDOR() + +VENDOR(114e,"Nikon Systems Inc") +ENDVENDOR() + +VENDOR(114f,"Digi International") + DEVICE(114f,0002,"AccelePort EPC") + DEVICE(114f,0003,"RightSwitch SE-6") + DEVICE(114f,0004,"AccelePort Xem") + DEVICE(114f,0005,"AccelePort Xr") + DEVICE(114f,0006,"AccelePort Xr,C/X") + DEVICE(114f,0009,"AccelePort Xr/J") + DEVICE(114f,000a,"AccelePort EPC/J") + DEVICE(114f,000c,"DataFirePRIme T1 (1-port)") + DEVICE(114f,000d,"SyncPort 2-Port (x.25/FR)") + DEVICE(114f,0011,"AccelePort 8r EIA-232 (IBM)") + DEVICE(114f,0012,"AccelePort 8r EIA-422") + DEVICE(114f,0013,"AccelePort Xr") + DEVICE(114f,0014,"AccelePort 8r EIA-422") + DEVICE(114f,0015,"AccelePort Xem") + DEVICE(114f,0016,"AccelePort EPC/X") + DEVICE(114f,0017,"AccelePort C/X") + DEVICE(114f,001a,"DataFirePRIme E1 (1-port)") + DEVICE(114f,001b,"AccelePort C/X (IBM)") + DEVICE(114f,001d,"DataFire RAS T1/E1/PRI") + DEVICE(114f,0023,"AccelePort RAS") + DEVICE(114f,0024,"DataFire RAS B4 ST/U") + DEVICE(114f,0026,"AccelePort 4r 920") + DEVICE(114f,0027,"AccelePort Xr 920") + DEVICE(114f,0034,"AccelePort 2r 920") + DEVICE(114f,0035,"DataFire DSP T1/E1/PRI cPCI") + DEVICE(114f,6001,"Avanstar") +ENDVENDOR() + +VENDOR(1150,"Thinking Machines Corp") +ENDVENDOR() + +VENDOR(1151,"JAE Electronics Inc.") +ENDVENDOR() + +VENDOR(1152,"Megatek") +ENDVENDOR() + +VENDOR(1153,"Land Win Electronic Corp") +ENDVENDOR() + +VENDOR(1154,"Melco Inc") +ENDVENDOR() + +VENDOR(1155,"Pine Technology Ltd") +ENDVENDOR() + +VENDOR(1156,"Periscope Engineering") +ENDVENDOR() + +VENDOR(1157,"Avsys Corporation") +ENDVENDOR() + +VENDOR(1158,"Voarx R & D Inc") + DEVICE(1158,3011,"Tokenet/vg 1001/10m anylan") + DEVICE(1158,9050,"Lanfleet/Truevalue") + DEVICE(1158,9051,"Lanfleet/Truevalue") +ENDVENDOR() + +VENDOR(1159,"Mutech Corp") + DEVICE(1159,0001,"MV-1000") +ENDVENDOR() + +VENDOR(115a,"Harlequin Ltd") +ENDVENDOR() + +VENDOR(115b,"Parallax Graphics") +ENDVENDOR() + +VENDOR(115c,"Photron Ltd.") +ENDVENDOR() + +VENDOR(115d,"Xircom") + DEVICE(115d,0003,"Cardbus Ethernet 10/100") + DEVICE(115d,0005,"Cardbus Ethernet 10/100") + DEVICE(115d,0007,"Cardbus Ethernet 10/100") + DEVICE(115d,000b,"Cardbus Ethernet 10/100") + DEVICE(115d,000f,"Cardbus Ethernet 10/100") + DEVICE(115d,0101,"Cardbus 56k modem") + DEVICE(115d,0103,"Cardbus Ethernet + 56k Modem") +ENDVENDOR() + +VENDOR(115e,"Peer Protocols Inc") +ENDVENDOR() + +VENDOR(115f,"Maxtor Corporation") +ENDVENDOR() + +VENDOR(1160,"Megasoft Inc") +ENDVENDOR() + +VENDOR(1161,"PFU Limited") +ENDVENDOR() + +VENDOR(1162,"OA Laboratory Co Ltd") +ENDVENDOR() + +VENDOR(1163,"Rendition") + DEVICE(1163,0001,"Verite 1000") + DEVICE(1163,2000,"Verite V2000/V2100/V2200") +ENDVENDOR() + +VENDOR(1164,"Advanced Peripherals Technologies") +ENDVENDOR() + +VENDOR(1165,"Imagraph Corporation") + DEVICE(1165,0001,"Motion TPEG Recorder/Player with audio") +ENDVENDOR() + +VENDOR(1166,"ServerWorks") + DEVICE(1166,0007,"CNB20-LE CPU to PCI Bridge") + DEVICE(1166,0008,"CNB20HE") + DEVICE(1166,0009,"CNB20LE") + DEVICE(1166,0010,"CIOB30") + DEVICE(1166,0011,"CMIC-HE") + DEVICE(1166,0200,"OSB4") + DEVICE(1166,0201,"CSB5") +ENDVENDOR() + +VENDOR(1167,"Mutoh Industries Inc") +ENDVENDOR() + +VENDOR(1168,"Thine Electronics Inc") +ENDVENDOR() + +VENDOR(1169,"Centre for Development of Advanced Computing") +ENDVENDOR() + +VENDOR(116a,"Polaris Communications") + DEVICE(116a,6100,"Bus/Tag Channel") + DEVICE(116a,6800,"Escon Channel") + DEVICE(116a,7100,"Bus/Tag Channel") + DEVICE(116a,7800,"Escon Channel") +ENDVENDOR() + +VENDOR(116b,"Connectware Inc") +ENDVENDOR() + +VENDOR(116c,"Intelligent Resources Integrated Systems") +ENDVENDOR() + +VENDOR(116d,"Martin-Marietta") +ENDVENDOR() + +VENDOR(116e,"Electronics for Imaging") +ENDVENDOR() + +VENDOR(116f,"Workstation Technology") +ENDVENDOR() + +VENDOR(1170,"Inventec Corporation") +ENDVENDOR() + +VENDOR(1171,"Loughborough Sound Images Plc") +ENDVENDOR() + +VENDOR(1172,"Altera Corporation") +ENDVENDOR() + +VENDOR(1173,"Adobe Systems, Inc") +ENDVENDOR() + +VENDOR(1174,"Bridgeport Machines") +ENDVENDOR() + +VENDOR(1175,"Mitron Computer Inc.") +ENDVENDOR() + +VENDOR(1176,"SBE Incorporated") +ENDVENDOR() + +VENDOR(1177,"Silicon Engineering") +ENDVENDOR() + +VENDOR(1178,"Alfa, Inc.") + DEVICE(1178,afa1,"Fast Ethernet Adapter") +ENDVENDOR() + +VENDOR(1179,"Toshiba America Info Systems") + DEVICE(1179,0404,"DVD Decoder card") + DEVICE(1179,0406,"Tecra Video Capture device") + DEVICE(1179,0407,"DVD Decoder card (Version 2)") + DEVICE(1179,0601,"601") + DEVICE(1179,0603,"ToPIC95 PCI to CardBus Bridge for Notebooks") + DEVICE(1179,060a,"ToPIC95") + DEVICE(1179,060f,"ToPIC97") + DEVICE(1179,0617,"ToPIC95 PCI to Cardbus Bridge with ZV Support") + DEVICE(1179,0618,"CPU to PCI and PCI to ISA bridge") + DEVICE(1179,0701,"FIR Port") + DEVICE(1179,0d01,"FIR Port Type-DO") +ENDVENDOR() + +VENDOR(117a,"A-Trend Technology") +ENDVENDOR() + +VENDOR(117b,"L G Electronics, Inc.") +ENDVENDOR() + +VENDOR(117c,"Atto Technology") +ENDVENDOR() + +VENDOR(117d,"Becton & Dickinson") +ENDVENDOR() + +VENDOR(117e,"T/R Systems") +ENDVENDOR() + +VENDOR(117f,"Integrated Circuit Systems") +ENDVENDOR() + +VENDOR(1180,"Ricoh Co Ltd") + DEVICE(1180,0465,"RL5c465") + DEVICE(1180,0466,"RL5c466") + DEVICE(1180,0475,"RL5c475") + DEVICE(1180,0476,"RL5c476 II") + DEVICE(1180,0477,"RL5c477") + DEVICE(1180,0478,"RL5c478") +ENDVENDOR() + +VENDOR(1181,"Telmatics International") +ENDVENDOR() + +VENDOR(1183,"Fujikura Ltd") +ENDVENDOR() + +VENDOR(1184,"Forks Inc") +ENDVENDOR() + +VENDOR(1185,"Dataworld International Ltd") +ENDVENDOR() + +VENDOR(1186,"D-Link System Inc") + DEVICE(1186,0100,"DC21041") +ENDVENDOR() + +VENDOR(1187,"Advanced Technology Laboratories, Inc.") +ENDVENDOR() + +VENDOR(1188,"Shima Seiki Manufacturing Ltd.") +ENDVENDOR() + +VENDOR(1189,"Matsushita Electronics Co Ltd") +ENDVENDOR() + +VENDOR(118a,"Hilevel Technology") +ENDVENDOR() + +VENDOR(118b,"Hypertec Pty Limited") +ENDVENDOR() + +VENDOR(118c,"Corollary, Inc") + DEVICE(118c,0014,"PCIB [C-bus II to PCI bus host bridge chip]") +ENDVENDOR() + +VENDOR(118d,"BitFlow Inc") + DEVICE(118d,0001,"Raptor-PCI framegrabber") + DEVICE(118d,0012,"Model 12 Road Runner Frame Grabber") + DEVICE(118d,0014,"Model 14 Road Runner Frame Grabber") + DEVICE(118d,0024,"Model 24 Road Runner Frame Grabber") + DEVICE(118d,0044,"Model 44 Road Runner Frame Grabber") + DEVICE(118d,0112,"Model 12 Road Runner Frame Grabber") + DEVICE(118d,0114,"Model 14 Road Runner Frame Grabber") + DEVICE(118d,0124,"Model 24 Road Runner Frame Grabber") + DEVICE(118d,0144,"Model 44 Road Runner Frame Grabber") + DEVICE(118d,0212,"Model 12 Road Runner Frame Grabber") + DEVICE(118d,0214,"Model 14 Road Runner Frame Grabber") + DEVICE(118d,0224,"Model 24 Road Runner Frame Grabber") + DEVICE(118d,0244,"Model 44 Road Runner Frame Grabber") + DEVICE(118d,0312,"Model 12 Road Runner Frame Grabber") + DEVICE(118d,0314,"Model 14 Road Runner Frame Grabber") + DEVICE(118d,0324,"Model 24 Road Runner Frame Grabber") + DEVICE(118d,0344,"Model 44 Road Runner Frame Grabber") +ENDVENDOR() + +VENDOR(118e,"Hermstedt GmbH") +ENDVENDOR() + +VENDOR(118f,"Green Logic") +ENDVENDOR() + +VENDOR(1190,"Tripace") + DEVICE(1190,c731,"TP-910/920/940 PCI Ultra(Wide) SCSI Adapter") +ENDVENDOR() + +VENDOR(1191,"Artop Electronic Corp") + DEVICE(1191,0003,"SCSI Cache Host Adapter") + DEVICE(1191,0004,"ATP8400") + DEVICE(1191,0005,"ATP850UF") + DEVICE(1191,0006,"ATP860 NO-BIOS") + DEVICE(1191,0007,"ATP860") + DEVICE(1191,8002,"AEC6710 SCSI-2 Host Adapter") + DEVICE(1191,8010,"AEC6712UW SCSI") + DEVICE(1191,8020,"AEC6712U SCSI") + DEVICE(1191,8030,"AEC6712S SCSI") + DEVICE(1191,8040,"AEC6712D SCSI") + DEVICE(1191,8050,"AEC6712SUW SCSI") +ENDVENDOR() + +VENDOR(1192,"Densan Company Ltd") +ENDVENDOR() + +VENDOR(1193,"Zeitnet Inc.") + DEVICE(1193,0001,"1221") + DEVICE(1193,0002,"1225") +ENDVENDOR() + +VENDOR(1194,"Toucan Technology") +ENDVENDOR() + +VENDOR(1195,"Ratoc System Inc") +ENDVENDOR() + +VENDOR(1196,"Hytec Electronics Ltd") +ENDVENDOR() + +VENDOR(1197,"Gage Applied Sciences, Inc.") +ENDVENDOR() + +VENDOR(1198,"Lambda Systems Inc") +ENDVENDOR() + +VENDOR(1199,"Attachmate Corporation") +ENDVENDOR() + +VENDOR(119a,"Mind Share, Inc.") +ENDVENDOR() + +VENDOR(119b,"Omega Micro Inc.") + DEVICE(119b,1221,"82C092G") +ENDVENDOR() + +VENDOR(119c,"Information Technology Inst.") +ENDVENDOR() + +VENDOR(119d,"Bug, Inc. Sapporo Japan") +ENDVENDOR() + +VENDOR(119e,"Fujitsu Microelectronics Europe GMBH") + DEVICE(119e,0001,"FireStream 155") + DEVICE(119e,0003,"FireStream 50") +ENDVENDOR() + +VENDOR(119f,"Bull HN Information Systems") +ENDVENDOR() + +VENDOR(11a0,"Convex Computer Corporation") +ENDVENDOR() + +VENDOR(11a1,"Hamamatsu Photonics K.K.") +ENDVENDOR() + +VENDOR(11a2,"Sierra Research and Technology") +ENDVENDOR() + +VENDOR(11a3,"Deuretzbacher GmbH & Co. Eng. KG") +ENDVENDOR() + +VENDOR(11a4,"Barco Graphics NV") +ENDVENDOR() + +VENDOR(11a5,"Microunity Systems Eng. Inc") +ENDVENDOR() + +VENDOR(11a6,"Pure Data Ltd.") +ENDVENDOR() + +VENDOR(11a7,"Power Computing Corp.") +ENDVENDOR() + +VENDOR(11a8,"Systech Corp.") +ENDVENDOR() + +VENDOR(11a9,"InnoSys Inc.") + DEVICE(11a9,4240,"AMCC S933Q Intelligent Serial Card") +ENDVENDOR() + +VENDOR(11aa,"Actel") +ENDVENDOR() + +VENDOR(11ab,"Galileo Technology Ltd.") + DEVICE(11ab,0146,"GT-64010") + DEVICE(11ab,4801,"GT-48001") + DEVICE(11ab,f003,"GT-64010 Primary Image Piranha Image Generator") +ENDVENDOR() + +VENDOR(11ac,"Canon Information Systems Research Aust.") +ENDVENDOR() + +VENDOR(11ad,"Lite-On Communications Inc") + DEVICE(11ad,0002,"LNE100TX") + DEVICE(11ad,c115,"LNE100TX [Linksys EtherFast 10/100]") +ENDVENDOR() + +VENDOR(11ae,"Aztech System Ltd") +ENDVENDOR() + +VENDOR(11af,"Avid Technology Inc.") +ENDVENDOR() + +VENDOR(11b0,"V3 Semiconductor Inc.") + DEVICE(11b0,0002,"V300PSC") + DEVICE(11b0,0292,"V292PBC [Am29030/40 Bridge]") + DEVICE(11b0,0960,"V96xPBC") + DEVICE(11b0,c960,"V96DPC") +ENDVENDOR() + +VENDOR(11b1,"Apricot Computers") +ENDVENDOR() + +VENDOR(11b2,"Eastman Kodak") +ENDVENDOR() + +VENDOR(11b3,"Barr Systems Inc.") +ENDVENDOR() + +VENDOR(11b4,"Leitch Technology International") +ENDVENDOR() + +VENDOR(11b5,"Radstone Technology Plc") +ENDVENDOR() + +VENDOR(11b6,"United Video Corp") +ENDVENDOR() + +VENDOR(11b7,"Motorola") +ENDVENDOR() + +VENDOR(11b8,"XPoint Technologies, Inc") + DEVICE(11b8,0001,"Quad PeerMaster") +ENDVENDOR() + +VENDOR(11b9,"Pathlight Technology Inc.") + DEVICE(11b9,c0ed,"SSA Controller") +ENDVENDOR() + +VENDOR(11ba,"Videotron Corp") +ENDVENDOR() + +VENDOR(11bb,"Pyramid Technology") +ENDVENDOR() + +VENDOR(11bc,"Network Peripherals Inc") + DEVICE(11bc,0001,"NP-PCI") +ENDVENDOR() + +VENDOR(11bd,"Pinnacle Systems Inc.") +ENDVENDOR() + +VENDOR(11be,"International Microcircuits Inc") +ENDVENDOR() + +VENDOR(11bf,"Astrodesign, Inc.") +ENDVENDOR() + +VENDOR(11c0,"Hewlett Packard") +ENDVENDOR() + +VENDOR(11c1,"Lucent Microelectronics") + DEVICE(11c1,0440,"56k WinModem") + DEVICE(11c1,0441,"56k WinModem") + DEVICE(11c1,0442,"56k WinModem") + DEVICE(11c1,0443,"LT WinModem") + DEVICE(11c1,0444,"LT WinModem") + DEVICE(11c1,0445,"LT WinModem") + DEVICE(11c1,0446,"LT WinModem") + DEVICE(11c1,0447,"LT WinModem") + DEVICE(11c1,0448,"WinModem 56k") + DEVICE(11c1,0449,"WinModem 56k") + DEVICE(11c1,044a,"F-1156IV WinModem (V90, 56KFlex)") + DEVICE(11c1,044b,"LT WinModem") + DEVICE(11c1,044c,"LT WinModem") + DEVICE(11c1,044d,"LT WinModem") + DEVICE(11c1,044e,"LT WinModem") + DEVICE(11c1,0450,"LT WinModem") + DEVICE(11c1,0451,"LT WinModem") + DEVICE(11c1,0452,"LT WinModem") + DEVICE(11c1,0453,"LT WinModem") + DEVICE(11c1,0454,"LT WinModem") + DEVICE(11c1,0455,"LT WinModem") + DEVICE(11c1,0456,"LT WinModem") + DEVICE(11c1,0457,"LT WinModem") + DEVICE(11c1,0458,"LT WinModem") + DEVICE(11c1,0459,"LT WinModem") + DEVICE(11c1,045a,"LT WinModem") + DEVICE(11c1,0480,"Venus Modem (V90, 56KFlex)") +ENDVENDOR() + +VENDOR(11c2,"Sand Microelectronics") +ENDVENDOR() + +VENDOR(11c3,"NEC Corp") +ENDVENDOR() + +VENDOR(11c4,"Document Technologies, Inc") +ENDVENDOR() + +VENDOR(11c5,"Shiva Corporation") +ENDVENDOR() + +VENDOR(11c6,"Dainippon Screen Mfg. Co. Ltd") +ENDVENDOR() + +VENDOR(11c7,"D.C.M. Data Systems") +ENDVENDOR() + +VENDOR(11c8,"Dolphin Interconnect Solutions AS") + DEVICE(11c8,0658,"PSB32 SCI-Adapter D31x") + DEVICE(11c8,d665,"PSB64 SCI-Adapter D32x") + DEVICE(11c8,d667,"PSB66 SCI-Adapter D33x") +ENDVENDOR() + +VENDOR(11c9,"Magma") + DEVICE(11c9,0010,"16-line serial port w/- DMA") + DEVICE(11c9,0011,"4-line serial port w/- DMA") +ENDVENDOR() + +VENDOR(11ca,"LSI Systems, Inc") +ENDVENDOR() + +VENDOR(11cb,"Specialix Research Ltd.") + DEVICE(11cb,2000,"PCI_9050") + DEVICE(11cb,4000,"SUPI_1") + DEVICE(11cb,8000,"T225") +ENDVENDOR() + +VENDOR(11cc,"Michels & Kleberhoff Computer GmbH") +ENDVENDOR() + +VENDOR(11cd,"HAL Computer Systems, Inc.") +ENDVENDOR() + +VENDOR(11ce,"Netaccess") +ENDVENDOR() + +VENDOR(11cf,"Pioneer Electronic Corporation") +ENDVENDOR() + +VENDOR(11d0,"Lockheed Martin Federal Systems-Manassas") +ENDVENDOR() + +VENDOR(11d1,"Auravision") + DEVICE(11d1,01f7,"VxP524") +ENDVENDOR() + +VENDOR(11d2,"Intercom Inc.") +ENDVENDOR() + +VENDOR(11d3,"Trancell Systems Inc") +ENDVENDOR() + +VENDOR(11d4,"Analog Devices") +ENDVENDOR() + +VENDOR(11d5,"Ikon Corporation") + DEVICE(11d5,0115,"10115") + DEVICE(11d5,0117,"10117") +ENDVENDOR() + +VENDOR(11d6,"Tekelec Telecom") +ENDVENDOR() + +VENDOR(11d7,"Trenton Technology, Inc.") +ENDVENDOR() + +VENDOR(11d8,"Image Technologies Development") +ENDVENDOR() + +VENDOR(11d9,"TEC Corporation") +ENDVENDOR() + +VENDOR(11da,"Novell") +ENDVENDOR() + +VENDOR(11db,"Sega Enterprises Ltd") +ENDVENDOR() + +VENDOR(11dc,"Questra Corporation") +ENDVENDOR() + +VENDOR(11dd,"Crosfield Electronics Limited") +ENDVENDOR() + +VENDOR(11de,"Zoran Corporation") + DEVICE(11de,6057,"ZR36057PQC Video cutting chipset") + DEVICE(11de,6120,"ZR36120") +ENDVENDOR() + +VENDOR(11df,"New Wave PDG") +ENDVENDOR() + +VENDOR(11e0,"Cray Communications A/S") +ENDVENDOR() + +VENDOR(11e1,"GEC Plessey Semi Inc.") +ENDVENDOR() + +VENDOR(11e2,"Samsung Information Systems America") +ENDVENDOR() + +VENDOR(11e3,"Quicklogic Corporation") +ENDVENDOR() + +VENDOR(11e4,"Second Wave Inc") +ENDVENDOR() + +VENDOR(11e5,"IIX Consulting") +ENDVENDOR() + +VENDOR(11e6,"Mitsui-Zosen System Research") +ENDVENDOR() + +VENDOR(11e7,"Toshiba America, Elec. Company") +ENDVENDOR() + +VENDOR(11e8,"Digital Processing Systems Inc.") +ENDVENDOR() + +VENDOR(11e9,"Highwater Designs Ltd.") +ENDVENDOR() + +VENDOR(11ea,"Elsag Bailey") +ENDVENDOR() + +VENDOR(11eb,"Formation Inc.") +ENDVENDOR() + +VENDOR(11ec,"Coreco Inc") +ENDVENDOR() + +VENDOR(11ed,"Mediamatics") +ENDVENDOR() + +VENDOR(11ee,"Dome Imaging Systems Inc") +ENDVENDOR() + +VENDOR(11ef,"Nicolet Technologies B.V.") +ENDVENDOR() + +VENDOR(11f0,"Compu-Shack") + DEVICE(11f0,4231,"FDDI") + DEVICE(11f0,4232,"FASTline UTP Quattro") + DEVICE(11f0,4233,"FASTline FO") + DEVICE(11f0,4234,"FASTline UTP") + DEVICE(11f0,4235,"FASTline-II UTP") + DEVICE(11f0,4236,"FASTline-II FO") + DEVICE(11f0,4731,"GIGAline") +ENDVENDOR() + +VENDOR(11f1,"Symbios Logic Inc") +ENDVENDOR() + +VENDOR(11f2,"Picture Tel Japan K.K.") +ENDVENDOR() + +VENDOR(11f3,"Keithley Metrabyte") +ENDVENDOR() + +VENDOR(11f4,"Kinetic Systems Corporation") + DEVICE(11f4,2915,"CAMAC controller") +ENDVENDOR() + +VENDOR(11f5,"Computing Devices International") +ENDVENDOR() + +VENDOR(11f6,"Compex") + DEVICE(11f6,0112,"ENet100VG4") + DEVICE(11f6,1401,"ReadyLink 2000") + DEVICE(11f6,2011,"RL100-ATX 10/100") + DEVICE(11f6,2201,"ReadyLink 100TX (Winbond W89C840)") + DEVICE(11f6,9881,"RL100TX") +ENDVENDOR() + +VENDOR(11f7,"Scientific Atlanta") +ENDVENDOR() + +VENDOR(11f8,"PMC-Sierra Inc.") + DEVICE(11f8,7375,"PM7375 [LASAR-155 ATM SAR]") +ENDVENDOR() + +VENDOR(11f9,"I-Cube Inc") +ENDVENDOR() + +VENDOR(11fa,"Kasan Electronics Company, Ltd.") +ENDVENDOR() + +VENDOR(11fb,"Datel Inc") +ENDVENDOR() + +VENDOR(11fc,"Silicon Magic") +ENDVENDOR() + +VENDOR(11fd,"High Street Consultants") +ENDVENDOR() + +VENDOR(11fe,"Comtrol Corporation") + DEVICE(11fe,0001,"RocketPort 8 Oct") + DEVICE(11fe,0002,"RocketPort 8 Intf") + DEVICE(11fe,0003,"RocketPort 16 Intf") + DEVICE(11fe,0004,"RocketPort 32 Intf") + DEVICE(11fe,0005,"RocketPort Octacable") + DEVICE(11fe,0006,"RocketPort 8J") + DEVICE(11fe,0008,"RocketPort 8-port") + DEVICE(11fe,0009,"RocketPort 16-port") + DEVICE(11fe,000a,"RocketPort Plus Quadcable") + DEVICE(11fe,000b,"RocketPort Plus Octacable") + DEVICE(11fe,000c,"RocketPort 8-port Modem") +ENDVENDOR() + +VENDOR(11ff,"Scion Corporation") +ENDVENDOR() + +VENDOR(1200,"CSS Corporation") +ENDVENDOR() + +VENDOR(1201,"Vista Controls Corp") +ENDVENDOR() + +VENDOR(1202,"Network General Corp.") +ENDVENDOR() + +VENDOR(1203,"Bayer Corporation, Agfa Division") +ENDVENDOR() + +VENDOR(1204,"Lattice Semiconductor Corporation") +ENDVENDOR() + +VENDOR(1205,"Array Corporation") +ENDVENDOR() + +VENDOR(1206,"Amdahl Corporation") +ENDVENDOR() + +VENDOR(1208,"Parsytec GmbH") + DEVICE(1208,4853,"HS-Link Device") +ENDVENDOR() + +VENDOR(1209,"SCI Systems Inc") +ENDVENDOR() + +VENDOR(120a,"Synaptel") +ENDVENDOR() + +VENDOR(120b,"Adaptive Solutions") +ENDVENDOR() + +VENDOR(120c,"Technical Corp.") +ENDVENDOR() + +VENDOR(120d,"Compression Labs, Inc.") +ENDVENDOR() + +VENDOR(120e,"Cyclades Corporation") + DEVICE(120e,0100,"Cyclom_Y below first megabyte") + DEVICE(120e,0101,"Cyclom_Y above first megabyte") + DEVICE(120e,0102,"Cyclom_4Y below first megabyte") + DEVICE(120e,0103,"Cyclom_4Y above first megabyte") + DEVICE(120e,0104,"Cyclom_8Y below first megabyte") + DEVICE(120e,0105,"Cyclom_8Y above first megabyte") + DEVICE(120e,0200,"Cyclom_Z below first megabyte") + DEVICE(120e,0201,"Cyclom_Z above first megabyte") + DEVICE(120e,0300,"PC300 RX 2") + DEVICE(120e,0301,"PC300 RX 1") + DEVICE(120e,0310,"PC300 TE 2") + DEVICE(120e,0311,"PC300 TE 1") +ENDVENDOR() + +VENDOR(120f,"Essential Communications") + DEVICE(120f,0001,"Roadrunner serial HIPPI") +ENDVENDOR() + +VENDOR(1210,"Hyperparallel Technologies") +ENDVENDOR() + +VENDOR(1211,"Braintech Inc") +ENDVENDOR() + +VENDOR(1212,"Kingston Technology Corp.") +ENDVENDOR() + +VENDOR(1213,"Applied Intelligent Systems, Inc.") +ENDVENDOR() + +VENDOR(1214,"Performance Technologies, Inc.") +ENDVENDOR() + +VENDOR(1215,"Interware Co., Ltd") +ENDVENDOR() + +VENDOR(1216,"Purup Prepress A/S") +ENDVENDOR() + +VENDOR(1217,"O2 Micro, Inc.") + DEVICE(1217,6729,"6729") + DEVICE(1217,673a,"6730") + DEVICE(1217,6832,"6832") + DEVICE(1217,6836,"6836") + DEVICE(1217,6872,"OZ6812 Cardbus Controller") + DEVICE(1217,6933,"OZ6933 Cardbus Controller") +ENDVENDOR() + +VENDOR(1218,"Hybricon Corp.") +ENDVENDOR() + +VENDOR(1219,"First Virtual Corporation") +ENDVENDOR() + +VENDOR(121a,"3Dfx Interactive, Inc.") + DEVICE(121a,0001,"Voodoo") + DEVICE(121a,0002,"Voodoo 2") + DEVICE(121a,0003,"Voodoo Banshee") + DEVICE(121a,0004,"Voodoo Banshee [Velocity 100]") + DEVICE(121a,0005,"Voodoo 3") + DEVICE(121a,0009,"Voodoo 4") +ENDVENDOR() + +VENDOR(121b,"Advanced Telecommunications Modules") +ENDVENDOR() + +VENDOR(121c,"Nippon Texaco., Ltd") +ENDVENDOR() + +VENDOR(121d,"Lippert Automationstechnik GmbH") +ENDVENDOR() + +VENDOR(121e,"CSPI") +ENDVENDOR() + +VENDOR(121f,"Arcus Technology, Inc.") +ENDVENDOR() + +VENDOR(1220,"Ariel Corporation") + DEVICE(1220,1220,"AMCC 5933 TMS320C80 DSP/Imaging board") +ENDVENDOR() + +VENDOR(1221,"Contec Co., Ltd") +ENDVENDOR() + +VENDOR(1222,"Ancor Communications, Inc.") +ENDVENDOR() + +VENDOR(1223,"Artesyn Communication Products") + DEVICE(1223,0003,"PM/Link") + DEVICE(1223,0004,"PM/T1") + DEVICE(1223,0005,"PM/E1") + DEVICE(1223,0008,"PM/SLS") + DEVICE(1223,0009,"BajaSpan Resource Target") + DEVICE(1223,000a,"BajaSpan Section 0") + DEVICE(1223,000b,"BajaSpan Section 1") + DEVICE(1223,000c,"BajaSpan Section 2") + DEVICE(1223,000d,"BajaSpan Section 3") + DEVICE(1223,000e,"PM/PPC") +ENDVENDOR() + +VENDOR(1224,"Interactive Images") +ENDVENDOR() + +VENDOR(1225,"Power I/O, Inc.") +ENDVENDOR() + +VENDOR(1227,"Tech-Source") +ENDVENDOR() + +VENDOR(1228,"Norsk Elektro Optikk A/S") +ENDVENDOR() + +VENDOR(1229,"Data Kinesis Inc.") +ENDVENDOR() + +VENDOR(122a,"Integrated Telecom") +ENDVENDOR() + +VENDOR(122b,"LG Industrial Systems Co., Ltd") +ENDVENDOR() + +VENDOR(122c,"Sican GmbH") +ENDVENDOR() + +VENDOR(122d,"Aztech System Ltd") + DEVICE(122d,1206,"368DSP") + DEVICE(122d,50dc,"3328 Audio") + DEVICE(122d,80da,"3328 Audio") +ENDVENDOR() + +VENDOR(122e,"Xyratex") +ENDVENDOR() + +VENDOR(122f,"Andrew Corporation") +ENDVENDOR() + +VENDOR(1230,"Fishcamp Engineering") +ENDVENDOR() + +VENDOR(1231,"Woodward McCoach, Inc.") +ENDVENDOR() + +VENDOR(1232,"GPT Limited") +ENDVENDOR() + +VENDOR(1233,"Bus-Tech, Inc.") +ENDVENDOR() + +VENDOR(1234,"Technical Corp.") +ENDVENDOR() + +VENDOR(1235,"Risq Modular Systems, Inc.") +ENDVENDOR() + +VENDOR(1236,"Sigma Designs Corporation") + DEVICE(1236,0000,"RealMagic64/GX") + DEVICE(1236,6401,"REALmagic 64/GX (SD 6425)") +ENDVENDOR() + +VENDOR(1237,"Alta Technology Corporation") +ENDVENDOR() + +VENDOR(1238,"Adtran") +ENDVENDOR() + +VENDOR(1239,"3DO Company") +ENDVENDOR() + +VENDOR(123a,"Visicom Laboratories, Inc.") +ENDVENDOR() + +VENDOR(123b,"Seeq Technology, Inc.") +ENDVENDOR() + +VENDOR(123c,"Century Systems, Inc.") +ENDVENDOR() + +VENDOR(123d,"Engineering Design Team, Inc.") + DEVICE(123d,0000,"EasyConnect 8/32") + DEVICE(123d,0002,"EasyConnect 8/64") + DEVICE(123d,0003,"EasyIO") +ENDVENDOR() + +VENDOR(123e,"Simutech, Inc.") +ENDVENDOR() + +VENDOR(123f,"C-Cube Microsystems") + DEVICE(123f,00e4,"MPEG") + DEVICE(123f,8120,"E4?") + DEVICE(123f,8888,"Cinemaster C 3.0 DVD Decoder") +ENDVENDOR() + +VENDOR(1240,"Marathon Technologies Corp.") +ENDVENDOR() + +VENDOR(1241,"DSC Communications") +ENDVENDOR() + +VENDOR(1242,"Jaycor Networks, Inc.") + DEVICE(1242,4643,"FCI-1063 Fibre Channel Adapter") +ENDVENDOR() + +VENDOR(1243,"Delphax") +ENDVENDOR() + +VENDOR(1244,"AVM Audiovisuelles MKTG & Computer System GmbH") + DEVICE(1244,0700,"B1 ISDN") + DEVICE(1244,0a00,"A1 ISDN [Fritz]") +ENDVENDOR() + +VENDOR(1245,"A.P.D., S.A.") +ENDVENDOR() + +VENDOR(1246,"Dipix Technologies, Inc.") +ENDVENDOR() + +VENDOR(1247,"Xylon Research, Inc.") +ENDVENDOR() + +VENDOR(1248,"Central Data Corporation") +ENDVENDOR() + +VENDOR(1249,"Samsung Electronics Co., Ltd.") +ENDVENDOR() + +VENDOR(124a,"AEG Electrocom GmbH") +ENDVENDOR() + +VENDOR(124b,"SBS/Greenspring Modular I/O") +ENDVENDOR() + +VENDOR(124c,"Solitron Technologies, Inc.") +ENDVENDOR() + +VENDOR(124d,"Stallion Technologies, Inc.") + DEVICE(124d,0000,"EasyConnection 8/32") + DEVICE(124d,0002,"EasyConnection 8/64") + DEVICE(124d,0003,"EasyIO") +ENDVENDOR() + +VENDOR(124e,"Cylink") +ENDVENDOR() + +VENDOR(124f,"Infotrend Technology, Inc.") + DEVICE(124f,0041,"IFT-2000 Series RAID Controller") +ENDVENDOR() + +VENDOR(1250,"Hitachi Microcomputer System Ltd") +ENDVENDOR() + +VENDOR(1251,"VLSI Solutions Oy") +ENDVENDOR() + +VENDOR(1253,"Guzik Technical Enterprises") +ENDVENDOR() + +VENDOR(1254,"Linear Systems Ltd.") +ENDVENDOR() + +VENDOR(1255,"Optibase Ltd") + DEVICE(1255,1110,"MPEG Forge") + DEVICE(1255,1210,"MPEG Fusion") + DEVICE(1255,2110,"VideoPlex") + DEVICE(1255,2120,"VideoPlex CC") + DEVICE(1255,2130,"VideoQuest") +ENDVENDOR() + +VENDOR(1256,"Perceptive Solutions, Inc.") +ENDVENDOR() + +VENDOR(1257,"Vertex Networks, Inc.") +ENDVENDOR() + +VENDOR(1258,"Gilbarco, Inc.") +ENDVENDOR() + +VENDOR(1259,"Allied Telesyn International") + DEVICE(1259,2560,"AT-2560 Fast Ethernet Adapter (i82557B)") +ENDVENDOR() + +VENDOR(125a,"ABB Power Systems") +ENDVENDOR() + +VENDOR(125b,"Asix Electronics Corporation") +ENDVENDOR() + +VENDOR(125c,"Aurora Technologies, Inc.") +ENDVENDOR() + +VENDOR(125d,"ESS Technology") + DEVICE(125d,0000,"ES336H Fax Modem (Early Model)") + DEVICE(125d,1948,"Solo?") + DEVICE(125d,1968,"ES1968 Maestro 2") + DEVICE(125d,1969,"ES1969 Solo-1 Audiodrive") + DEVICE(125d,1978,"ES1978 Maestro 2E") + DEVICE(125d,1988,"ES1988 Allegro-1") + DEVICE(125d,1989,"ESS Modem") + DEVICE(125d,1998,"ES1983S Maestro-3i PCI Audio Accelerator") + DEVICE(125d,1999,"ES1983S Maestro-3i PCI Modem Accelerator") + DEVICE(125d,2808,"ES336H Fax Modem (Later Model)") + DEVICE(125d,2838,"ES2838/2839 SuperLink Modem") + DEVICE(125d,2898,"ES2898 Modem") +ENDVENDOR() + +VENDOR(125e,"Specialvideo Engineering SRL") +ENDVENDOR() + +VENDOR(125f,"Concurrent Technologies, Inc.") +ENDVENDOR() + +VENDOR(1260,"Harris Semiconductor") + DEVICE(1260,8130,"HMP8130 NTSC/PAL Video Decoder") + DEVICE(1260,8131,"HMP8131 NTSC/PAL Video Decoder") +ENDVENDOR() + +VENDOR(1261,"Matsushita-Kotobuki Electronics Industries, Ltd.") +ENDVENDOR() + +VENDOR(1262,"ES Computer Company, Ltd.") +ENDVENDOR() + +VENDOR(1263,"Sonic Solutions") +ENDVENDOR() + +VENDOR(1264,"Aval Nagasaki Corporation") +ENDVENDOR() + +VENDOR(1265,"Casio Computer Co., Ltd.") +ENDVENDOR() + +VENDOR(1266,"Microdyne Corporation") + DEVICE(1266,0001,"NE10/100 Adapter (i82557B)") + DEVICE(1266,1910,"NE2000Plus (RT8029) Ethernet Adapter") +ENDVENDOR() + +VENDOR(1267,"S. A. Telecommunications") + DEVICE(1267,5352,"PCR2101") + DEVICE(1267,5a4b,"Telsat Turbo") +ENDVENDOR() + +VENDOR(1268,"Tektronix") +ENDVENDOR() + +VENDOR(1269,"Thomson-CSF/TTM") +ENDVENDOR() + +VENDOR(126a,"Lexmark International, Inc.") +ENDVENDOR() + +VENDOR(126b,"Adax, Inc.") +ENDVENDOR() + +VENDOR(126c,"Northern Telecom") +ENDVENDOR() + +VENDOR(126d,"Splash Technology, Inc.") +ENDVENDOR() + +VENDOR(126e,"Sumitomo Metal Industries, Ltd.") +ENDVENDOR() + +VENDOR(126f,"Silicon Motion, Inc.") + DEVICE(126f,0710,"SM710 LynxEM") + DEVICE(126f,0712,"SM712 LynxEM+") + DEVICE(126f,0720,"SM720 Lynx3DM") + DEVICE(126f,0810,"SM810 LynxE") + DEVICE(126f,0811,"SM811 LynxE") + DEVICE(126f,0820,"SM820 Lynx3D") + DEVICE(126f,0910,"SM910") +ENDVENDOR() + +VENDOR(1270,"Olympus Optical Co., Ltd.") +ENDVENDOR() + +VENDOR(1271,"GW Instruments") +ENDVENDOR() + +VENDOR(1272,"Telematics International") +ENDVENDOR() + +VENDOR(1273,"Hughes Network Systems") + DEVICE(1273,0002,"DirecPC") +ENDVENDOR() + +VENDOR(1274,"Ensoniq") + DEVICE(1274,1371,"ES1371 [AudioPCI-97]") + DEVICE(1274,5000,"ES1370 [AudioPCI]") + DEVICE(1274,5880,"CT5880 [AudioPCI]") +ENDVENDOR() + +VENDOR(1275,"Network Appliance Corporation") +ENDVENDOR() + +VENDOR(1276,"Switched Network Technologies, Inc.") +ENDVENDOR() + +VENDOR(1277,"Comstream") +ENDVENDOR() + +VENDOR(1278,"Transtech Parallel Systems Ltd.") +ENDVENDOR() + +VENDOR(1279,"Transmeta Corporation") + DEVICE(1279,0295,"Northbridge") + DEVICE(1279,0395,"LongRun Northbridge") + DEVICE(1279,0396,"SDRAM controller") + DEVICE(1279,0397,"BIOS scratchpad") +ENDVENDOR() + +VENDOR(127a,"Rockwell International") + DEVICE(127a,1002,"HCF 56k V90 FaxModem") + DEVICE(127a,1003,"HCF 56k V90 FaxModem") + DEVICE(127a,1004,"HCF 56k V90 FaxModem") + DEVICE(127a,1005,"HCF 56k V90 FaxModem") + DEVICE(127a,1025,"HCF 56k PCI Modem") + DEVICE(127a,1026,"HCF 56k PCI Speakerphone Modem") + DEVICE(127a,1035,"HCF 56k PCI Speakerphone Modem") + DEVICE(127a,1085,"Volcano HCF 56k PCI Modem") + DEVICE(127a,2005,"HCF 56k V90 FaxModem") + DEVICE(127a,2015,"Conexant SoftK56 Speakerphone Modem") + DEVICE(127a,4320,"Riptide PCI Audio Controller") + DEVICE(127a,4321,"Riptide HCF 56k PCI Modem") + DEVICE(127a,4322,"Riptide PCI Game Controller") + DEVICE(127a,8234,"RapidFire 616X ATM155 Adapter") +ENDVENDOR() + +VENDOR(127b,"Pixera Corporation") +ENDVENDOR() + +VENDOR(127c,"Crosspoint Solutions, Inc.") +ENDVENDOR() + +VENDOR(127d,"Vela Research") +ENDVENDOR() + +VENDOR(127e,"Winnov, L.P.") +ENDVENDOR() + +VENDOR(127f,"Fujifilm") +ENDVENDOR() + +VENDOR(1280,"Photoscript Group Ltd.") +ENDVENDOR() + +VENDOR(1281,"Yokogawa Electric Corporation") +ENDVENDOR() + +VENDOR(1282,"Davicom Semiconductor, Inc.") + DEVICE(1282,9102,"Ethernet 100/10 MBit") +ENDVENDOR() + +VENDOR(1283,"Integrated Technology Express, Inc.") + DEVICE(1283,673a,"IT8330G") + DEVICE(1283,8330,"IT8330G") + DEVICE(1283,8888,"IT8888F PCI to ISA Bridge with SMB") + DEVICE(1283,8889,"IT8889F PCI to ISA Bridge") + DEVICE(1283,e886,"IT8330G") +ENDVENDOR() + +VENDOR(1284,"Sahara Networks, Inc.") +ENDVENDOR() + +VENDOR(1285,"Platform Technologies, Inc.") + DEVICE(1285,0100,"AGOGO sound chip (aka ESS Maestro 1)") +ENDVENDOR() + +VENDOR(1286,"Mazet GmbH") +ENDVENDOR() + +VENDOR(1287,"M-Pact, Inc.") + DEVICE(1287,001e,"LS220D DVD Decoder") + DEVICE(1287,001f,"LS220C DVD Decoder") +ENDVENDOR() + +VENDOR(1288,"Timestep Corporation") +ENDVENDOR() + +VENDOR(1289,"AVC Technology, Inc.") +ENDVENDOR() + +VENDOR(128a,"Asante Technologies, Inc.") +ENDVENDOR() + +VENDOR(128b,"Transwitch Corporation") +ENDVENDOR() + +VENDOR(128c,"Retix Corporation") +ENDVENDOR() + +VENDOR(128d,"G2 Networks, Inc.") + DEVICE(128d,0021,"ATM155 Adapter") +ENDVENDOR() + +VENDOR(128e,"Hoontech Corporation/Samho Multi Tech Ltd.") + DEVICE(128e,0008,"ST128 WSS/SB") + DEVICE(128e,0009,"ST128 SAM9407") + DEVICE(128e,000a,"ST128 Game Port") + DEVICE(128e,000b,"ST128 MPU Port") + DEVICE(128e,000c,"ST128 Ctrl Port") +ENDVENDOR() + +VENDOR(128f,"Tateno Dennou, Inc.") +ENDVENDOR() + +VENDOR(1290,"Sord Computer Corporation") +ENDVENDOR() + +VENDOR(1291,"NCS Computer Italia") +ENDVENDOR() + +VENDOR(1292,"Tritech Microelectronics Inc") +ENDVENDOR() + +VENDOR(1293,"Media Reality Technology") +ENDVENDOR() + +VENDOR(1294,"Rhetorex, Inc.") +ENDVENDOR() + +VENDOR(1295,"Imagenation Corporation") +ENDVENDOR() + +VENDOR(1296,"Kofax Image Products") +ENDVENDOR() + +VENDOR(1297,"Holco Enterprise Co, Ltd/Shuttle Computer") +ENDVENDOR() + +VENDOR(1298,"Spellcaster Telecommunications Inc.") +ENDVENDOR() + +VENDOR(1299,"Knowledge Technology Lab.") +ENDVENDOR() + +VENDOR(129a,"VMetro, inc.") +ENDVENDOR() + +VENDOR(129b,"Image Access") +ENDVENDOR() + +VENDOR(129c,"Jaycor") +ENDVENDOR() + +VENDOR(129d,"Compcore Multimedia, Inc.") +ENDVENDOR() + +VENDOR(129e,"Victor Company of Japan, Ltd.") +ENDVENDOR() + +VENDOR(129f,"OEC Medical Systems, Inc.") +ENDVENDOR() + +VENDOR(12a0,"Allen-Bradley Company") +ENDVENDOR() + +VENDOR(12a1,"Simpact Associates, Inc.") +ENDVENDOR() + +VENDOR(12a2,"Newgen Systems Corporation") +ENDVENDOR() + +VENDOR(12a3,"Lucent Technologies") +ENDVENDOR() + +VENDOR(12a4,"NTT Electronics Technology Company") +ENDVENDOR() + +VENDOR(12a5,"Vision Dynamics Ltd.") +ENDVENDOR() + +VENDOR(12a6,"Scalable Networks, Inc.") +ENDVENDOR() + +VENDOR(12a7,"AMO GmbH") +ENDVENDOR() + +VENDOR(12a8,"News Datacom") +ENDVENDOR() + +VENDOR(12a9,"Xiotech Corporation") +ENDVENDOR() + +VENDOR(12aa,"SDL Communications, Inc.") +ENDVENDOR() + +VENDOR(12ab,"Yuan Yuan Enterprise Co., Ltd.") + DEVICE(12ab,3000,"MPG-200C PCI DVD Decoder Card") +ENDVENDOR() + +VENDOR(12ac,"Measurex Corporation") +ENDVENDOR() + +VENDOR(12ad,"Multidata GmbH") +ENDVENDOR() + +VENDOR(12ae,"Alteon Networks Inc.") + DEVICE(12ae,0001,"AceNIC Gigabit Ethernet") +ENDVENDOR() + +VENDOR(12af,"TDK USA Corp") +ENDVENDOR() + +VENDOR(12b0,"Jorge Scientific Corp") +ENDVENDOR() + +VENDOR(12b1,"GammaLink") +ENDVENDOR() + +VENDOR(12b2,"General Signal Networks") +ENDVENDOR() + +VENDOR(12b3,"Inter-Face Co Ltd") +ENDVENDOR() + +VENDOR(12b4,"FutureTel Inc") +ENDVENDOR() + +VENDOR(12b5,"Granite Systems Inc.") +ENDVENDOR() + +VENDOR(12b6,"Natural Microsystems") +ENDVENDOR() + +VENDOR(12b7,"Cognex Modular Vision Systems Div. - Acumen Inc.") +ENDVENDOR() + +VENDOR(12b8,"Korg") +ENDVENDOR() + +VENDOR(12b9,"US Robotics/3Com") + DEVICE(12b9,1006,"WinModem") + DEVICE(12b9,1007,"USR 56k Internal WinModem") + DEVICE(12b9,1008,"56K FaxModem Model 5610") +ENDVENDOR() + +VENDOR(12ba,"PMC Sierra") +ENDVENDOR() + +VENDOR(12bb,"Nippon Unisoft Corporation") +ENDVENDOR() + +VENDOR(12bc,"Array Microsystems") +ENDVENDOR() + +VENDOR(12bd,"Computerm Corp.") +ENDVENDOR() + +VENDOR(12be,"Anchor Chips Inc.") + DEVICE(12be,3041,"AN3041Q CO-MEM") + DEVICE(12be,3042,"AN3042Q CO-MEM Lite") +ENDVENDOR() + +VENDOR(12bf,"Fujifilm Microdevices") +ENDVENDOR() + +VENDOR(12c0,"Infimed") +ENDVENDOR() + +VENDOR(12c1,"GMM Research Corp") +ENDVENDOR() + +VENDOR(12c2,"Mentec Limited") +ENDVENDOR() + +VENDOR(12c3,"Holtek Microelectronics Inc") +ENDVENDOR() + +VENDOR(12c4,"Connect Tech Inc") +ENDVENDOR() + +VENDOR(12c5,"Picture Elements Incorporated") + DEVICE(12c5,0081,"PCIVST [Grayscale Thresholding Engine]") + DEVICE(12c5,0085,"Video Simulator/Sender") + DEVICE(12c5,0086,"THR2 Multi-scale Thresholder") +ENDVENDOR() + +VENDOR(12c6,"Mitani Corporation") +ENDVENDOR() + +VENDOR(12c7,"Dialogic Corp") +ENDVENDOR() + +VENDOR(12c8,"G Force Co, Ltd") +ENDVENDOR() + +VENDOR(12c9,"Gigi Operations") +ENDVENDOR() + +VENDOR(12ca,"Integrated Computing Engines") +ENDVENDOR() + +VENDOR(12cb,"Antex Electronics Corporation") +ENDVENDOR() + +VENDOR(12cc,"Pluto Technologies International") +ENDVENDOR() + +VENDOR(12cd,"Aims Lab") +ENDVENDOR() + +VENDOR(12ce,"Netspeed Inc.") +ENDVENDOR() + +VENDOR(12cf,"Prophet Systems, Inc.") +ENDVENDOR() + +VENDOR(12d0,"GDE Systems, Inc.") +ENDVENDOR() + +VENDOR(12d1,"PSITech") +ENDVENDOR() + +VENDOR(12d2,"NVidia / SGS Thomson (Joint Venture)") + DEVICE(12d2,0008,"NV1") + DEVICE(12d2,0009,"DAC64") + DEVICE(12d2,0018,"Riva128") + DEVICE(12d2,0019,"Riva128ZX") + DEVICE(12d2,0020,"TNT") + DEVICE(12d2,0028,"TNT2") + DEVICE(12d2,0029,"UTNT2") + DEVICE(12d2,002c,"VTNT2") + DEVICE(12d2,00a0,"ITNT2") +ENDVENDOR() + +VENDOR(12d3,"Vingmed Sound A/S") +ENDVENDOR() + +VENDOR(12d4,"DGM&S") +ENDVENDOR() + +VENDOR(12d5,"Equator Technologies") +ENDVENDOR() + +VENDOR(12d6,"Analogic Corp") +ENDVENDOR() + +VENDOR(12d7,"Biotronic SRL") +ENDVENDOR() + +VENDOR(12d8,"Pericom Semiconductor") +ENDVENDOR() + +VENDOR(12d9,"Aculab PLC") +ENDVENDOR() + +VENDOR(12da,"True Time Inc.") +ENDVENDOR() + +VENDOR(12db,"Annapolis Micro Systems, Inc") +ENDVENDOR() + +VENDOR(12dc,"Symicron Computer Communication Ltd.") +ENDVENDOR() + +VENDOR(12dd,"Management Graphics") +ENDVENDOR() + +VENDOR(12de,"Rainbow Technologies") +ENDVENDOR() + +VENDOR(12df,"SBS Technologies Inc") +ENDVENDOR() + +VENDOR(12e0,"Chase Research") + DEVICE(12e0,0010,"ST16C654 Quad UART") + DEVICE(12e0,0020,"ST16C654 Quad UART") + DEVICE(12e0,0030,"ST16C654 Quad UART") +ENDVENDOR() + +VENDOR(12e1,"Nintendo Co, Ltd") +ENDVENDOR() + +VENDOR(12e2,"Datum Inc. Bancomm-Timing Division") +ENDVENDOR() + +VENDOR(12e3,"Imation Corp - Medical Imaging Systems") +ENDVENDOR() + +VENDOR(12e4,"Brooktrout Technology Inc") +ENDVENDOR() + +VENDOR(12e5,"Apex Semiconductor Inc") +ENDVENDOR() + +VENDOR(12e6,"Cirel Systems") +ENDVENDOR() + +VENDOR(12e7,"Sunsgroup Corporation") +ENDVENDOR() + +VENDOR(12e8,"Crisc Corp") +ENDVENDOR() + +VENDOR(12e9,"GE Spacenet") +ENDVENDOR() + +VENDOR(12ea,"Zuken") +ENDVENDOR() + +VENDOR(12eb,"Aureal Semiconductor") + DEVICE(12eb,0001,"Vortex 1") + DEVICE(12eb,0002,"Vortex 2") + DEVICE(12eb,0003,"AU8810 Vortex Digital Audio Processor") + DEVICE(12eb,8803,"Vortex 56k Software Modem") +ENDVENDOR() + +VENDOR(12ec,"3A International, Inc.") +ENDVENDOR() + +VENDOR(12ed,"Optivision Inc.") +ENDVENDOR() + +VENDOR(12ee,"Orange Micro") +ENDVENDOR() + +VENDOR(12ef,"Vienna Systems") +ENDVENDOR() + +VENDOR(12f0,"Pentek") +ENDVENDOR() + +VENDOR(12f1,"Sorenson Vision Inc") +ENDVENDOR() + +VENDOR(12f2,"Gammagraphx, Inc.") +ENDVENDOR() + +VENDOR(12f3,"Radstone Technology") +ENDVENDOR() + +VENDOR(12f4,"Megatel") +ENDVENDOR() + +VENDOR(12f5,"Forks") +ENDVENDOR() + +VENDOR(12f6,"Dawson France") +ENDVENDOR() + +VENDOR(12f7,"Cognex") +ENDVENDOR() + +VENDOR(12f8,"Electronic Design GmbH") + DEVICE(12f8,0002,"VideoMaker") +ENDVENDOR() + +VENDOR(12f9,"Four Fold Ltd") +ENDVENDOR() + +VENDOR(12fb,"Spectrum Signal Processing") +ENDVENDOR() + +VENDOR(12fc,"Capital Equipment Corp") +ENDVENDOR() + +VENDOR(12fd,"I2S") +ENDVENDOR() + +VENDOR(12fe,"ESD Electronic System Design GmbH") +ENDVENDOR() + +VENDOR(12ff,"Lexicon") +ENDVENDOR() + +VENDOR(1300,"Harman International Industries Inc") +ENDVENDOR() + +VENDOR(1302,"Computer Sciences Corp") +ENDVENDOR() + +VENDOR(1303,"Innovative Integration") +ENDVENDOR() + +VENDOR(1304,"Juniper Networks") +ENDVENDOR() + +VENDOR(1305,"Netphone, Inc") +ENDVENDOR() + +VENDOR(1306,"Duet Technologies") +ENDVENDOR() + +VENDOR(1307,"Computer Boards") + DEVICE(1307,0001,"PCI-DAS1602/16") + DEVICE(1307,000b,"PCI-DIO48H") + DEVICE(1307,000c,"PCI-PDISO8") + DEVICE(1307,000d,"PCI-PDISO16") + DEVICE(1307,000f,"PCI-DAS1200") + DEVICE(1307,0010,"PCI-DAS1602/12") + DEVICE(1307,0014,"PCI-DIO24H") + DEVICE(1307,0015,"PCI-DIO24H/CTR3") + DEVICE(1307,0016,"PCI-DIO48H/CTR15") + DEVICE(1307,0017,"PCI-DIO96H") + DEVICE(1307,0018,"PCI-CTR05") + DEVICE(1307,0019,"PCI-DAS1200/JR") + DEVICE(1307,001a,"PCI-DAS1001") + DEVICE(1307,001b,"PCI-DAS1002") + DEVICE(1307,001c,"PCI-DAS1602JR/16") + DEVICE(1307,001d,"PCI-DAS6402/16") + DEVICE(1307,001e,"PCI-DAS6402/12") + DEVICE(1307,001f,"PCI-DAS16/M1") + DEVICE(1307,0020,"PCI-DDA02/12") + DEVICE(1307,0021,"PCI-DDA04/12") + DEVICE(1307,0022,"PCI-DDA08/12") + DEVICE(1307,0023,"PCI-DDA02/16") + DEVICE(1307,0024,"PCI-DDA04/16") + DEVICE(1307,0025,"PCI-DDA08/16") + DEVICE(1307,0026,"PCI-DAC04/12-HS") + DEVICE(1307,0027,"PCI-DAC04/16-HS") + DEVICE(1307,0028,"PCI-DIO24") + DEVICE(1307,0029,"PCI-DAS08") + DEVICE(1307,002c,"PCI-INT32") + DEVICE(1307,0033,"PCI-DUAL-AC5") + DEVICE(1307,0034,"PCI-DAS-TC") + DEVICE(1307,0035,"PCI-DAS64/M1/16") + DEVICE(1307,0036,"PCI-DAS64/M2/16") + DEVICE(1307,0037,"PCI-DAS64/M3/16") + DEVICE(1307,004c,"PCI-DAS1000") +ENDVENDOR() + +VENDOR(1308,"Jato Technologies Inc.") + DEVICE(1308,0001,"NetCelerator Adapter") +ENDVENDOR() + +VENDOR(1309,"AB Semiconductor Ltd") +ENDVENDOR() + +VENDOR(130a,"Mitsubishi Electric Microcomputer") +ENDVENDOR() + +VENDOR(130b,"Colorgraphic Communications Corp") +ENDVENDOR() + +VENDOR(130c,"Ambex Technologies, Inc") +ENDVENDOR() + +VENDOR(130d,"Accelerix Inc") +ENDVENDOR() + +VENDOR(130e,"Yamatake-Honeywell Co. Ltd") +ENDVENDOR() + +VENDOR(130f,"Advanet Inc") +ENDVENDOR() + +VENDOR(1310,"Gespac") +ENDVENDOR() + +VENDOR(1311,"Videoserver, Inc") +ENDVENDOR() + +VENDOR(1312,"Acuity Imaging, Inc") +ENDVENDOR() + +VENDOR(1313,"Yaskawa Electric Co.") +ENDVENDOR() + +VENDOR(1316,"Teradyne Inc") +ENDVENDOR() + +VENDOR(1317,"Bridgecom, Inc") +ENDVENDOR() + +VENDOR(1318,"Packet Engines Inc.") + DEVICE(1318,0911,"PCI Ethernet Adapter") +ENDVENDOR() + +VENDOR(1319,"Fortemedia, Inc") + DEVICE(1319,0801,"Xwave QS3000A [FM801]") + DEVICE(1319,0802,"Xwave QS3000A [FM801 game port]") + DEVICE(1319,1000,"FM801 PCI Audio") + DEVICE(1319,1001,"FM801 PCI Joystick") +ENDVENDOR() + +VENDOR(131a,"Finisar Corp.") +ENDVENDOR() + +VENDOR(131c,"Nippon Electro-Sensory Devices Corp") +ENDVENDOR() + +VENDOR(131d,"Sysmic, Inc.") +ENDVENDOR() + +VENDOR(131e,"Xinex Networks Inc") +ENDVENDOR() + +VENDOR(131f,"Siig Inc") + DEVICE(131f,1000,"CyberSerial (1-port) 16550") + DEVICE(131f,1001,"CyberSerial (1-port) 16650") + DEVICE(131f,1002,"CyberSerial (1-port) 16850") + DEVICE(131f,1010,"Duet 1S(16550)+1P") + DEVICE(131f,1011,"Duet 1S(16650)+1P") + DEVICE(131f,1012,"Duet 1S(16850)+1P") + DEVICE(131f,1020,"CyberParallel (1-port)") + DEVICE(131f,1021,"CyberParallel (2-port)") + DEVICE(131f,1030,"CyberSerial (2-port) 16550") + DEVICE(131f,1031,"CyberSerial (2-port) 16650") + DEVICE(131f,1032,"CyberSerial (2-port) 16850") + DEVICE(131f,1034,"Trio 2S(16550)+1P") + DEVICE(131f,1035,"Trio 2S(16650)+1P") + DEVICE(131f,1036,"Trio 2S(16850)+1P") + DEVICE(131f,1050,"CyberSerial (4-port) 16550") + DEVICE(131f,1051,"CyberSerial (4-port) 16650") + DEVICE(131f,1052,"CyberSerial (4-port) 16850") + DEVICE(131f,2000,"CyberSerial (1-port) 16550") + DEVICE(131f,2001,"CyberSerial (1-port) 16650") + DEVICE(131f,2002,"CyberSerial (1-port) 16850") + DEVICE(131f,2010,"Duet 1S(16550)+1P") + DEVICE(131f,2011,"Duet 1S(16650)+1P") + DEVICE(131f,2012,"Duet 1S(16850)+1P") + DEVICE(131f,2020,"CyberParallel (1-port)") + DEVICE(131f,2021,"CyberParallel (2-port)") + DEVICE(131f,2030,"CyberSerial (2-port) 16550") + DEVICE(131f,2031,"CyberSerial (2-port) 16650") + DEVICE(131f,2032,"CyberSerial (2-port) 16850") + DEVICE(131f,2040,"Trio 1S(16550)+2P") + DEVICE(131f,2041,"Trio 1S(16650)+2P") + DEVICE(131f,2042,"Trio 1S(16850)+2P") + DEVICE(131f,2050,"CyberSerial (4-port) 16550") + DEVICE(131f,2051,"CyberSerial (4-port) 16650") + DEVICE(131f,2052,"CyberSerial (4-port) 16850") + DEVICE(131f,2060,"Trio 2S(16550)+1P") + DEVICE(131f,2061,"Trio 2S(16650)+1P") + DEVICE(131f,2062,"Trio 2S(16850)+1P") +ENDVENDOR() + +VENDOR(1320,"Crypto AG") +ENDVENDOR() + +VENDOR(1321,"Arcobel Graphics BV") +ENDVENDOR() + +VENDOR(1322,"MTT Co., Ltd") +ENDVENDOR() + +VENDOR(1323,"Dome Inc") +ENDVENDOR() + +VENDOR(1324,"Sphere Communications") +ENDVENDOR() + +VENDOR(1325,"Salix Technologies, Inc") +ENDVENDOR() + +VENDOR(1326,"Seachange international") +ENDVENDOR() + +VENDOR(1327,"Voss scientific") +ENDVENDOR() + +VENDOR(1328,"quadrant international") +ENDVENDOR() + +VENDOR(1329,"Productivity Enhancement") +ENDVENDOR() + +VENDOR(132a,"Microcom Inc.") +ENDVENDOR() + +VENDOR(132b,"Broadband Technologies") +ENDVENDOR() + +VENDOR(132c,"Micrel Inc") +ENDVENDOR() + +VENDOR(132d,"Integrated Silicon Solution, Inc.") +ENDVENDOR() + +VENDOR(1330,"MMC Networks") +ENDVENDOR() + +VENDOR(1331,"Radisys Corp.") +ENDVENDOR() + +VENDOR(1332,"Micro Memory") +ENDVENDOR() + +VENDOR(1334,"Redcreek Communications, Inc") +ENDVENDOR() + +VENDOR(1335,"Videomail, Inc") +ENDVENDOR() + +VENDOR(1337,"Third Planet Publishing") +ENDVENDOR() + +VENDOR(1338,"BT Electronics") +ENDVENDOR() + +VENDOR(133a,"Vtel Corp") +ENDVENDOR() + +VENDOR(133b,"Softcom Microsystems") +ENDVENDOR() + +VENDOR(133c,"Holontech Corp") +ENDVENDOR() + +VENDOR(133d,"SS Technologies") +ENDVENDOR() + +VENDOR(133e,"Virtual Computer Corp") +ENDVENDOR() + +VENDOR(133f,"SCM Microsystems") +ENDVENDOR() + +VENDOR(1340,"Atalla Corp") +ENDVENDOR() + +VENDOR(1341,"Kyoto Microcomputer Co") +ENDVENDOR() + +VENDOR(1342,"Promax Systems Inc") +ENDVENDOR() + +VENDOR(1343,"Phylon Communications Inc") +ENDVENDOR() + +VENDOR(1344,"Crucial Technology") +ENDVENDOR() + +VENDOR(1345,"Arescom Inc") +ENDVENDOR() + +VENDOR(1347,"Odetics") +ENDVENDOR() + +VENDOR(1349,"Sumitomo Electric Industries, Ltd.") +ENDVENDOR() + +VENDOR(134a,"DTC Technology Corp.") + DEVICE(134a,0001,"Domex 536") + DEVICE(134a,0002,"Domex DMX3194UP SCSI Adapter") +ENDVENDOR() + +VENDOR(134b,"ARK Research Corp.") +ENDVENDOR() + +VENDOR(134c,"Chori Joho System Co. Ltd") +ENDVENDOR() + +VENDOR(134d,"PCTel Inc") + DEVICE(134d,7890,"HSP MicroModem 56") + DEVICE(134d,7891,"HSP MicroModem 56") + DEVICE(134d,7892,"HSP MicroModem 56") + DEVICE(134d,7893,"HSP MicroModem 56") + DEVICE(134d,7894,"HSP MicroModem 56") + DEVICE(134d,7895,"HSP MicroModem 56") + DEVICE(134d,7896,"HSP MicroModem 56") + DEVICE(134d,7897,"HSP MicroModem 56") +ENDVENDOR() + +VENDOR(134e,"CSTI") +ENDVENDOR() + +VENDOR(134f,"Algo System Co Ltd") +ENDVENDOR() + +VENDOR(1350,"Systec Co. Ltd") +ENDVENDOR() + +VENDOR(1351,"Sonix Inc") +ENDVENDOR() + +VENDOR(1353,"Dassault A.T.") +ENDVENDOR() + +VENDOR(1354,"Dwave System Inc") +ENDVENDOR() + +VENDOR(1355,"Kratos Analytical Ltd") +ENDVENDOR() + +VENDOR(1356,"The Logical Co") +ENDVENDOR() + +VENDOR(1359,"Prisa Networks") +ENDVENDOR() + +VENDOR(135a,"Brain Boxes") +ENDVENDOR() + +VENDOR(135b,"Giganet Inc") +ENDVENDOR() + +VENDOR(135c,"Quatech Inc") +ENDVENDOR() + +VENDOR(135d,"ABB Network Partner AB") +ENDVENDOR() + +VENDOR(135e,"Sealevel Systems Inc") + DEVICE(135e,7101,"Single Port RS-232/422/485/530") + DEVICE(135e,7201,"Dual Port RS-232/422/485 Interface") + DEVICE(135e,7202,"Dual Port RS-232 Interface") + DEVICE(135e,7401,"Four Port RS-232 Interface") + DEVICE(135e,7402,"Four Port RS-422/485 Interface") + DEVICE(135e,7801,"Eight Port RS-232 Interface") + DEVICE(135e,8001,"8001 Digital I/O Adapter") +ENDVENDOR() + +VENDOR(135f,"I-Data International A-S") +ENDVENDOR() + +VENDOR(1360,"Meinberg Funkuhren") +ENDVENDOR() + +VENDOR(1361,"Soliton Systems K.K.") +ENDVENDOR() + +VENDOR(1362,"Fujifacom Corporation") +ENDVENDOR() + +VENDOR(1363,"Phoenix Technology Ltd") +ENDVENDOR() + +VENDOR(1364,"ATM Communications Inc") +ENDVENDOR() + +VENDOR(1365,"Hypercope GmbH") +ENDVENDOR() + +VENDOR(1366,"Teijin Seiki Co. Ltd") +ENDVENDOR() + +VENDOR(1367,"Hitachi Zosen Corporation") +ENDVENDOR() + +VENDOR(1368,"Skyware Corporation") +ENDVENDOR() + +VENDOR(1369,"Digigram") +ENDVENDOR() + +VENDOR(136a,"High Soft Tech") +ENDVENDOR() + +VENDOR(136b,"Kawasaki Steel Corporation") +ENDVENDOR() + +VENDOR(136c,"Adtek System Science Co Ltd") +ENDVENDOR() + +VENDOR(136d,"Gigalabs Inc") +ENDVENDOR() + +VENDOR(136f,"Applied Magic Inc") +ENDVENDOR() + +VENDOR(1370,"ATL Products") +ENDVENDOR() + +VENDOR(1371,"CNet Technology Inc") +ENDVENDOR() + +VENDOR(1373,"Silicon Vision Inc") +ENDVENDOR() + +VENDOR(1374,"Silicom Ltd") +ENDVENDOR() + +VENDOR(1375,"Argosystems Inc") +ENDVENDOR() + +VENDOR(1376,"LMC") +ENDVENDOR() + +VENDOR(1377,"Electronic Equipment Production & Distribution GmbH") +ENDVENDOR() + +VENDOR(1378,"Telemann Co. Ltd") +ENDVENDOR() + +VENDOR(1379,"Asahi Kasei Microsystems Co Ltd") +ENDVENDOR() + +VENDOR(137a,"Mark of the Unicorn Inc") +ENDVENDOR() + +VENDOR(137b,"PPT Vision") +ENDVENDOR() + +VENDOR(137c,"Iwatsu Electric Co Ltd") +ENDVENDOR() + +VENDOR(137d,"Dynachip Corporation") +ENDVENDOR() + +VENDOR(137e,"Patriot Scientific Corporation") +ENDVENDOR() + +VENDOR(137f,"Japan Satellite Systems Inc") +ENDVENDOR() + +VENDOR(1380,"Sanritz Automation Co Ltd") +ENDVENDOR() + +VENDOR(1381,"Brains Co. Ltd") +ENDVENDOR() + +VENDOR(1382,"Marian - Electronic & Software") +ENDVENDOR() + +VENDOR(1383,"Controlnet Inc") +ENDVENDOR() + +VENDOR(1384,"Reality Simulation Systems Inc") +ENDVENDOR() + +VENDOR(1385,"Netgear") + DEVICE(1385,620a,"GA620") +ENDVENDOR() + +VENDOR(1386,"Video Domain Technologies") +ENDVENDOR() + +VENDOR(1387,"Systran Corp") +ENDVENDOR() + +VENDOR(1388,"Hitachi Information Technology Co Ltd") +ENDVENDOR() + +VENDOR(1389,"Applicom International") + DEVICE(1389,0001,"PCI1500PFB [Intelligent fieldbus adaptor]") +ENDVENDOR() + +VENDOR(138a,"Fusion Micromedia Corp") +ENDVENDOR() + +VENDOR(138b,"Tokimec Inc") +ENDVENDOR() + +VENDOR(138c,"Silicon Reality") +ENDVENDOR() + +VENDOR(138d,"Future Techno Designs pte Ltd") +ENDVENDOR() + +VENDOR(138e,"Basler GmbH") +ENDVENDOR() + +VENDOR(138f,"Patapsco Designs Inc") +ENDVENDOR() + +VENDOR(1390,"Concept Development Inc") +ENDVENDOR() + +VENDOR(1391,"Development Concepts Inc") +ENDVENDOR() + +VENDOR(1392,"Medialight Inc") +ENDVENDOR() + +VENDOR(1393,"Moxa Technologies Co Ltd") +ENDVENDOR() + +VENDOR(1394,"Level One Communications") +ENDVENDOR() + +VENDOR(1395,"Ambicom Inc") +ENDVENDOR() + +VENDOR(1396,"Cipher Systems Inc") +ENDVENDOR() + +VENDOR(1397,"Cologne Chip Designs GmbH") + DEVICE(1397,2bd0,"ISDN network controller [HFC-PCI]") +ENDVENDOR() + +VENDOR(1398,"Clarion co. Ltd") +ENDVENDOR() + +VENDOR(1399,"Rios systems Co Ltd") +ENDVENDOR() + +VENDOR(139a,"Alacritech Inc") +ENDVENDOR() + +VENDOR(139b,"Mediasonic Multimedia Systems Ltd") +ENDVENDOR() + +VENDOR(139c,"Quantum 3d Inc") +ENDVENDOR() + +VENDOR(139d,"EPL limited") +ENDVENDOR() + +VENDOR(139e,"Media4") +ENDVENDOR() + +VENDOR(139f,"Aethra s.r.l.") +ENDVENDOR() + +VENDOR(13a0,"Crystal Group Inc") +ENDVENDOR() + +VENDOR(13a1,"Kawasaki Heavy Industries Ltd") +ENDVENDOR() + +VENDOR(13a2,"Ositech Communications Inc") +ENDVENDOR() + +VENDOR(13a3,"Hi-Fn") +ENDVENDOR() + +VENDOR(13a4,"Rascom Inc") +ENDVENDOR() + +VENDOR(13a5,"Audio Digital Imaging Inc") +ENDVENDOR() + +VENDOR(13a6,"Videonics Inc") +ENDVENDOR() + +VENDOR(13a7,"Teles AG") +ENDVENDOR() + +VENDOR(13a8,"Exar Corp.") +ENDVENDOR() + +VENDOR(13a9,"Siemens Medical Systems, Ultrasound Group") +ENDVENDOR() + +VENDOR(13aa,"Broadband Networks Inc") +ENDVENDOR() + +VENDOR(13ab,"Arcom Control Systems Ltd") +ENDVENDOR() + +VENDOR(13ac,"Motion Media Technology Ltd") +ENDVENDOR() + +VENDOR(13ad,"Nexus Inc") +ENDVENDOR() + +VENDOR(13ae,"ALD Technology Ltd") +ENDVENDOR() + +VENDOR(13af,"T.Sqware") +ENDVENDOR() + +VENDOR(13b0,"Maxspeed Corp") +ENDVENDOR() + +VENDOR(13b1,"Tamura corporation") +ENDVENDOR() + +VENDOR(13b2,"Techno Chips Co. Ltd") +ENDVENDOR() + +VENDOR(13b3,"Lanart Corporation") +ENDVENDOR() + +VENDOR(13b4,"Wellbean Co Inc") +ENDVENDOR() + +VENDOR(13b5,"ARM") +ENDVENDOR() + +VENDOR(13b6,"Dlog GmbH") +ENDVENDOR() + +VENDOR(13b7,"Logic Devices Inc") +ENDVENDOR() + +VENDOR(13b8,"Nokia Telecommunications oy") +ENDVENDOR() + +VENDOR(13b9,"Elecom Co Ltd") +ENDVENDOR() + +VENDOR(13ba,"Oxford Instruments") +ENDVENDOR() + +VENDOR(13bb,"Sanyo Technosound Co Ltd") +ENDVENDOR() + +VENDOR(13bc,"Bitran Corporation") +ENDVENDOR() + +VENDOR(13bd,"Sharp corporation") +ENDVENDOR() + +VENDOR(13be,"Miroku Jyoho Service Co. Ltd") +ENDVENDOR() + +VENDOR(13bf,"Sharewave Inc") +ENDVENDOR() + +VENDOR(13c0,"Microgate Corporation") + DEVICE(13c0,0010,"SyncLink WAN Adapter") +ENDVENDOR() + +VENDOR(13c1,"3ware Inc") + DEVICE(13c1,1000,"3ware ATA-RAID") +ENDVENDOR() + +VENDOR(13c2,"Technotrend Systemtechnik GmbH") +ENDVENDOR() + +VENDOR(13c3,"Janz Computer AG") +ENDVENDOR() + +VENDOR(13c4,"Phase Metrics") +ENDVENDOR() + +VENDOR(13c5,"Alphi Technology Corp") +ENDVENDOR() + +VENDOR(13c6,"Condor Engineering Inc") +ENDVENDOR() + +VENDOR(13c7,"Blue Chip Technology Ltd") +ENDVENDOR() + +VENDOR(13c8,"Apptech Inc") +ENDVENDOR() + +VENDOR(13c9,"Eaton Corporation") +ENDVENDOR() + +VENDOR(13ca,"Iomega Corporation") +ENDVENDOR() + +VENDOR(13cb,"Yano Electric Co Ltd") +ENDVENDOR() + +VENDOR(13cc,"Metheus Corporation") +ENDVENDOR() + +VENDOR(13cd,"Compatible Systems Corporation") +ENDVENDOR() + +VENDOR(13ce,"Cocom A/S") +ENDVENDOR() + +VENDOR(13cf,"Studio Audio & Video Ltd") +ENDVENDOR() + +VENDOR(13d0,"Techsan Electronics Co Ltd") +ENDVENDOR() + +VENDOR(13d1,"Abocom Systems Inc") +ENDVENDOR() + +VENDOR(13d2,"Shark Multimedia Inc") +ENDVENDOR() + +VENDOR(13d3,"IMC Networks") +ENDVENDOR() + +VENDOR(13d4,"Graphics Microsystems Inc") +ENDVENDOR() + +VENDOR(13d5,"Media 100 Inc") +ENDVENDOR() + +VENDOR(13d6,"K.I. Technology Co Ltd") +ENDVENDOR() + +VENDOR(13d7,"Toshiba Engineering Corporation") +ENDVENDOR() + +VENDOR(13d8,"Phobos corporation") +ENDVENDOR() + +VENDOR(13d9,"Apex PC Solutions Inc") +ENDVENDOR() + +VENDOR(13da,"Intresource Systems pte Ltd") +ENDVENDOR() + +VENDOR(13db,"Janich & Klass Computertechnik GmbH") +ENDVENDOR() + +VENDOR(13dc,"Netboost Corporation") +ENDVENDOR() + +VENDOR(13dd,"Multimedia Bundle Inc") +ENDVENDOR() + +VENDOR(13de,"ABB Robotics Products AB") +ENDVENDOR() + +VENDOR(13df,"E-Tech Inc") + DEVICE(13df,0001,"PCI56RVP Modem") +ENDVENDOR() + +VENDOR(13e0,"GVC Corporation") +ENDVENDOR() + +VENDOR(13e1,"Silicom Multimedia Systems Inc") +ENDVENDOR() + +VENDOR(13e2,"Dynamics Research Corporation") +ENDVENDOR() + +VENDOR(13e3,"Nest Inc") +ENDVENDOR() + +VENDOR(13e4,"Calculex Inc") +ENDVENDOR() + +VENDOR(13e5,"Telesoft Design Ltd") +ENDVENDOR() + +VENDOR(13e6,"Argosy research Inc") +ENDVENDOR() + +VENDOR(13e7,"NAC Incorporated") +ENDVENDOR() + +VENDOR(13e8,"Chip Express Corporation") +ENDVENDOR() + +VENDOR(13e9,"Chip Express Corporation") +ENDVENDOR() + +VENDOR(13ea,"Dallas Semiconductor") +ENDVENDOR() + +VENDOR(13eb,"Hauppauge Computer Works Inc") +ENDVENDOR() + +VENDOR(13ec,"Zydacron Inc") +ENDVENDOR() + +VENDOR(13ed,"Raytheion E-Systems") +ENDVENDOR() + +VENDOR(13ee,"Hayes Microcomputer Products Inc") +ENDVENDOR() + +VENDOR(13ef,"Coppercom Inc") +ENDVENDOR() + +VENDOR(13f0,"Sundance technology Inc") +ENDVENDOR() + +VENDOR(13f1,"Oce' - Technologies B.V.") +ENDVENDOR() + +VENDOR(13f2,"Ford Microelectronics Inc") +ENDVENDOR() + +VENDOR(13f3,"Mcdata Corporation") +ENDVENDOR() + +VENDOR(13f4,"Troika Design Inc") +ENDVENDOR() + +VENDOR(13f5,"Kansai Electric Co. Ltd") +ENDVENDOR() + +VENDOR(13f6,"C-Media Electronics Inc") + DEVICE(13f6,0100,"CM8338A") + DEVICE(13f6,0101,"CM8338B") + DEVICE(13f6,0111,"CM8738") + DEVICE(13f6,0211,"CM8738") +ENDVENDOR() + +VENDOR(13f7,"Wildfire Communications") +ENDVENDOR() + +VENDOR(13f8,"Ad Lib Multimedia Inc") +ENDVENDOR() + +VENDOR(13f9,"NTT Advanced Technology Corp.") +ENDVENDOR() + +VENDOR(13fa,"Pentland Systems Ltd") +ENDVENDOR() + +VENDOR(13fb,"Aydin Corp") +ENDVENDOR() + +VENDOR(13fc,"Computer Peripherals International") +ENDVENDOR() + +VENDOR(13fd,"Micro Science Inc") +ENDVENDOR() + +VENDOR(13fe,"Advantech Co. Ltd") +ENDVENDOR() + +VENDOR(13ff,"Silicon Spice Inc") +ENDVENDOR() + +VENDOR(1400,"Artx Inc") +ENDVENDOR() + +VENDOR(1401,"CR-Systems A/S") +ENDVENDOR() + +VENDOR(1402,"Meilhaus Electronic GmbH") +ENDVENDOR() + +VENDOR(1403,"Ascor Inc") +ENDVENDOR() + +VENDOR(1404,"Fundamental Software Inc") +ENDVENDOR() + +VENDOR(1405,"Excalibur Systems Inc") +ENDVENDOR() + +VENDOR(1406,"Oce' Printing Systems GmbH") +ENDVENDOR() + +VENDOR(1407,"Lava Computer mfg Inc") + DEVICE(1407,8000,"Lava Parallel") + DEVICE(1407,8002,"Lava Dual Parallel port A") + DEVICE(1407,8003,"Lava Dual Parallel port B") + DEVICE(1407,8800,"BOCA Research IOPPAR") +ENDVENDOR() + +VENDOR(1408,"Aloka Co. Ltd") +ENDVENDOR() + +VENDOR(1409,"Timedia Technology Co Ltd") +ENDVENDOR() + +VENDOR(140a,"DSP Research Inc") +ENDVENDOR() + +VENDOR(140b,"Ramix Inc") +ENDVENDOR() + +VENDOR(140c,"Elmic Systems Inc") +ENDVENDOR() + +VENDOR(140d,"Matsushita Electric Works Ltd") +ENDVENDOR() + +VENDOR(140e,"Goepel Electronic GmbH") +ENDVENDOR() + +VENDOR(140f,"Salient Systems Corp") +ENDVENDOR() + +VENDOR(1410,"Midas lab Inc") +ENDVENDOR() + +VENDOR(1411,"Ikos Systems Inc") +ENDVENDOR() + +VENDOR(1412,"IC Ensemble Inc") + DEVICE(1412,1712,"ICE1712 [Envy24]") +ENDVENDOR() + +VENDOR(1413,"Addonics") +ENDVENDOR() + +VENDOR(1414,"Microsoft Corporation") +ENDVENDOR() + +VENDOR(1415,"Oxford Semiconductor Ltd") +ENDVENDOR() + +VENDOR(1416,"Multiwave Innovation pte Ltd") +ENDVENDOR() + +VENDOR(1417,"Convergenet Technologies Inc") +ENDVENDOR() + +VENDOR(1418,"Kyushu electronics systems Inc") +ENDVENDOR() + +VENDOR(1419,"Excel Switching Corp") +ENDVENDOR() + +VENDOR(141a,"Apache Micro Peripherals Inc") +ENDVENDOR() + +VENDOR(141b,"Zoom Telephonics Inc") +ENDVENDOR() + +VENDOR(141d,"Digitan Systems Inc") +ENDVENDOR() + +VENDOR(141e,"Fanuc Ltd") +ENDVENDOR() + +VENDOR(141f,"Visiontech Ltd") +ENDVENDOR() + +VENDOR(1420,"Psion Dacom plc") +ENDVENDOR() + +VENDOR(1421,"Ads Technologies Inc") +ENDVENDOR() + +VENDOR(1422,"Ygrec Systems Co Ltd") +ENDVENDOR() + +VENDOR(1423,"Custom Technology Corp.") +ENDVENDOR() + +VENDOR(1424,"Videoserver Connections") +ENDVENDOR() + +VENDOR(1425,"ASIC Designers Inc") +ENDVENDOR() + +VENDOR(1426,"Storage Technology Corp.") +ENDVENDOR() + +VENDOR(1427,"Better On-Line Solutions") +ENDVENDOR() + +VENDOR(1428,"Edec Co Ltd") +ENDVENDOR() + +VENDOR(1429,"Unex Technology Corp.") +ENDVENDOR() + +VENDOR(142a,"Kingmax Technology Inc") +ENDVENDOR() + +VENDOR(142b,"Radiolan") +ENDVENDOR() + +VENDOR(142c,"Minton Optic Industry Co Ltd") +ENDVENDOR() + +VENDOR(142d,"Pix stream Inc") +ENDVENDOR() + +VENDOR(142e,"Vitec Multimedia") +ENDVENDOR() + +VENDOR(142f,"Radicom Research Inc") +ENDVENDOR() + +VENDOR(1430,"ITT Aerospace/Communications Division") +ENDVENDOR() + +VENDOR(1431,"Gilat Satellite Networks") +ENDVENDOR() + +VENDOR(1432,"Edimax Computer Co.") +ENDVENDOR() + +VENDOR(1433,"Eltec Elektronik GmbH") +ENDVENDOR() + +VENDOR(1435,"Real Time Devices US Inc.") +ENDVENDOR() + +VENDOR(1436,"CIS Technology Inc") +ENDVENDOR() + +VENDOR(1437,"Nissin Inc Co") +ENDVENDOR() + +VENDOR(1438,"Atmel-dream") +ENDVENDOR() + +VENDOR(1439,"Outsource Engineering & Mfg. Inc") +ENDVENDOR() + +VENDOR(143a,"Stargate Solutions Inc") +ENDVENDOR() + +VENDOR(143b,"Canon Research Center, America") +ENDVENDOR() + +VENDOR(143c,"Amlogic Inc") +ENDVENDOR() + +VENDOR(143d,"Tamarack Microelectronics Inc") +ENDVENDOR() + +VENDOR(143e,"Jones Futurex Inc") +ENDVENDOR() + +VENDOR(143f,"Lightwell Co Ltd - Zax Division") +ENDVENDOR() + +VENDOR(1440,"ALGOL Corp.") +ENDVENDOR() + +VENDOR(1441,"AGIE Ltd") +ENDVENDOR() + +VENDOR(1442,"Phoenix Contact GmbH & Co.") +ENDVENDOR() + +VENDOR(1443,"Unibrain S.A.") +ENDVENDOR() + +VENDOR(1444,"TRW") +ENDVENDOR() + +VENDOR(1445,"Logical DO Ltd") +ENDVENDOR() + +VENDOR(1446,"Graphin Co Ltd") +ENDVENDOR() + +VENDOR(1447,"AIM GmBH") +ENDVENDOR() + +VENDOR(1448,"Alesis Studio Electronics") +ENDVENDOR() + +VENDOR(1449,"TUT Systems Inc") +ENDVENDOR() + +VENDOR(144a,"Adlink Technology") + DEVICE(144a,7296,"PCI-7296") + DEVICE(144a,7432,"PCI-7432") + DEVICE(144a,7433,"PCI-7433") + DEVICE(144a,7434,"PCI-7434") + DEVICE(144a,7841,"PCI-7841") + DEVICE(144a,8133,"PCI-8133") + DEVICE(144a,8554,"PCI-8554") + DEVICE(144a,9111,"PCI-9111") + DEVICE(144a,9113,"PCI-9113") + DEVICE(144a,9114,"PCI-9114") +ENDVENDOR() + +VENDOR(144b,"Loronix Information Systems Inc") +ENDVENDOR() + +VENDOR(144c,"Catalina Research Inc") +ENDVENDOR() + +VENDOR(144d,"Samsung Electronics Co Ltd") +ENDVENDOR() + +VENDOR(144e,"OLITEC") +ENDVENDOR() + +VENDOR(144f,"Askey Computer Corp.") +ENDVENDOR() + +VENDOR(1450,"Octave Communications Ind.") +ENDVENDOR() + +VENDOR(1451,"SP3D Chip Design GmBH") +ENDVENDOR() + +VENDOR(1453,"MYCOM Inc") +ENDVENDOR() + +VENDOR(1454,"Altiga Networks") +ENDVENDOR() + +VENDOR(1455,"Logic Plus Plus Inc") +ENDVENDOR() + +VENDOR(1456,"Advanced Hardware Architectures") +ENDVENDOR() + +VENDOR(1457,"Nuera Communications Inc") +ENDVENDOR() + +VENDOR(1458,"Giga-byte Technology") +ENDVENDOR() + +VENDOR(1459,"DOOIN Electronics") +ENDVENDOR() + +VENDOR(145a,"Escalate Networks Inc") +ENDVENDOR() + +VENDOR(145b,"PRAIM SRL") +ENDVENDOR() + +VENDOR(145c,"Cryptek") +ENDVENDOR() + +VENDOR(145d,"Gallant Computer Inc") +ENDVENDOR() + +VENDOR(145e,"Aashima Technology B.V.") +ENDVENDOR() + +VENDOR(145f,"Baldor Electric Company") + DEVICE(145f,0001,"NextMove PCI") +ENDVENDOR() + +VENDOR(1460,"DYNARC INC") +ENDVENDOR() + +VENDOR(1461,"Avermedia Technologies Inc") +ENDVENDOR() + +VENDOR(1462,"Micro-star International Co Ltd") +ENDVENDOR() + +VENDOR(1463,"Fast Corporation") +ENDVENDOR() + +VENDOR(1464,"Interactive Circuits & Systems Ltd") +ENDVENDOR() + +VENDOR(1465,"GN NETTEST Telecom DIV.") +ENDVENDOR() + +VENDOR(1466,"Designpro Inc.") +ENDVENDOR() + +VENDOR(1467,"DIGICOM SPA") +ENDVENDOR() + +VENDOR(1468,"AMBIT Microsystem Corp.") +ENDVENDOR() + +VENDOR(1469,"Cleveland Motion Controls") +ENDVENDOR() + +VENDOR(146a,"IFR") +ENDVENDOR() + +VENDOR(146b,"Parascan Technologies Ltd") +ENDVENDOR() + +VENDOR(146c,"Ruby Tech Corp.") +ENDVENDOR() + +VENDOR(146d,"Tachyon, INC.") +ENDVENDOR() + +VENDOR(146e,"Williams Electronics Games, Inc.") +ENDVENDOR() + +VENDOR(146f,"Multi Dimensional Consulting Inc") +ENDVENDOR() + +VENDOR(1470,"Bay Networks") +ENDVENDOR() + +VENDOR(1471,"Integrated Telecom Express Inc") +ENDVENDOR() + +VENDOR(1472,"DAIKIN Industries, Ltd") +ENDVENDOR() + +VENDOR(1473,"ZAPEX Technologies Inc") +ENDVENDOR() + +VENDOR(1474,"Doug Carson & Associates") +ENDVENDOR() + +VENDOR(1475,"PICAZO Communications") +ENDVENDOR() + +VENDOR(1476,"MORTARA Instrument Inc") +ENDVENDOR() + +VENDOR(1477,"Net Insight") +ENDVENDOR() + +VENDOR(1478,"DIATREND Corporation") +ENDVENDOR() + +VENDOR(1479,"TORAY Industries Inc") +ENDVENDOR() + +VENDOR(147a,"FORMOSA Industrial Computing") +ENDVENDOR() + +VENDOR(147b,"ABIT Computer Corp.") +ENDVENDOR() + +VENDOR(147c,"AWARE, Inc.") +ENDVENDOR() + +VENDOR(147d,"Interworks Computer Products") +ENDVENDOR() + +VENDOR(147e,"Matsushita Graphic Communication Systems, Inc.") +ENDVENDOR() + +VENDOR(147f,"NIHON UNISYS, Ltd.") +ENDVENDOR() + +VENDOR(1480,"SCII Telecom") +ENDVENDOR() + +VENDOR(1481,"BIOPAC Systems Inc") +ENDVENDOR() + +VENDOR(1482,"ISYTEC - Integrierte Systemtechnik GmBH") +ENDVENDOR() + +VENDOR(1483,"LABWAY Corporation") +ENDVENDOR() + +VENDOR(1484,"Logic Corporation") +ENDVENDOR() + +VENDOR(1485,"ERMA - Electronic GmBH") +ENDVENDOR() + +VENDOR(1486,"L3 Communications Telemetry & Instrumentation") +ENDVENDOR() + +VENDOR(1487,"MARQUETTE Medical Systems") +ENDVENDOR() + +VENDOR(1488,"KONTRON Electronik GmBH") +ENDVENDOR() + +VENDOR(1489,"KYE Systems Corporation") +ENDVENDOR() + +VENDOR(148a,"OPTO") +ENDVENDOR() + +VENDOR(148b,"INNOMEDIALOGIC Inc.") +ENDVENDOR() + +VENDOR(148c,"C.P. Technology Co. Ltd") +ENDVENDOR() + +VENDOR(148d,"DIGICOM Systems, Inc.") +ENDVENDOR() + +VENDOR(148e,"OSI Plus Corporation") +ENDVENDOR() + +VENDOR(148f,"Plant Equipment, Inc.") +ENDVENDOR() + +VENDOR(1490,"Stone Microsystems PTY Ltd.") +ENDVENDOR() + +VENDOR(1491,"ZEAL Corporation") +ENDVENDOR() + +VENDOR(1492,"Time Logic Corporation") +ENDVENDOR() + +VENDOR(1493,"MAKER Communications") +ENDVENDOR() + +VENDOR(1494,"WINTOP Technology, Inc.") +ENDVENDOR() + +VENDOR(1495,"TOKAI Communications Industry Co. Ltd") +ENDVENDOR() + +VENDOR(1496,"JOYTECH Computer Co., Ltd.") +ENDVENDOR() + +VENDOR(1497,"SMA Regelsysteme GmBH") +ENDVENDOR() + +VENDOR(1498,"TEWS Datentechnik GmBH") +ENDVENDOR() + +VENDOR(1499,"EMTEC CO., Ltd") +ENDVENDOR() + +VENDOR(149a,"ANDOR Technology Ltd") +ENDVENDOR() + +VENDOR(149b,"SEIKO Instruments Inc") +ENDVENDOR() + +VENDOR(149c,"OVISLINK Corp.") +ENDVENDOR() + +VENDOR(149d,"NEWTEK Inc") +ENDVENDOR() + +VENDOR(149e,"Mapletree Networks Inc.") +ENDVENDOR() + +VENDOR(149f,"LECTRON Co Ltd") +ENDVENDOR() + +VENDOR(14a0,"SOFTING GmBH") +ENDVENDOR() + +VENDOR(14a1,"Systembase Co Ltd") +ENDVENDOR() + +VENDOR(14a2,"Millennium Engineering Inc") +ENDVENDOR() + +VENDOR(14a3,"Maverick Networks") +ENDVENDOR() + +VENDOR(14a4,"GVC/BCM Advanced Research") +ENDVENDOR() + +VENDOR(14a5,"XIONICS Document Technologies Inc") +ENDVENDOR() + +VENDOR(14a6,"INOVA Computers GmBH & Co KG") +ENDVENDOR() + +VENDOR(14a7,"MYTHOS Systems Inc") +ENDVENDOR() + +VENDOR(14a8,"FEATRON Technologies Corporation") +ENDVENDOR() + +VENDOR(14a9,"HIVERTEC Inc") +ENDVENDOR() + +VENDOR(14aa,"Advanced MOS Technology Inc") +ENDVENDOR() + +VENDOR(14ab,"Mentor Graphics Corp.") +ENDVENDOR() + +VENDOR(14ac,"Novaweb Technologies Inc") +ENDVENDOR() + +VENDOR(14ad,"Time Space Radio AB") +ENDVENDOR() + +VENDOR(14ae,"CTI, Inc") +ENDVENDOR() + +VENDOR(14af,"Guillemot Corporation") +ENDVENDOR() + +VENDOR(14b0,"BST Communication Technology Ltd") +ENDVENDOR() + +VENDOR(14b1,"Nextcom K.K.") +ENDVENDOR() + +VENDOR(14b2,"ENNOVATE Networks Inc") +ENDVENDOR() + +VENDOR(14b3,"XPEED Inc") + DEVICE(14b3,0000,"DSL NIC") +ENDVENDOR() + +VENDOR(14b4,"PHILIPS Business Electronics B.V.") +ENDVENDOR() + +VENDOR(14b5,"Creamware GmBH") +ENDVENDOR() + +VENDOR(14b6,"Quantum Data Corp.") +ENDVENDOR() + +VENDOR(14b7,"PROXIM Inc") + DEVICE(14b7,0001,"Symphony 4110") +ENDVENDOR() + +VENDOR(14b8,"Techsoft Technology Co Ltd") +ENDVENDOR() + +VENDOR(14b9,"AIRONET Wireless Communications") + DEVICE(14b9,0001,"PC4800") +ENDVENDOR() + +VENDOR(14ba,"INTERNIX Inc.") +ENDVENDOR() + +VENDOR(14bb,"SEMTECH Corporation") +ENDVENDOR() + +VENDOR(14bc,"Globespan Semiconductor Inc.") +ENDVENDOR() + +VENDOR(14bd,"CARDIO Control N.V.") +ENDVENDOR() + +VENDOR(14be,"L3 Communications") +ENDVENDOR() + +VENDOR(14bf,"SPIDER Communications Inc.") +ENDVENDOR() + +VENDOR(14c0,"COMPAL Electronics Inc") +ENDVENDOR() + +VENDOR(14c1,"MYRICOM Inc.") +ENDVENDOR() + +VENDOR(14c2,"DTK Computer") +ENDVENDOR() + +VENDOR(14c3,"MEDIATEK Corp.") +ENDVENDOR() + +VENDOR(14c4,"IWASAKI Information Systems Co Ltd") +ENDVENDOR() + +VENDOR(14c5,"Automation Products AB") +ENDVENDOR() + +VENDOR(14c6,"Data Race Inc") +ENDVENDOR() + +VENDOR(14c7,"Modular Technology Holdings Ltd") +ENDVENDOR() + +VENDOR(14c8,"Turbocomm Tech. Inc.") +ENDVENDOR() + +VENDOR(14c9,"ODIN Telesystems Inc") +ENDVENDOR() + +VENDOR(14ca,"PE Logic Corp.") +ENDVENDOR() + +VENDOR(14cb,"Billionton Systems Inc") +ENDVENDOR() + +VENDOR(14cc,"NAKAYO Telecommunications Inc") +ENDVENDOR() + +VENDOR(14cd,"Universal Scientific Ind.") +ENDVENDOR() + +VENDOR(14ce,"Whistle Communications") +ENDVENDOR() + +VENDOR(14cf,"TEK Microsystems Inc.") +ENDVENDOR() + +VENDOR(14d0,"Ericsson Axe R & D") +ENDVENDOR() + +VENDOR(14d1,"Computer Hi-Tech Co Ltd") +ENDVENDOR() + +VENDOR(14d2,"Titan Electronics Inc") +ENDVENDOR() + +VENDOR(14d3,"CIRTECH (UK) Ltd") +ENDVENDOR() + +VENDOR(14d4,"Panacom Technology Corp") +ENDVENDOR() + +VENDOR(14d5,"Nitsuko Corporation") +ENDVENDOR() + +VENDOR(14d6,"Accusys Inc") +ENDVENDOR() + +VENDOR(14d7,"Hirakawa Hewtech Corp") +ENDVENDOR() + +VENDOR(14d8,"HOPF Elektronik GmBH") +ENDVENDOR() + +VENDOR(14d9,"Alpha Processor Inc") +ENDVENDOR() + +VENDOR(14da,"National Aerospace Laboratories") +ENDVENDOR() + +VENDOR(14db,"AFAVLAB Technology Inc") + DEVICE(14db,2120,"TK9902") +ENDVENDOR() + +VENDOR(14dc,"Amplicon Liveline Ltd") + DEVICE(14dc,0000,"PCI230") + DEVICE(14dc,0001,"PCI242") + DEVICE(14dc,0002,"PCI244") + DEVICE(14dc,0003,"PCI247") + DEVICE(14dc,0004,"PCI248") + DEVICE(14dc,0005,"PCI249") + DEVICE(14dc,0006,"PCI260") + DEVICE(14dc,0007,"PCI224") + DEVICE(14dc,0008,"PCI234") + DEVICE(14dc,0009,"PCI236") +ENDVENDOR() + +VENDOR(14dd,"Boulder Design Labs Inc") +ENDVENDOR() + +VENDOR(14de,"Applied Integration Corporation") +ENDVENDOR() + +VENDOR(14df,"ASIC Communications Corp") +ENDVENDOR() + +VENDOR(14e1,"INVERTEX") +ENDVENDOR() + +VENDOR(14e2,"INFOLIBRIA") +ENDVENDOR() + +VENDOR(14e3,"AMTELCO") +ENDVENDOR() + +VENDOR(14e4,"BROADCOM Corporation") +ENDVENDOR() + +VENDOR(14e5,"Pixelfusion Ltd") +ENDVENDOR() + +VENDOR(14e6,"SHINING Technology Inc") +ENDVENDOR() + +VENDOR(14e7,"3CX") +ENDVENDOR() + +VENDOR(14e8,"RAYCER Inc") +ENDVENDOR() + +VENDOR(14e9,"GARNETS System CO Ltd") +ENDVENDOR() + +VENDOR(14ea,"PLANEX COMMUNICATIONS Inc") +ENDVENDOR() + +VENDOR(14eb,"SEIKO EPSON Corp") +ENDVENDOR() + +VENDOR(14ec,"ACQIRIS") +ENDVENDOR() + +VENDOR(14ed,"DATAKINETICS Ltd") +ENDVENDOR() + +VENDOR(14ee,"MASPRO KENKOH Corp") +ENDVENDOR() + +VENDOR(14ef,"CARRY Computer ENG. CO Ltd") +ENDVENDOR() + +VENDOR(14f0,"CANON RESEACH CENTRE FRANCE") +ENDVENDOR() + +VENDOR(14f1,"CONEXANT") + DEVICE(14f1,1033,"56K Winmodem") + DEVICE(14f1,1035,"PCI Modem Enumerator") + DEVICE(14f1,2003,"SoftK56 Winmodem") + DEVICE(14f1,2004,"SoftK56 RemoteTAM Winmodem") + DEVICE(14f1,2005,"SoftK56 Speakerphone Winmodem") + DEVICE(14f1,2006,"SoftK56 Speakerphone Winmodem") + DEVICE(14f1,2013,"HSP MicroModem 56K") + DEVICE(14f1,2014,"SoftK56 RemoteTAM Winmodem") + DEVICE(14f1,2015,"SoftK56 Speakerphone Winmodem") + DEVICE(14f1,2016,"SoftK56 Speakerphone Winmodem") +ENDVENDOR() + +VENDOR(14f2,"MOBILITY Electronics") +ENDVENDOR() + +VENDOR(14f3,"BROADLOGIC") +ENDVENDOR() + +VENDOR(14f4,"TOKYO Electronic Industry CO Ltd") +ENDVENDOR() + +VENDOR(14f5,"SOPAC Ltd") +ENDVENDOR() + +VENDOR(14f6,"COYOTE Technologies LLC") +ENDVENDOR() + +VENDOR(14f7,"WOLF Technology Inc") +ENDVENDOR() + +VENDOR(14f8,"AUDIOCODES Inc") +ENDVENDOR() + +VENDOR(14f9,"AG COMMUNICATIONS") +ENDVENDOR() + +VENDOR(14fa,"WANDEL & GOCHERMANN") +ENDVENDOR() + +VENDOR(14fb,"TRANSAS MARINE (UK) Ltd") +ENDVENDOR() + +VENDOR(14fc,"QUADRICS Supercomputers World") +ENDVENDOR() + +VENDOR(14fd,"JAPAN Computer Industry Inc") +ENDVENDOR() + +VENDOR(14fe,"ARCHTEK TELECOM Corp") +ENDVENDOR() + +VENDOR(14ff,"TWINHEAD INTERNATIONAL Corp") +ENDVENDOR() + +VENDOR(1500,"DELTA Electronics, Inc") +ENDVENDOR() + +VENDOR(1501,"BANKSOFT CANADA Ltd") +ENDVENDOR() + +VENDOR(1502,"MITSUBISHI ELECTRIC LOGISTICS SUPPORT Co Ltd") +ENDVENDOR() + +VENDOR(1503,"KAWASAKI LSI USA Inc") +ENDVENDOR() + +VENDOR(1504,"KAISER Electronics") +ENDVENDOR() + +VENDOR(1505,"ITA INGENIEURBURO FUR TESTAUFGABEN GmbH") +ENDVENDOR() + +VENDOR(1506,"CHAMELEON Systems Inc") +ENDVENDOR() + +VENDOR(1507,"Motorola ? / HTEC") + DEVICE(1507,0001,"MPC105 [Eagle]") + DEVICE(1507,0002,"MPC106 [Grackle]") + DEVICE(1507,0003,"MPC8240 [Kahlua]") + DEVICE(1507,0100,"MC145575 [HFC-PCI]") + DEVICE(1507,0431,"KTI829c 100VG") + DEVICE(1507,4801,"Raven") + DEVICE(1507,4802,"Falcon") + DEVICE(1507,4803,"Hawk") + DEVICE(1507,4806,"CPX8216") +ENDVENDOR() + +VENDOR(1508,"HONDA CONNECTORS/MHOTRONICS Inc") +ENDVENDOR() + +VENDOR(1509,"FIRST INTERNATIONAL Computer Inc") +ENDVENDOR() + +VENDOR(150a,"FORVUS RESEARCH Inc") +ENDVENDOR() + +VENDOR(150b,"YAMASHITA Systems Corp") +ENDVENDOR() + +VENDOR(150c,"KYOPAL CO Ltd") +ENDVENDOR() + +VENDOR(150d,"WARPSPPED Inc") +ENDVENDOR() + +VENDOR(150e,"C-PORT Corp") +ENDVENDOR() + +VENDOR(150f,"INTEC GmbH") +ENDVENDOR() + +VENDOR(1510,"BEHAVIOR TECH Computer Corp") +ENDVENDOR() + +VENDOR(1511,"CENTILLIUM Technology Corp") +ENDVENDOR() + +VENDOR(1512,"ROSUN Technologies Inc") +ENDVENDOR() + +VENDOR(1513,"Raychem") +ENDVENDOR() + +VENDOR(1514,"TFL LAN Inc") +ENDVENDOR() + +VENDOR(1515,"Advent design") +ENDVENDOR() + +VENDOR(1516,"MYSON Technology Inc") +ENDVENDOR() + +VENDOR(1517,"ECHOTEK Corp") +ENDVENDOR() + +VENDOR(1518,"PEP MODULAR Computers GmbH") +ENDVENDOR() + +VENDOR(1519,"TELEFON AKTIEBOLAGET LM Ericsson") +ENDVENDOR() + +VENDOR(151a,"Globetek") + DEVICE(151a,1002,"PCI-1002") + DEVICE(151a,1004,"PCI-1004") + DEVICE(151a,1008,"PCI-1008") +ENDVENDOR() + +VENDOR(151b,"COMBOX Ltd") +ENDVENDOR() + +VENDOR(151c,"DIGITAL AUDIO LABS Inc") +ENDVENDOR() + +VENDOR(151d,"Fujitsu Computer Products Of America") +ENDVENDOR() + +VENDOR(151e,"MATRIX Corp") +ENDVENDOR() + +VENDOR(151f,"TOPIC SEMICONDUCTOR Corp") +ENDVENDOR() + +VENDOR(1520,"CHAPLET System Inc") +ENDVENDOR() + +VENDOR(1521,"BELL Corp") +ENDVENDOR() + +VENDOR(1522,"MainPine Ltd") +ENDVENDOR() + +VENDOR(1523,"MUSIC Semiconductors") +ENDVENDOR() + +VENDOR(1524,"ENE Technology Inc") +ENDVENDOR() + +VENDOR(1525,"IMPACT Technologies") +ENDVENDOR() + +VENDOR(1526,"ISS, Inc") +ENDVENDOR() + +VENDOR(1527,"SOLECTRON") +ENDVENDOR() + +VENDOR(1528,"ACKSYS") +ENDVENDOR() + +VENDOR(1529,"AMERICAN MICROSystems Inc") +ENDVENDOR() + +VENDOR(152a,"QUICKTURN DESIGN Systems") +ENDVENDOR() + +VENDOR(152b,"FLYTECH Technology CO Ltd") +ENDVENDOR() + +VENDOR(152c,"MACRAIGOR Systems LLC") +ENDVENDOR() + +VENDOR(152d,"QUANTA Computer Inc") +ENDVENDOR() + +VENDOR(152e,"MELEC Inc") +ENDVENDOR() + +VENDOR(152f,"PHILIPS - CRYPTO") +ENDVENDOR() + +VENDOR(1530,"ACQIS Technology Inc") +ENDVENDOR() + +VENDOR(1531,"CHRYON Corp") +ENDVENDOR() + +VENDOR(1532,"ECHELON Corp") +ENDVENDOR() + +VENDOR(1533,"BALTIMORE") +ENDVENDOR() + +VENDOR(1534,"ROAD Corp") +ENDVENDOR() + +VENDOR(1535,"EVERGREEN Technologies Inc") +ENDVENDOR() + +VENDOR(1537,"DATALEX COMMUNCATIONS") +ENDVENDOR() + +VENDOR(1538,"ARALION Inc") +ENDVENDOR() + +VENDOR(1539,"ATELIER INFORMATIQUES et ELECTRONIQUE ETUDES S.A.") +ENDVENDOR() + +VENDOR(153a,"ONO SOKKI") +ENDVENDOR() + +VENDOR(153b,"TERRATEC Electronic GmbH") +ENDVENDOR() + +VENDOR(153c,"ANTAL Electronic") +ENDVENDOR() + +VENDOR(153d,"FILANET Corp") +ENDVENDOR() + +VENDOR(153e,"TECHWELL Inc") +ENDVENDOR() + +VENDOR(153f,"MIPS DENMARK") +ENDVENDOR() + +VENDOR(1540,"PROVIDEO MULTIMEDIA Co Ltd") +ENDVENDOR() + +VENDOR(1541,"MACHONE Communications") +ENDVENDOR() + +VENDOR(1542,"VIVID Technology Inc") +ENDVENDOR() + +VENDOR(1543,"SILICON Laboratories") +ENDVENDOR() + +VENDOR(1544,"DCM DATA Systems") +ENDVENDOR() + +VENDOR(1545,"VISIONTEK") +ENDVENDOR() + +VENDOR(1546,"IOI Technology Corp") +ENDVENDOR() + +VENDOR(1547,"MITUTOYO Corp") +ENDVENDOR() + +VENDOR(1548,"JET PROPULSION Laboratory") +ENDVENDOR() + +VENDOR(1549,"INTERCONNECT Systems Solutions") +ENDVENDOR() + +VENDOR(154a,"MAX Technologies Inc") +ENDVENDOR() + +VENDOR(154b,"COMPUTEX Co Ltd") +ENDVENDOR() + +VENDOR(154c,"VISUAL Technology Inc") +ENDVENDOR() + +VENDOR(154d,"PAN INTERNATIONAL Industrial Corp") +ENDVENDOR() + +VENDOR(154e,"SERVOTEST Ltd") +ENDVENDOR() + +VENDOR(154f,"STRATABEAM Technology") +ENDVENDOR() + +VENDOR(1550,"OPEN NETWORK Co Ltd") +ENDVENDOR() + +VENDOR(1551,"SMART Electronic DEVELOPMENT GmBH") +ENDVENDOR() + +VENDOR(1552,"RACAL AIRTECH Ltd") +ENDVENDOR() + +VENDOR(1553,"CHICONY Electronics Co Ltd") +ENDVENDOR() + +VENDOR(1554,"PROLINK Microsystems Corp") +ENDVENDOR() + +VENDOR(1555,"GESYTEC GmBH") +ENDVENDOR() + +VENDOR(1556,"PLD APPLICATIONS") +ENDVENDOR() + +VENDOR(1557,"MEDIASTAR Co Ltd") +ENDVENDOR() + +VENDOR(1558,"CLEVO/KAPOK Computer") +ENDVENDOR() + +VENDOR(1559,"SI LOGIC Ltd") +ENDVENDOR() + +VENDOR(155a,"INNOMEDIA Inc") +ENDVENDOR() + +VENDOR(155b,"PROTAC INTERNATIONAL Corp") +ENDVENDOR() + +VENDOR(155c,"Cemax-Icon Inc") +ENDVENDOR() + +VENDOR(155d,"Mac System Co Ltd") +ENDVENDOR() + +VENDOR(155e,"LP Elektronik GmbH") +ENDVENDOR() + +VENDOR(155f,"Perle Systems Ltd") +ENDVENDOR() + +VENDOR(1560,"Terayon Communications Systems") +ENDVENDOR() + +VENDOR(1561,"Viewgraphics Inc") +ENDVENDOR() + +VENDOR(1562,"Symbol Technologies") +ENDVENDOR() + +VENDOR(1563,"A-Trend Technology Co Ltd") +ENDVENDOR() + +VENDOR(1564,"Yamakatsu Electronics Industry Co Ltd") +ENDVENDOR() + +VENDOR(1565,"Biostar Microtech Int'l Corp") +ENDVENDOR() + +VENDOR(1566,"Ardent Technologies Inc") +ENDVENDOR() + +VENDOR(1567,"Jungsoft") +ENDVENDOR() + +VENDOR(1568,"DDK Electronics Inc") +ENDVENDOR() + +VENDOR(1569,"Palit Microsystems Inc.") +ENDVENDOR() + +VENDOR(156a,"Avtec Systems") +ENDVENDOR() + +VENDOR(156b,"2wire Inc") +ENDVENDOR() + +VENDOR(156c,"Vidac Electronics GmbH") +ENDVENDOR() + +VENDOR(156d,"Alpha-Top Corp") +ENDVENDOR() + +VENDOR(156e,"Alfa Inc") +ENDVENDOR() + +VENDOR(156f,"M-Systems Flash Disk Pioneers Ltd") +ENDVENDOR() + +VENDOR(1570,"Lecroy Corp") +ENDVENDOR() + +VENDOR(1571,"Contemporary Controls") + DEVICE(1571,a001,"CCSI PCI20-485 ARCnet") + DEVICE(1571,a002,"CCSI PCI20-485D ARCnet") + DEVICE(1571,a003,"CCSI PCI20-485X ARCnet") + DEVICE(1571,a004,"CCSI PCI20-CXB ARCnet") + DEVICE(1571,a005,"CCSI PCI20-CXS ARCnet") + DEVICE(1571,a006,"CCSI PCI20-FOG-SMA ARCnet") + DEVICE(1571,a007,"CCSI PCI20-FOG-ST ARCnet") + DEVICE(1571,a008,"CCSI PCI20-TB5 ARCnet") + DEVICE(1571,a009,"CCSI PCI20-5-485 5Mbit ARCnet") + DEVICE(1571,a00a,"CCSI PCI20-5-485D 5Mbit ARCnet") + DEVICE(1571,a00b,"CCSI PCI20-5-485X 5Mbit ARCnet") + DEVICE(1571,a00c,"CCSI PCI20-5-FOG-ST 5Mbit ARCnet") + DEVICE(1571,a00d,"CCSI PCI20-5-FOG-SMA 5Mbit ARCnet") + DEVICE(1571,a201,"CCSI PCI22-485 10Mbit ARCnet") + DEVICE(1571,a202,"CCSI PCI22-485D 10Mbit ARCnet") + DEVICE(1571,a203,"CCSI PCI22-485X 10Mbit ARCnet") + DEVICE(1571,a204,"CCSI PCI22-CHB 10Mbit ARCnet") + DEVICE(1571,a205,"CCSI PCI22-FOG_ST 10Mbit ARCnet") + DEVICE(1571,a206,"CCSI PCI22-THB 10Mbit ARCnet") +ENDVENDOR() + +VENDOR(1572,"Otis Elevator Company") +ENDVENDOR() + +VENDOR(1573,"Lattice - Vantis") +ENDVENDOR() + +VENDOR(1574,"Fairchild Semiconductor") +ENDVENDOR() + +VENDOR(1575,"Voltaire Advanced Data Security Ltd") +ENDVENDOR() + +VENDOR(1576,"Viewcast COM") +ENDVENDOR() + +VENDOR(1578,"HITT") +ENDVENDOR() + +VENDOR(1579,"Dual Technology Corp") +ENDVENDOR() + +VENDOR(157a,"Japan Elecronics Ind Inc") +ENDVENDOR() + +VENDOR(157b,"Star Multimedia Corp") +ENDVENDOR() + +VENDOR(157c,"Eurosoft (UK)") + DEVICE(157c,8001,"Fix2000 PCI Y2K Compliance Card") +ENDVENDOR() + +VENDOR(157d,"Gemflex Networks") +ENDVENDOR() + +VENDOR(157e,"Transition Networks") +ENDVENDOR() + +VENDOR(157f,"PX Instruments Technology Ltd") +ENDVENDOR() + +VENDOR(1580,"Primex Aerospace Co") +ENDVENDOR() + +VENDOR(1581,"SEH Computertechnik GmbH") +ENDVENDOR() + +VENDOR(1582,"Cytec Corp") +ENDVENDOR() + +VENDOR(1583,"Inet Technologies Inc") +ENDVENDOR() + +VENDOR(1584,"Uniwill Computer Corp") +ENDVENDOR() + +VENDOR(1585,"Logitron") +ENDVENDOR() + +VENDOR(1586,"Lancast Inc") +ENDVENDOR() + +VENDOR(1587,"Konica Corp") +ENDVENDOR() + +VENDOR(1588,"Solidum Systems Corp") +ENDVENDOR() + +VENDOR(1589,"Atlantek Microsystems Pty Ltd") +ENDVENDOR() + +VENDOR(158a,"Digalog Systems Inc") +ENDVENDOR() + +VENDOR(158b,"Allied Data Technologies") +ENDVENDOR() + +VENDOR(158c,"Hitachi Semiconductor & Devices Sales Co Ltd") +ENDVENDOR() + +VENDOR(158d,"Point Multimedia Systems") +ENDVENDOR() + +VENDOR(158e,"Lara Technology Inc") +ENDVENDOR() + +VENDOR(158f,"Ditect Coop") +ENDVENDOR() + +VENDOR(1590,"3pardata Inc") +ENDVENDOR() + +VENDOR(1591,"ARN") +ENDVENDOR() + +VENDOR(1592,"Syba Tech Ltd") + DEVICE(1592,0781,"Multi-IO Card") + DEVICE(1592,0782,"Parallel Port Card 2xEPP") + DEVICE(1592,0783,"Multi-IO Card") + DEVICE(1592,0785,"Multi-IO Card") + DEVICE(1592,0786,"Multi-IO Card") + DEVICE(1592,0787,"Multi-IO Card") + DEVICE(1592,0788,"Multi-IO Card") + DEVICE(1592,078a,"Multi-IO Card") +ENDVENDOR() + +VENDOR(1593,"Bops Inc") +ENDVENDOR() + +VENDOR(1594,"Netgame Ltd") +ENDVENDOR() + +VENDOR(1595,"Diva Systems Corp") +ENDVENDOR() + +VENDOR(1596,"Folsom Research Inc") +ENDVENDOR() + +VENDOR(1597,"Memec Design Services") +ENDVENDOR() + +VENDOR(1598,"Granite Microsystems") +ENDVENDOR() + +VENDOR(1599,"Delta Electronics Inc") +ENDVENDOR() + +VENDOR(159a,"General Instrument") +ENDVENDOR() + +VENDOR(159b,"Faraday Technology Corp") +ENDVENDOR() + +VENDOR(159c,"Stratus Computer Systems") +ENDVENDOR() + +VENDOR(159d,"Ningbo Harrison Electronics Co Ltd") +ENDVENDOR() + +VENDOR(159e,"A-Max Technology Co Ltd") +ENDVENDOR() + +VENDOR(159f,"Galea Network Security") +ENDVENDOR() + +VENDOR(15a0,"Compumaster SRL") +ENDVENDOR() + +VENDOR(15a1,"Geocast Network Systems") +ENDVENDOR() + +VENDOR(15a2,"Catalyst Enterprises Inc") +ENDVENDOR() + +VENDOR(15a3,"Italtel") +ENDVENDOR() + +VENDOR(15a4,"X-Net OY") +ENDVENDOR() + +VENDOR(15a5,"Toyota Macs Inc") +ENDVENDOR() + +VENDOR(15a6,"Sunlight Ultrasound Technologies Ltd") +ENDVENDOR() + +VENDOR(15a7,"SSE Telecom Inc") +ENDVENDOR() + +VENDOR(15a8,"Shanghai Communications Technologies Center") +ENDVENDOR() + +VENDOR(15aa,"Moreton Bay") +ENDVENDOR() + +VENDOR(15ab,"Bluesteel Networks Inc") +ENDVENDOR() + +VENDOR(15ac,"North Atlantic Instruments") +ENDVENDOR() + +VENDOR(15ad,"VMWare Inc") + DEVICE(15ad,0710,"Virtual SVGA") +ENDVENDOR() + +VENDOR(15ae,"Amersham Pharmacia Biotech") +ENDVENDOR() + +VENDOR(15b0,"Zoltrix International Ltd") +ENDVENDOR() + +VENDOR(15b1,"Source Technology Inc") +ENDVENDOR() + +VENDOR(15b2,"Mosaid Technologies Inc") +ENDVENDOR() + +VENDOR(15b3,"Mellanox Technology") +ENDVENDOR() + +VENDOR(15b4,"CCI/TRIAD") +ENDVENDOR() + +VENDOR(15b5,"Cimetrics Inc") +ENDVENDOR() + +VENDOR(15b6,"Texas Memory Systems Inc") +ENDVENDOR() + +VENDOR(15b7,"Sandisk Corp") +ENDVENDOR() + +VENDOR(15b8,"ADDI-DATA GmbH") +ENDVENDOR() + +VENDOR(15b9,"Maestro Digital Communications") +ENDVENDOR() + +VENDOR(15ba,"Impacct Technology Corp") +ENDVENDOR() + +VENDOR(15bb,"Portwell Inc") +ENDVENDOR() + +VENDOR(15bc,"Agilent Technologies") +ENDVENDOR() + +VENDOR(15bd,"DFI Inc") +ENDVENDOR() + +VENDOR(15be,"Sola Electronics") +ENDVENDOR() + +VENDOR(15bf,"High Tech Computer Corp (HTC)") +ENDVENDOR() + +VENDOR(15c0,"BVM Ltd") +ENDVENDOR() + +VENDOR(15c1,"Quantel") +ENDVENDOR() + +VENDOR(15c2,"Newer Technology Inc") +ENDVENDOR() + +VENDOR(15c3,"Taiwan Mycomp Co Ltd") +ENDVENDOR() + +VENDOR(15c4,"EVSX Inc") +ENDVENDOR() + +VENDOR(15c5,"Procomp Informatics Ltd") +ENDVENDOR() + +VENDOR(15c6,"Technical University of Budapest") +ENDVENDOR() + +VENDOR(15c7,"Tateyama Dystem Laboratory Co Ltd") +ENDVENDOR() + +VENDOR(15c8,"Penta Media Co Ltd") +ENDVENDOR() + +VENDOR(15c9,"Serome Technology Inc") +ENDVENDOR() + +VENDOR(15ca,"Bitboys OY") +ENDVENDOR() + +VENDOR(15cb,"AG Electronics Ltd") +ENDVENDOR() + +VENDOR(15cc,"Hotrail Inc") +ENDVENDOR() + +VENDOR(15cd,"Dreamtech Co Ltd") +ENDVENDOR() + +VENDOR(15ce,"Genrad Inc") +ENDVENDOR() + +VENDOR(15cf,"Hilscher GmbH") +ENDVENDOR() + +VENDOR(15d1,"Infineon Technologies AG") +ENDVENDOR() + +VENDOR(15d2,"FIC (First International Computer Inc)") +ENDVENDOR() + +VENDOR(15d3,"NDS Technologies Israel Ltd") +ENDVENDOR() + +VENDOR(15d4,"Iwill Corp") +ENDVENDOR() + +VENDOR(15d5,"Tatung Co") +ENDVENDOR() + +VENDOR(15d6,"Entridia Corp") +ENDVENDOR() + +VENDOR(15d7,"Rockwell-Collins Inc") +ENDVENDOR() + +VENDOR(15d8,"Cybernetics Technology Co Ltd") +ENDVENDOR() + +VENDOR(15d9,"Super Micro Computer Inc") +ENDVENDOR() + +VENDOR(15da,"Cyberfirm Inc") +ENDVENDOR() + +VENDOR(15db,"Applied Computing Systems Inc") +ENDVENDOR() + +VENDOR(15dc,"Litronic Inc") + DEVICE(15dc,0001,"Argus 300 PCI Cryptography Module") +ENDVENDOR() + +VENDOR(15dd,"Sigmatel Inc") +ENDVENDOR() + +VENDOR(15de,"Malleable Technologies Inc") +ENDVENDOR() + +VENDOR(15df,"Infinilink Corp") +ENDVENDOR() + +VENDOR(15e0,"Cacheflow Inc") +ENDVENDOR() + +VENDOR(15e1,"Voice Technologies Group Inc") +ENDVENDOR() + +VENDOR(15e2,"Quicknet Technologies Inc") +ENDVENDOR() + +VENDOR(15e3,"Networth Technologies Inc") +ENDVENDOR() + +VENDOR(15e4,"VSN Systemen BV") +ENDVENDOR() + +VENDOR(15e5,"Valley technologies Inc") +ENDVENDOR() + +VENDOR(15e6,"Agere Inc") +ENDVENDOR() + +VENDOR(15e7,"Get Engineering Corp") +ENDVENDOR() + +VENDOR(15e8,"National Datacomm Corp") +ENDVENDOR() + +VENDOR(15e9,"Pacific Digital Corp") +ENDVENDOR() + +VENDOR(15ea,"Tokyo Denshi Sekei K.K.") +ENDVENDOR() + +VENDOR(15eb,"Drsearch GmbH") +ENDVENDOR() + +VENDOR(15ec,"Beckhoff GmbH") +ENDVENDOR() + +VENDOR(15ed,"Macrolink Inc") +ENDVENDOR() + +VENDOR(15ee,"In Win Development Inc") +ENDVENDOR() + +VENDOR(15ef,"Intelligent Paradigm Inc") +ENDVENDOR() + +VENDOR(15f0,"B-Tree Systems Inc") +ENDVENDOR() + +VENDOR(15f1,"Times N Systems Inc") +ENDVENDOR() + +VENDOR(15f2,"Diagnostic Instruments Inc") +ENDVENDOR() + +VENDOR(15f3,"Digitmedia Corp") +ENDVENDOR() + +VENDOR(15f4,"Valuesoft") +ENDVENDOR() + +VENDOR(15f5,"Power Micro Research") +ENDVENDOR() + +VENDOR(15f6,"Extreme Packet Device Inc") +ENDVENDOR() + +VENDOR(15f7,"Banctec") +ENDVENDOR() + +VENDOR(15f8,"Koga Electronics Co") +ENDVENDOR() + +VENDOR(15f9,"Zenith Electronics Corp") +ENDVENDOR() + +VENDOR(15fa,"J.P. Axzam Corp") +ENDVENDOR() + +VENDOR(15fb,"Zilog Inc") +ENDVENDOR() + +VENDOR(15fc,"Techsan Electronics Co Ltd") +ENDVENDOR() + +VENDOR(15fd,"N-CUBED.NET") +ENDVENDOR() + +VENDOR(15fe,"Kinpo Electronics Inc") +ENDVENDOR() + +VENDOR(15ff,"Fastpoint Technologies Inc") +ENDVENDOR() + +VENDOR(1600,"Northrop Grumman - Canada Ltd") +ENDVENDOR() + +VENDOR(1601,"Tenta Technology") +ENDVENDOR() + +VENDOR(1602,"Prosys-tec Inc") +ENDVENDOR() + +VENDOR(1603,"Nokia Wireless Communications") +ENDVENDOR() + +VENDOR(1604,"Central System Research Co Ltd") +ENDVENDOR() + +VENDOR(1605,"Pairgain Technologies") +ENDVENDOR() + +VENDOR(1606,"Europop AG") +ENDVENDOR() + +VENDOR(1607,"Lava Semiconductor Manufacturing Inc") +ENDVENDOR() + +VENDOR(1608,"Automated Wagering International") +ENDVENDOR() + +VENDOR(1609,"Scimetric Instruments Inc") +ENDVENDOR() + +VENDOR(1668,"Action Tec Electronics Inc") +ENDVENDOR() + +VENDOR(1813,"Ambient Technologies Inc") +ENDVENDOR() + +VENDOR(1a08,"Sierra semiconductor") + DEVICE(1a08,0000,"SC15064") +ENDVENDOR() + +VENDOR(1b13,"Jaton Corp") +ENDVENDOR() + +VENDOR(1c1c,"Symphony") + DEVICE(1c1c,0001,"82C101") +ENDVENDOR() + +VENDOR(1d44,"DPT") + DEVICE(1d44,a400,"PM2x24/PM3224") +ENDVENDOR() + +VENDOR(1de1,"Tekram Technology Co.,Ltd.") + DEVICE(1de1,0391,"TRM-S1040") + DEVICE(1de1,2020,"DC-390") + DEVICE(1de1,690c,"690c") + DEVICE(1de1,dc29,"DC290") +ENDVENDOR() + +VENDOR(2001,"Temporal Research Ltd") +ENDVENDOR() + +VENDOR(21c3,"21st Century Computer Corp.") +ENDVENDOR() + +VENDOR(2348,"Racore") + DEVICE(2348,2010,"8142 100VG/AnyLAN") +ENDVENDOR() + +VENDOR(2646,"Kingston Technologies") +ENDVENDOR() + +VENDOR(270b,"Xantel Corporation") +ENDVENDOR() + +VENDOR(270f,"Chaintech Computer Co. Ltd") +ENDVENDOR() + +VENDOR(2711,"AVID Technology Inc.") +ENDVENDOR() + +VENDOR(2a15,"3D Vision(?)") +ENDVENDOR() + +VENDOR(3000,"Hansol Electronics Inc.") +ENDVENDOR() + +VENDOR(3142,"Post Impression Systems.") +ENDVENDOR() + +VENDOR(3388,"Hint Corp") + DEVICE(3388,8011,"VXPro II Chipset") + DEVICE(3388,8012,"VXPro II Chipset") + DEVICE(3388,8013,"VXPro II Chipset") +ENDVENDOR() + +VENDOR(3411,"Quantum Designs (H.K.) Inc") +ENDVENDOR() + +VENDOR(3513,"ARCOM Control Systems Ltd") +ENDVENDOR() + +VENDOR(38ef,"4Links") +ENDVENDOR() + +VENDOR(3d3d,"3DLabs") + DEVICE(3d3d,0001,"GLINT 300SX") + DEVICE(3d3d,0002,"GLINT 500TX") + DEVICE(3d3d,0003,"GLINT Delta") + DEVICE(3d3d,0004,"Permedia") + DEVICE(3d3d,0005,"Permedia") + DEVICE(3d3d,0006,"GLINT MX") + DEVICE(3d3d,0007,"3D Extreme") + DEVICE(3d3d,0008,"GLINT Gamma G1") + DEVICE(3d3d,0009,"Permedia II 2D+3D") + DEVICE(3d3d,000a,"GLINT R3") + DEVICE(3d3d,0100,"Permedia II 2D+3D") + DEVICE(3d3d,1004,"Permedia") + DEVICE(3d3d,3d04,"Permedia") + DEVICE(3d3d,ffff,"Glint VGA") +ENDVENDOR() + +VENDOR(4005,"Avance Logic Inc.") + DEVICE(4005,0300,"ALS300 PCI Audio Device") + DEVICE(4005,0308,"ALS300+ PCI Audio Device") + DEVICE(4005,0309,"PCI Input Controller") + DEVICE(4005,1064,"ALG-2064") + DEVICE(4005,2064,"ALG-2064i") + DEVICE(4005,2128,"ALG-2364A GUI Accelerator") + DEVICE(4005,2301,"ALG-2301") + DEVICE(4005,2302,"ALG-2302") + DEVICE(4005,2303,"AVG-2302 GUI Accelerator") + DEVICE(4005,2364,"ALG-2364A") + DEVICE(4005,2464,"ALG-2464") + DEVICE(4005,2501,"ALG-2564A/25128A") + DEVICE(4005,4000,"ALS4000 Audio Chipset") +ENDVENDOR() + +VENDOR(4033,"Addtron Technology Co, Inc.") +ENDVENDOR() + +VENDOR(4143,"Digital Equipment Corp") +ENDVENDOR() + +VENDOR(416c,"Aladdin Knowledge Systems") +ENDVENDOR() + +VENDOR(4444,"Internext Compression Inc") +ENDVENDOR() + +VENDOR(4468,"Bridgeport machines") +ENDVENDOR() + +VENDOR(4594,"Cogetec Informatique Inc") +ENDVENDOR() + +VENDOR(45fb,"Baldor Electric Company") +ENDVENDOR() + +VENDOR(4680,"Umax Computer Corp") +ENDVENDOR() + +VENDOR(4843,"Hercules Computer Technology Inc") +ENDVENDOR() + +VENDOR(4916,"RedCreek Communications Inc") + DEVICE(4916,1960,"RedCreek PCI adapter") +ENDVENDOR() + +VENDOR(4943,"Growth Networks") +ENDVENDOR() + +VENDOR(4978,"Axil Computer Inc") +ENDVENDOR() + +VENDOR(4a14,"NetVin") + DEVICE(4a14,5000,"NV5000SC") +ENDVENDOR() + +VENDOR(4b10,"Buslogic Inc.") +ENDVENDOR() + +VENDOR(4c48,"LUNG HWA Electronics") +ENDVENDOR() + +VENDOR(4ca1,"Seanix Technology Inc") +ENDVENDOR() + +VENDOR(4d51,"MediaQ Inc.") + DEVICE(4d51,0200,"MQ-200") +ENDVENDOR() + +VENDOR(4d54,"Microtechnica Co Ltd") +ENDVENDOR() + +VENDOR(4ddc,"ILC Data Device Corp") +ENDVENDOR() + +VENDOR(5053,"Voyetra Technologies") + DEVICE(5053,2010,"Daytona Audio Adapter") +ENDVENDOR() + +VENDOR(5136,"S S Technologies") +ENDVENDOR() + +VENDOR(5143,"Qualcomm Inc") +ENDVENDOR() + +VENDOR(5145,"Ensoniq (Old)") + DEVICE(5145,3031,"Concert AudioPCI") +ENDVENDOR() + +VENDOR(5301,"Alliance Semiconductor Corp.") + DEVICE(5301,0001,"ProMotion aT3D") +ENDVENDOR() + +VENDOR(5333,"S3 Inc.") + DEVICE(5333,0551,"Plato/PX (system)") + DEVICE(5333,5631,"86c325 [ViRGE]") + DEVICE(5333,8800,"86c866 [Vision 866]") + DEVICE(5333,8801,"86c964 [Vision 964]") + DEVICE(5333,8810,"86c764_0 [Trio 32 vers 0]") + DEVICE(5333,8811,"86c764/765 [Trio32/64/64V+]") + DEVICE(5333,8812,"86cM65 [Aurora64V+]") + DEVICE(5333,8813,"86c764_3 [Trio 32/64 vers 3]") + DEVICE(5333,8814,"86c767 [Trio 64UV+]") + DEVICE(5333,8815,"86cM65 [Aurora 128]") + DEVICE(5333,883d,"86c988 [ViRGE/VX]") + DEVICE(5333,8870,"FireGL") + DEVICE(5333,8880,"86c868 [Vision 868 VRAM] vers 0") + DEVICE(5333,8881,"86c868 [Vision 868 VRAM] vers 1") + DEVICE(5333,8882,"86c868 [Vision 868 VRAM] vers 2") + DEVICE(5333,8883,"86c868 [Vision 868 VRAM] vers 3") + DEVICE(5333,88b0,"86c928 [Vision 928 VRAM] vers 0") + DEVICE(5333,88b1,"86c928 [Vision 928 VRAM] vers 1") + DEVICE(5333,88b2,"86c928 [Vision 928 VRAM] vers 2") + DEVICE(5333,88b3,"86c928 [Vision 928 VRAM] vers 3") + DEVICE(5333,88c0,"86c864 [Vision 864 DRAM] vers 0") + DEVICE(5333,88c1,"86c864 [Vision 864 DRAM] vers 1") + DEVICE(5333,88c2,"86c864 [Vision 864-P DRAM] vers 2") + DEVICE(5333,88c3,"86c864 [Vision 864-P DRAM] vers 3") + DEVICE(5333,88d0,"86c964 [Vision 964 VRAM] vers 0") + DEVICE(5333,88d1,"86c964 [Vision 964 VRAM] vers 1") + DEVICE(5333,88d2,"86c964 [Vision 964-P VRAM] vers 2") + DEVICE(5333,88d3,"86c964 [Vision 964-P VRAM] vers 3") + DEVICE(5333,88f0,"86c968 [Vision 968 VRAM] rev 0") + DEVICE(5333,88f1,"86c968 [Vision 968 VRAM] rev 1") + DEVICE(5333,88f2,"86c968 [Vision 968 VRAM] rev 2") + DEVICE(5333,88f3,"86c968 [Vision 968 VRAM] rev 3") + DEVICE(5333,8900,"86c755 [Trio 64V2/DX]") + DEVICE(5333,8901,"Trio 64V2/DX or /GX") + DEVICE(5333,8902,"Plato/PX") + DEVICE(5333,8903,"Trio 3D business multimedia") + DEVICE(5333,8904,"Trio 64 3D") + DEVICE(5333,8905,"Trio 64V+ family") + DEVICE(5333,8906,"Trio 64V+ family") + DEVICE(5333,8907,"Trio 64V+ family") + DEVICE(5333,8908,"Trio 64V+ family") + DEVICE(5333,8909,"Trio 64V+ family") + DEVICE(5333,890a,"Trio 64V+ family") + DEVICE(5333,890b,"Trio 64V+ family") + DEVICE(5333,890c,"Trio 64V+ family") + DEVICE(5333,890d,"Trio 64V+ family") + DEVICE(5333,890e,"Trio 64V+ family") + DEVICE(5333,890f,"Trio 64V+ family") + DEVICE(5333,8a01,"ViRGE/DX or /GX") + DEVICE(5333,8a10,"ViRGE/GX2") + DEVICE(5333,8a13,"86c368 [Trio 3D/2X]") + DEVICE(5333,8a20,"86c794 [Savage 3D]") + DEVICE(5333,8a21,"86c795 [Savage 3D/MV]") + DEVICE(5333,8a22,"Savage 4") + DEVICE(5333,8a23,"Savage 4") + DEVICE(5333,8c00,"ViRGE/M3") + DEVICE(5333,8c01,"ViRGE/MX") + DEVICE(5333,8c02,"ViRGE/MX+") + DEVICE(5333,8c03,"ViRGE/MX+MV") + DEVICE(5333,8c10,"86C270-294 Savage/MX-/IX") + DEVICE(5333,8c12,"86C270-294 Savage/MX-/IX") + DEVICE(5333,9102,"86C410 Savage 2000") + DEVICE(5333,ca00,"SonicVibes") +ENDVENDOR() + +VENDOR(544c,"Teralogic Inc") +ENDVENDOR() + +VENDOR(5455,"Technische University Berlin") + DEVICE(5455,4458,"S5933") +ENDVENDOR() + +VENDOR(5519,"Cnet Technologies, Inc.") +ENDVENDOR() + +VENDOR(5555,"Genroco, Inc") + DEVICE(5555,0003,"TURBOstor HFP-832 [HiPPI NIC]") +ENDVENDOR() + +VENDOR(5700,"Netpower") +ENDVENDOR() + +VENDOR(6356,"UltraStor") +ENDVENDOR() + +VENDOR(6374,"c't Magazin für Computertechnik") + DEVICE(6374,6773,"GPPCI") +ENDVENDOR() + +VENDOR(6409,"Logitec Corp.") +ENDVENDOR() + +VENDOR(6666,"Decision Computer International Co.") + DEVICE(6666,0001,"PCCOM4") + DEVICE(6666,0002,"PCCOM8") +ENDVENDOR() + +VENDOR(7604,"O.N. Electronic Co Ltd.") +ENDVENDOR() + +VENDOR(7bde,"MIDAC Corporation") +ENDVENDOR() + +VENDOR(7fed,"PowerTV") +ENDVENDOR() + +VENDOR(8008,"Quancom Electronic GmbH") + DEVICE(8008,0010,"WDOG1 [PCI-Watchdog 1]") + DEVICE(8008,0011,"PWDOG2 [PCI-Watchdog 2]") +ENDVENDOR() + +VENDOR(8086,"Intel Corporation") + DEVICE(8086,0007,"82379AB") + DEVICE(8086,0039,"21145") + DEVICE(8086,0122,"82437FX") + DEVICE(8086,0482,"82375EB") + DEVICE(8086,0483,"82424ZX [Saturn]") + DEVICE(8086,0484,"82378IB [SIO ISA Bridge]") + DEVICE(8086,0486,"82430ZX [Aries]") + DEVICE(8086,04a3,"82434LX [Mercury/Neptune]") + DEVICE(8086,04d0,"82437FX [Triton FX]") + DEVICE(8086,0960,"80960RP [i960 RP Microprocessor/Bridge]") + DEVICE(8086,1000,"82542 Gigabit Ethernet Adapter") + DEVICE(8086,1030,"82559 InBusiness 10/100") + DEVICE(8086,1161,"82806AA PCI64 Hub Advanced Programmable Interrupt Controller") + DEVICE(8086,1209,"82559ER") + DEVICE(8086,1221,"82092AA_0") + DEVICE(8086,1222,"82092AA_1") + DEVICE(8086,1223,"SAA7116") + DEVICE(8086,1225,"82452KX/GX [Orion]") + DEVICE(8086,1226,"82596") + DEVICE(8086,1227,"82865 [Ether Express Pro 100]") + DEVICE(8086,1228,"82556 [Ether Express Pro 100 Smart]") + DEVICE(8086,1229,"82557 [Ethernet Pro 100]") + DEVICE(8086,122d,"430FX - 82437FX TSC [Triton I]") + DEVICE(8086,122e,"82371FB PIIX ISA [Triton I]") + DEVICE(8086,1230,"82371FB PIIX IDE [Triton I]") + DEVICE(8086,1231,"DSVD Modem") + DEVICE(8086,1234,"430MX - 82371MX MPIIX") + DEVICE(8086,1235,"430MX - 82437MX MTSC") + DEVICE(8086,1237,"440FX - 82441FX PMC [Natoma]") + DEVICE(8086,1239,"82371FB") + DEVICE(8086,123b,"82380PB") + DEVICE(8086,123c,"82380AB") + DEVICE(8086,123d,"683053 Programmable Interrupt Device") + DEVICE(8086,1240,"752 AGP") + DEVICE(8086,124b,"82380FB") + DEVICE(8086,1250,"430HX - 82439HX TXC [Triton II]") + DEVICE(8086,1360,"82806AA PCI64 Hub PCI Bridge") + DEVICE(8086,1361,"82806AA PCI64 Hub Controller (HRes)") + DEVICE(8086,1960,"80960RP [i960RP Microprocessor]") + DEVICE(8086,1a21,"82840 840 (Carmel) Chipset Host Bridge (Hub A)") + DEVICE(8086,1a23,"82840 840 (Carmel) Chipset AGP Bridge") + DEVICE(8086,1a24,"82840 840 (Carmel) Chipset PCI Bridge (Hub B)") + DEVICE(8086,2410,"82801AA ISA Bridge (LPC)") + DEVICE(8086,2411,"82801AA IDE") + DEVICE(8086,2412,"82801AA USB") + DEVICE(8086,2413,"82801AA SMBus") + DEVICE(8086,2415,"82801AA AC'97 Audio") + DEVICE(8086,2416,"82801AA AC'97 Modem") + DEVICE(8086,2418,"82801AA PCI Bridge") + DEVICE(8086,2420,"82801AB ISA Bridge (LPC)") + DEVICE(8086,2421,"82801AB IDE") + DEVICE(8086,2422,"82801AB USB") + DEVICE(8086,2423,"82801AB SMBus") + DEVICE(8086,2425,"82801AB AC'97 Audio") + DEVICE(8086,2426,"82801AB AC'97 Modem") + DEVICE(8086,2428,"82801AB PCI Bridge") + DEVICE(8086,2440,"82801BA ISA Bridge (ICH2)") + DEVICE(8086,2442,"82801BA(M) USB (Hub A)") + DEVICE(8086,2443,"82801BA(M) SMBus") + DEVICE(8086,2444,"82801BA(M) USB (Hub B)") + DEVICE(8086,2445,"82801BA(M) AC'97 Audio") + DEVICE(8086,2446,"82801BA(M) AC'97 Modem") + DEVICE(8086,2448,"82801BA PCI") + DEVICE(8086,2449,"82801BA(M) Ethernet") + DEVICE(8086,244a,"82801BAM IDE U100") + DEVICE(8086,244b,"82801BA IDE U100") + DEVICE(8086,244c,"82801BAM ISA Bridge (ICH2)") + DEVICE(8086,244e,"82801BAM PCI") + DEVICE(8086,2500,"82820 820 (Camino) Chipset Host Bridge (MCH)") + DEVICE(8086,2501,"82820 820 (Camino) Chipset Host Bridge (MCH)") + DEVICE(8086,250b,"82820 820 (Camino) Chipset Host Bridge") + DEVICE(8086,250f,"82820 820 (Camino) Chipset PCI to AGP Bridge") + DEVICE(8086,2520,"82805AA MTH Memory Translator Hub") + DEVICE(8086,2521,"82804AA MRH-S Memory Repeater Hub for SDRAM") + DEVICE(8086,2530,"82850 850 (Tehama) Chipset Host Bridge (MCH)") + DEVICE(8086,2532,"82850 850 (Tehama) Chipset AGP Bridge") + DEVICE(8086,5200,"EtherExpress PRO/100") + DEVICE(8086,5201,"EtherExpress PRO/100") + DEVICE(8086,7000,"82371SB PIIX3 ISA [Natoma/Triton II]") + DEVICE(8086,7010,"82371SB PIIX3 IDE [Natoma/Triton II]") + DEVICE(8086,7020,"82371SB PIIX3 USB [Natoma/Triton II]") + DEVICE(8086,7030,"430VX - 82437VX TVX [Triton VX]") + DEVICE(8086,7100,"430TX - 82439TX MTXC") + DEVICE(8086,7110,"82371AB PIIX4 ISA") + DEVICE(8086,7111,"82371AB PIIX4 IDE") + DEVICE(8086,7112,"82371AB PIIX4 USB") + DEVICE(8086,7113,"82371AB PIIX4 ACPI") + DEVICE(8086,7120,"82810 GMCH [Graphics Memory Controller Hub]") + DEVICE(8086,7121,"82810 CGC [Chipset Graphics Controller]") + DEVICE(8086,7122,"82810-DC100 GMCH [Graphics Memory Controller Hub]") + DEVICE(8086,7123,"82810-DC100 CGC [Chipset Graphics Controller]") + DEVICE(8086,7124,"82810E GMCH [Graphics Memory Controller Hub]") + DEVICE(8086,7125,"82810E CGC [Chipset Graphics Controller]") + DEVICE(8086,7126,"82810 810 Chipset Host Bridge and Memory Controller Hub") + DEVICE(8086,7180,"440LX/EX - 82443LX/EX Host bridge") + DEVICE(8086,7181,"440LX/EX - 82443LX/EX AGP bridge") + DEVICE(8086,7190,"440BX/ZX - 82443BX/ZX Host bridge") + DEVICE(8086,7191,"440BX/ZX - 82443BX/ZX AGP bridge") + DEVICE(8086,7192,"440BX/ZX - 82443BX/ZX Host bridge (AGP disabled)") + DEVICE(8086,7194,"82440MX I/O Controller") + DEVICE(8086,7195,"82440MX AC'97 Audio Controller") + DEVICE(8086,7198,"82440MX PCI to ISA Bridge") + DEVICE(8086,7199,"82440MX EIDE Controller") + DEVICE(8086,719a,"82440MX USB Universal Host Controller") + DEVICE(8086,719b,"82440MX Power Management Controller") + DEVICE(8086,71a0,"440GX - 82443GX Host bridge") + DEVICE(8086,71a1,"440GX - 82443GX AGP bridge") + DEVICE(8086,71a2,"440GX - 82443GX Host bridge (AGP disabled)") + DEVICE(8086,7600,"82372FB PCI to ISA Bridge") + DEVICE(8086,7601,"82372FB PIIX4 IDE") + DEVICE(8086,7602,"82372FB [PCI-to-USB UHCI]") + DEVICE(8086,7603,"82372FB System Management Bus Controller") + DEVICE(8086,7800,"i740") + DEVICE(8086,84c4,"450KX/GX [Orion] - 82454KX/GX PCI bridge") + DEVICE(8086,84c5,"450KX/GX [Orion] - 82453KX/GX Memory controller") + DEVICE(8086,84ca,"450NX - 82451NX Memory & I/O Controller") + DEVICE(8086,84cb,"450NX - 82454NX/84460GX PCI Expander Bridge") + DEVICE(8086,84e0,"460GX - 84460GX System Address Controller (SAC)") + DEVICE(8086,84e1,"460GX - 84460GX System Data Controller (SDC)") + DEVICE(8086,84e2,"460GX - 84460GX AGP Bridge (GXB)") + DEVICE(8086,84e3,"460GX - 84460GX Memory Address Controller (MAC)") + DEVICE(8086,84e4,"460GX - 84460GX Memory Data Controller (MDC)") + DEVICE(8086,ffff,"450NX/GX [Orion] - 82453KX/GX Memory controller [BUG]") +ENDVENDOR() + +VENDOR(8800,"Trigem Computer Inc.") + DEVICE(8800,2008,"Video assistent component") +ENDVENDOR() + +VENDOR(8866,"T-Square Design Inc.") +ENDVENDOR() + +VENDOR(8888,"Silicon Magic") +ENDVENDOR() + +VENDOR(8e0e,"Computone Corporation") +ENDVENDOR() + +VENDOR(8e2e,"KTI") + DEVICE(8e2e,3000,"ET32P2") +ENDVENDOR() + +VENDOR(9004,"Adaptec") + DEVICE(9004,1078,"AIC-7810") + DEVICE(9004,1160,"AIC-1160 [Family Fiber Channel Adapter]") + DEVICE(9004,2178,"AIC-7821") + DEVICE(9004,3860,"AHA-2930CU") + DEVICE(9004,3b78,"AHA-4844W/4844UW") + DEVICE(9004,5075,"AIC-755x") + DEVICE(9004,5078,"AHA-7850") + DEVICE(9004,5175,"AIC-755x") + DEVICE(9004,5178,"AIC-7851") + DEVICE(9004,5275,"AIC-755x") + DEVICE(9004,5278,"AIC-7852") + DEVICE(9004,5375,"AIC-755x") + DEVICE(9004,5378,"AIC-7850") + DEVICE(9004,5475,"AIC-2930") + DEVICE(9004,5478,"AIC-7850") + DEVICE(9004,5575,"AVA-2930") + DEVICE(9004,5578,"AIC-7855") + DEVICE(9004,5675,"AIC-755x") + DEVICE(9004,5678,"AIC-7850") + DEVICE(9004,5775,"AIC-755x") + DEVICE(9004,5778,"AIC-7850") + DEVICE(9004,5800,"AIC-5800") + DEVICE(9004,5900,"ANA-5910/5930/5940 ATM155 & 25 LAN Adapter") + DEVICE(9004,5905,"ANA-5910A/5930A/5940A ATM Adapter") + DEVICE(9004,6038,"AIC-3860") + DEVICE(9004,6075,"AIC-1480 / APA-1480") + DEVICE(9004,6078,"AIC-7860") + DEVICE(9004,6178,"AIC-7861") + DEVICE(9004,6278,"AIC-7860") + DEVICE(9004,6378,"AIC-7860") + DEVICE(9004,6478,"AIC-786") + DEVICE(9004,6578,"AIC-786x") + DEVICE(9004,6678,"AIC-786") + DEVICE(9004,6778,"AIC-786x") + DEVICE(9004,6915,"ANA620xx/ANA69011A") + DEVICE(9004,7078,"AHA-294x / AIC-7870") + DEVICE(9004,7178,"AHA-294x / AIC-7871") + DEVICE(9004,7278,"AHA-3940 / AIC-7872") + DEVICE(9004,7378,"AHA-3985 / AIC-7873") + DEVICE(9004,7478,"AHA-2944 / AIC-7874") + DEVICE(9004,7578,"AHA-3944 / AHA-3944W / 7875") + DEVICE(9004,7678,"AHA-4944W/UW / 7876") + DEVICE(9004,7778,"AIC-787x") + DEVICE(9004,7810,"AIC-7810") + DEVICE(9004,7815,"AIC-7815 RAID+Memory Controller IC") + DEVICE(9004,7850,"AIC-7850") + DEVICE(9004,7855,"AHA-2930") + DEVICE(9004,7860,"AIC-7860") + DEVICE(9004,7870,"AIC-7870") + DEVICE(9004,7871,"AHA-2940") + DEVICE(9004,7872,"AHA-3940") + DEVICE(9004,7873,"AHA-3980") + DEVICE(9004,7874,"AHA-2944") + DEVICE(9004,7880,"AIC-7880P") + DEVICE(9004,7890,"AIC-7890") + DEVICE(9004,7891,"AIC-789x") + DEVICE(9004,7892,"AIC-789x") + DEVICE(9004,7893,"AIC-789x") + DEVICE(9004,7894,"AIC-789x") + DEVICE(9004,7895,"AHA-2940U/UW / AHA-39xx / AIC-7895") + DEVICE(9004,7896,"AIC-789x") + DEVICE(9004,7897,"AIC-789x") + DEVICE(9004,8078,"AIC-7880U") + DEVICE(9004,8178,"AIC-7881U") + DEVICE(9004,8278,"AHA-3940U/UW / AIC-7882U") + DEVICE(9004,8378,"AHA-3940U/UW / AIC-7883U") + DEVICE(9004,8478,"AHA-294x / AIC-7884U") + DEVICE(9004,8578,"AHA-3944U / AHA-3944UWD / 7885") + DEVICE(9004,8678,"AHA-4944UW / 7886") + DEVICE(9004,8778,"AIC-788x") + DEVICE(9004,8878,"7888") + DEVICE(9004,8b78,"ABA-1030") + DEVICE(9004,ec78,"AHA-4944W/UW") +ENDVENDOR() + +VENDOR(9005,"Adaptec") + DEVICE(9005,0010,"AHA-2940U2/W") + DEVICE(9005,0011,"2930U2") + DEVICE(9005,0013,"78902") + DEVICE(9005,001f,"AHA-2940U2/W / 7890") + DEVICE(9005,0020,"AIC-7890") + DEVICE(9005,002f,"AIC-7890") + DEVICE(9005,0030,"AIC-7890") + DEVICE(9005,003f,"AIC-7890") + DEVICE(9005,0050,"3940U2") + DEVICE(9005,0051,"3950U2D") + DEVICE(9005,0053,"AIC-7896 SCSI Controller") + DEVICE(9005,005f,"7896") + DEVICE(9005,0080,"7892A") + DEVICE(9005,0081,"7892B") + DEVICE(9005,0083,"7892D") + DEVICE(9005,008f,"7892P") + DEVICE(9005,00c0,"7899A") + DEVICE(9005,00c1,"7899B") + DEVICE(9005,00c3,"7899D") + DEVICE(9005,00cf,"7899P") +ENDVENDOR() + +VENDOR(907f,"Atronics") + DEVICE(907f,2015,"IDE-2015PL") +ENDVENDOR() + +VENDOR(919a,"Gigapixel Corp") +ENDVENDOR() + +VENDOR(9412,"Holtek") + DEVICE(9412,6565,"6565") +ENDVENDOR() + +VENDOR(9699,"Omni Media Technology Inc") + DEVICE(9699,6565,"6565") +ENDVENDOR() + +VENDOR(a0a0,"AOPEN Inc.") +ENDVENDOR() + +VENDOR(a0f1,"UNISYS Corporation") +ENDVENDOR() + +VENDOR(a200,"NEC Corporation") +ENDVENDOR() + +VENDOR(a259,"Hewlett Packard") +ENDVENDOR() + +VENDOR(a25b,"Hewlett Packard GmbH PL24-MKT") +ENDVENDOR() + +VENDOR(a304,"Sony") +ENDVENDOR() + +VENDOR(a727,"3Com Corporation") +ENDVENDOR() + +VENDOR(aa42,"Scitex Digital Video") +ENDVENDOR() + +VENDOR(ac1e,"Digital Receiver Technology Inc") +ENDVENDOR() + +VENDOR(b1b3,"Shiva Europe Limited") +ENDVENDOR() + +VENDOR(c001,"TSI Telsys") +ENDVENDOR() + +VENDOR(c0a9,"Micron/Crucial Technology") +ENDVENDOR() + +VENDOR(c0de,"Motorola") +ENDVENDOR() + +VENDOR(c0fe,"Motion Engineering, Inc.") +ENDVENDOR() + +VENDOR(ca50,"Varian Australia Pty Ltd") +ENDVENDOR() + +VENDOR(cafe,"Chrysalis-ITS") +ENDVENDOR() + +VENDOR(cccc,"Catapult Communications") +ENDVENDOR() + +VENDOR(d4d4,"Dy4 Systems Inc") + DEVICE(d4d4,0601,"PCI Mezzanine Card") +ENDVENDOR() + +VENDOR(d84d,"Exsys") +ENDVENDOR() + +VENDOR(e000,"Winbond") + DEVICE(e000,e000,"W89C940") +ENDVENDOR() + +VENDOR(e159,"Tiger Jet Network Inc.") + DEVICE(e159,0001,"Model 300 128k") +ENDVENDOR() + +VENDOR(e4bf,"EKF Elektronik GmbH") +ENDVENDOR() + +VENDOR(ea01,"Eagle Technology") +ENDVENDOR() + +VENDOR(eabb,"Aashima Technology B.V.") +ENDVENDOR() + +VENDOR(ecc0,"Echo Corporation") +ENDVENDOR() + +VENDOR(edd8,"ARK Logic Inc") + DEVICE(edd8,a091,"1000PV [Stingray]") + DEVICE(edd8,a099,"2000PV [Stingray]") + DEVICE(edd8,a0a1,"2000MT") + DEVICE(edd8,a0a9,"2000MI") +ENDVENDOR() + +VENDOR(fa57,"Fast Search & Transfer ASA") +ENDVENDOR() + +VENDOR(feda,"Epigram Inc") +ENDVENDOR() + +VENDOR(fffe,"VMWare Inc") + DEVICE(fffe,0710,"Virtual SVGA") +ENDVENDOR() + +VENDOR(ffff,"Illegal Vendor ID") +ENDVENDOR() + +#undef VENDOR +#undef DEVICE +#undef ENDVENDOR Binary files linux.vanilla/drivers/pci/gen-devlist and linux.ac/drivers/pci/gen-devlist differ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pci/pci.ids linux.ac/drivers/pci/pci.ids --- linux.vanilla/drivers/pci/pci.ids Sat May 26 16:53:10 2001 +++ linux.ac/drivers/pci/pci.ids Sat May 26 00:24:54 2001 @@ -383,10 +383,12 @@ 1014 00cf 16/4 Token-Ring Adapter Special 1014 00e4 High-Speed 100/16/4 Token-Ring Adapter 1014 00e5 16/4 Token-Ring Adapter 2 + Wake-On-LAN + 1014 016d iSeries 2744 Card 0045 SSA Adapter 0046 MPIC interrupt controller 0047 PCI to PCI Bridge 0048 PCI to PCI Bridge + 0049 Warhead SCSI Controller 004e ATM Controller (14104e00) 004f ATM Controller (14104f00) 0050 ATM Controller (14105000) @@ -398,6 +400,10 @@ 0090 GXT 3000P 1014 008E GXT-3000P 0095 20H2999 PCI Docking Bridge + 0096 Chukar chipset SCSI controller + 1014 0099 iSeries 2748 DASD IOA + 1014 0098 iSeries 2763 DASD IOA + 1014 0097 iSeries 2778 DASD IOA 00a5 ATM Controller (1410a500) 00a6 ATM 155MBPS MM Controller (1410a600) 00b7 256-bit Graphics Rasterizer [Fire GL1] @@ -2455,7 +2461,9 @@ 1221 82C092G 119c Information Technology Inst. 119d Bug, Inc. Sapporo Japan -119e Fujitsu Microelectronics Ltd. +119e Fujitsu Microelectronics Europe GMBH + 0001 FireStream 155 + 0003 FireStream 50 119f Bull HN Information Systems 11a0 Convex Computer Corporation 11a1 Hamamatsu Photonics K.K. @@ -3002,13 +3010,14 @@ 8086 5643 ES1371, ES1373 AudioPCI On Motherboard Vancouver 8086 5753 ES1371, ES1373 AudioPCI On Motherboard WS440BX 5000 ES1370 [AudioPCI] - 5880 5880 AudioPCI + 4942 4c4c Creative Sound Blaster AudioPCI128 + 5880 CT5880 [AudioPCI] 1274 2000 Creative Sound Blaster AudioPCI128 1274 5880 Creative Sound Blaster AudioPCI128 - 1462 6880 5880 AudioPCI On Motherboard MS-6188 1.00 - 270f 2001 5880 AudioPCI On Motherboard 6CTR - 270f 2200 5880 AudioPCI On Motherboard 6WTX - 270f 7040 5880 AudioPCI On Motherboard 6ATA4 + 1462 6880 CT5880 AudioPCI On Motherboard MS-6188 1.00 + 270f 2001 CT5880 AudioPCI On Motherboard 6CTR + 270f 2200 CT5880 AudioPCI On Motherboard 6WTX + 270f 7040 CT5880 AudioPCI On Motherboard 6ATA4 1275 Network Appliance Corporation 1276 Switched Network Technologies, Inc. 1277 Comstream @@ -4599,13 +4608,18 @@ 11d4 0048 SoundMAX Integrated Digital Audio 2426 82801AB AC'97 Modem 2428 82801AB PCI Bridge - 2440 82820 820 (Camino 2) Chipset ISA Bridge (ICH2) - 2442 82820 820 (Camino 2) Chipset USB (Hub A) - 2443 82820 820 (Camino 2) Chipset SMBus - 2444 82820 820 (Camino 2) Chipset USB (Hub B) - 2449 82820 820 (Camino 2) Chipset Ethernet - 244b 82820 820 (Camino 2) Chipset IDE U100 - 244e 82820 820 (Camino 2) Chipset PCI + 2440 82801BA ISA Bridge (ICH2) + 2442 82801BA(M) USB (Hub A) + 2443 82801BA(M) SMBus + 2444 82801BA(M) USB (Hub B) + 2445 82801BA(M) AC'97 Audio + 2446 82801BA(M) AC'97 Modem + 2448 82801BA PCI + 2449 82801BA(M) Ethernet + 244a 82801BAM IDE U100 + 244b 82801BA IDE U100 + 244c 82801BAM ISA Bridge (ICH2) + 244e 82801BAM PCI 2500 82820 820 (Camino) Chipset Host Bridge (MCH) 1043 801c P3C-2000 system chipset 2501 82820 820 (Camino) Chipset Host Bridge (MCH) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/bulkmem.c linux.ac/drivers/pcmcia/bulkmem.c --- linux.vanilla/drivers/pcmcia/bulkmem.c Tue Apr 3 17:32:17 2001 +++ linux.ac/drivers/pcmcia/bulkmem.c Tue Apr 3 17:54:57 2001 @@ -211,7 +211,7 @@ retry_erase((erase_busy_t *)arg, MTD_REQ_TIMEOUT); } -static void setup_erase_request(client_handle_t handle, eraseq_entry_t *erase) +static int setup_erase_request(client_handle_t handle, eraseq_entry_t *erase) { erase_busy_t *busy; region_info_t *info; @@ -229,6 +229,8 @@ else { erase->State = 1; busy = kmalloc(sizeof(erase_busy_t), GFP_KERNEL); + if (!busy) + return CS_GENERAL_FAILURE; busy->erase = erase; busy->client = handle; init_timer(&busy->timeout); @@ -238,6 +240,7 @@ retry_erase(busy, 0); } } + return CS_SUCCESS; } /* setup_erase_request */ /*====================================================================== @@ -322,7 +325,7 @@ ======================================================================*/ -static void setup_regions(client_handle_t handle, int attr, +static int setup_regions(client_handle_t handle, int attr, memory_handle_t *list) { int i, code, has_jedec, has_geo; @@ -337,7 +340,7 @@ code = (attr) ? CISTPL_DEVICE_A : CISTPL_DEVICE; if (read_tuple(handle, code, &device) != CS_SUCCESS) - return; + return CS_GENERAL_FAILURE; code = (attr) ? CISTPL_JEDEC_A : CISTPL_JEDEC_C; has_jedec = (read_tuple(handle, code, &jedec) == CS_SUCCESS); if (has_jedec && (device.ndev != jedec.nid)) { @@ -360,6 +363,8 @@ if ((device.dev[i].type != CISTPL_DTYPE_NULL) && (device.dev[i].size != 0)) { r = kmalloc(sizeof(*r), GFP_KERNEL); + if (!r) + return CS_GENERAL_FAILURE; r->region_magic = REGION_MAGIC; r->state = 0; r->dev_info[0] = '\0'; @@ -384,6 +389,7 @@ } offset += device.dev[i].size; } + return CS_SUCCESS; } /* setup_regions */ /*====================================================================== @@ -417,8 +423,10 @@ if ((handle->Attributes & INFO_MASTER_CLIENT) && (!(s->state & SOCKET_REGION_INFO))) { - setup_regions(handle, 0, &s->c_region); - setup_regions(handle, 1, &s->a_region); + if (setup_regions(handle, 0, &s->c_region) != CS_SUCCESS) + return CS_GENERAL_FAILURE; + if (setup_regions(handle, 1, &s->a_region) != CS_SUCCESS) + return CS_GENERAL_FAILURE; s->state |= SOCKET_REGION_INFO; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/ds.c linux.ac/drivers/pcmcia/ds.c --- linux.vanilla/drivers/pcmcia/ds.c Sat Feb 17 00:02:36 2001 +++ linux.ac/drivers/pcmcia/ds.c Tue May 1 08:26:56 2001 @@ -415,7 +415,10 @@ driver->use_count++; b = kmalloc(sizeof(socket_bind_t), GFP_KERNEL); if (!b) - return -ENOMEM; + { + driver->use_count--; + return -ENOMEM; + } b->driver = driver; b->function = bind_info->function; b->instance = NULL; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pcmcia/yenta.c linux.ac/drivers/pcmcia/yenta.c --- linux.vanilla/drivers/pcmcia/yenta.c Mon Apr 30 15:13:21 2001 +++ linux.ac/drivers/pcmcia/yenta.c Tue May 8 17:25:56 2001 @@ -793,6 +793,8 @@ { PD(TI,1211), &ti_ops }, { PD(TI,1251B), &ti_ops }, { PD(TI,1420), &ti_ops }, + { PD(TI,4410), &ti_ops }, + { PD(TI,4451), &ti_ops }, { PD(RICOH,RL5C465), &ricoh_ops }, { PD(RICOH,RL5C466), &ricoh_ops }, diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pnp/Config.in linux.ac/drivers/pnp/Config.in --- linux.vanilla/drivers/pnp/Config.in Mon Oct 11 18:13:24 1999 +++ linux.ac/drivers/pnp/Config.in Tue Apr 17 16:09:31 2001 @@ -8,4 +8,8 @@ dep_tristate ' ISA Plug and Play support' CONFIG_ISAPNP $CONFIG_PNP +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_bool ' PNPBIOS support (EXPERIMENTAL)' CONFIG_PNPBIOS $CONFIG_PNP +fi + endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pnp/Makefile linux.ac/drivers/pnp/Makefile --- linux.vanilla/drivers/pnp/Makefile Tue Apr 3 17:32:17 2001 +++ linux.ac/drivers/pnp/Makefile Tue Apr 17 16:01:39 2001 @@ -8,17 +8,24 @@ # Note 2! The CFLAGS definitions are now inherited from the # parent makes.. -O_TARGET := pnp.o +O_TARGET := pnp.o -export-objs := isapnp.o -list-multi := isa-pnp.o +export-objs := isapnp.o pnp_bios.o +multi-objs := isa-pnp.o pnpbios.o proc-$(CONFIG_PROC_FS) = isapnp_proc.o isa-pnp-objs := isapnp.o quirks.o $(proc-y) +procpnp-$(CONFIG_PROC_FS) = pnp_proc.o +pnpbios-objs := pnp_bios.o $(procpnp-y) + obj-$(CONFIG_ISAPNP) += isa-pnp.o +obj-$(CONFIG_PNPBIOS) += pnpbios.o include $(TOPDIR)/Rules.make -isa-pnp.o: $(isa-pnp-objs) - $(LD) $(LD_RFLAG) -r -o $@ $(isa-pnp-objs) +isa-pnp.o: $(isa-pnp-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(isa-pnp-objs) + +pnpbios.o: $(pnpbios-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(pnpbios-objs) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pnp/pnp_bios.c linux.ac/drivers/pnp/pnp_bios.c --- linux.vanilla/drivers/pnp/pnp_bios.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pnp/pnp_bios.c Wed May 9 22:00:31 2001 @@ -0,0 +1,646 @@ +/* + * PnP bios services + * + * Originally (C) 1998 Christian Schmidt (chr.schmidt@tu-bs.de) + * Modifications (c) 1998 Tom Lees + * Minor reorganizations by David Hinds + * + * 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, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Reference: + * Compaq Computer Corporation, Phoenix Technologies Ltd., Intel + * Corporation. + * Plug and Play BIOS Specification, Version 1.0A, May 5, 1994 + * Plug and Play BIOS Clarification Paper, October 6, 1994 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PnP bios signature: "$PnP" */ +#define PNP_SIGNATURE (('$' << 0) + ('P' << 8) + ('n' << 16) + ('P' << 24)) + +void pnp_proc_init(void); + +/* + * This is the standard structure used to identify the entry point + * to the Plug and Play bios + */ +#pragma pack(1) +union pnpbios { + struct { + u32 signature; /* "$PnP" */ + u8 version; /* in BCD */ + u8 length; /* length in bytes, currently 21h */ + u16 control; /* system capabilities */ + u8 checksum; /* all bytes must add up to 0 */ + + u32 eventflag; /* phys. address of the event flag */ + u16 rmoffset; /* real mode entry point */ + u16 rmcseg; + u16 pm16offset; /* 16 bit protected mode entry */ + u32 pm16cseg; + u32 deviceID; /* EISA encoded system ID or 0 */ + u16 rmdseg; /* real mode data segment */ + u32 pm16dseg; /* 16 bit pm data segment base */ + } fields; + char chars[0x21]; /* To calculate the checksum */ +}; +#pragma pack() + +/* + * Local Variables + */ +static struct { + u32 offset; + u16 segment; +} pnp_bios_callpoint; + +static union pnpbios * pnp_bios_inst_struc = NULL; + +/* The PnP entries in the GDT */ +#define PNP_GDT 0x0060 +#define PNP_CS32 (PNP_GDT+0x00) /* segment for calling fn */ +#define PNP_CS16 (PNP_GDT+0x08) /* code segment for bios */ +#define PNP_DS (PNP_GDT+0x10) /* data segment for bios */ +#define PNP_TS1 (PNP_GDT+0x18) /* transfer data segment */ +#define PNP_TS2 (PNP_GDT+0x20) /* another data segment */ + +#if 0 +static struct desc_struct pnp_gdt[] = { + { 0, 0x00c09a00 }, /* 32-bit code */ + { 0, 0x00809a00 }, /* 16-bit code */ + { 0, 0x00809200 }, /* 16-bit data */ + { 0, 0x00809200 }, /* 16-bit data */ + { 0, 0x00809200 } /* 16-bit data */ +}; + +static void set_pnp_gdt(void) +{ + memcpy(&gdt[PNP_GDT >> 3], pnp_gdt, sizeof(pnp_gdt)); +} +#endif + +/* + * These are some opcodes for a "static asmlinkage" + * As this code is *not* executed inside the linux kernel segment, but in a + * alias at offset 0, we need a far return that can not be compiled by + * default (please, prove me wrong! this is *really* ugly!) + * This is the only way to get the bios to return into the kernel code, + * because the bios code runs in 16 bit protected mode and therefore can only + * return to the caller if the call is within the first 64kB, and the linux + * kernel begins at offset 1MB... + */ +static u8 pnp_bios_callfunc[] = +{ + 0x52, /* push edx */ + 0x51, /* push ecx */ + 0x53, /* push ebx */ + 0x50, /* push eax */ + 0x66, 0x9a, 0, 0, /* call far pnp_cs16:0, offset */ + (PNP_CS16) & 0xff, (PNP_CS16) >> 8, /* becomes fixed up later */ + 0x83, 0xc4, 0x10, /* add esp, 16 */ + 0xcb}; /* retf */ + +#define Q_SET_SEL(selname, address, size) \ +set_base (gdt [(selname) >> 3], __va((u32)(address))); \ +set_limit (gdt [(selname) >> 3], size) + +#define Q2_SET_SEL(selname, address, size) \ +set_base (gdt [(selname) >> 3], (u32)(address)); \ +set_limit (gdt [(selname) >> 3], size) + +/* + * Callable Functions + */ +#define PNP_GET_NUM_SYS_DEV_NODES 0x00 +#define PNP_GET_SYS_DEV_NODE 0x01 +#define PNP_SET_SYS_DEV_NODE 0x02 +#define PNP_GET_EVENT 0x03 +#define PNP_SEND_MESSAGE 0x04 +#define PNP_GET_DOCKING_STATION_INFORMATION 0x05 +#define PNP_SET_STATIC_ALLOCED_RES_INFO 0x09 +#define PNP_GET_STATIC_ALLOCED_RES_INFO 0x0a +#define PNP_GET_APM_ID_TABLE 0x0b +#define PNP_GET_PNP_ISA_CONFIG_STRUC 0x40 +#define PNP_GET_ESCD_INFO 0x41 +#define PNP_READ_ESCD 0x42 +#define PNP_WRITE_ESCD 0x43 + +/* + * Call pnp bios with function 0x00, "get number of system device nodes" + */ + +int pnp_bios_dev_node_info(struct pnp_dev_node_info *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_dev_node_info)); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(PNP_GET_NUM_SYS_DEV_NODES), + "b"((2 << 16) | PNP_TS1), + "c"((PNP_DS << 16) | PNP_TS1) + :"memory"); + data->no_nodes &= 0xff; + return status; +} + +/* + * Call pnp bios with function 0x01, "get system device node" + * Input: *nodenum=desired node, + * static=1: config (dynamic) config, else boot (static) config, + * Output: *nodenum=next node or 0xff if no more nodes + */ + +int pnp_bios_get_dev_node(u8 *nodenum, char config, struct pnp_bios_node *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, nodenum, sizeof(char)); + Q2_SET_SEL(PNP_TS2, data, 64 * 1024); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(PNP_GET_SYS_DEV_NODE), + "b"(PNP_TS1), + "c"(((config ? 1 : 2) <<16) | PNP_TS2), + "d"(PNP_DS) + :"memory"); + return status; +} + +/* + * Call pnp bios with function 0x02, "set system device node" + * Input: nodenum=desired node, + * static=1: config (dynamic) config, else boot (static) config, + */ + +int pnp_bios_set_dev_node(u8 nodenum, char config, struct pnp_bios_node *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, /* *((u16 *) data)*/ 65536); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(((u32) nodenum << 16) | PNP_SET_SYS_DEV_NODE), + "b"(PNP_TS1 << 16), + "c"((PNP_DS << 16) | (config ? 1 : 2)) + :"memory"); + return status; +} + +/* + * Call pnp bios with function 0x03, "get event" + */ +#if needed + +static int pnp_bios_get_event(u16 *event) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, event, sizeof(u16)); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(PNP_GET_EVENT), + "b"((PNP_DS << 16) | PNP_TS1) + :"memory"); + return status; +} +#endif + +/* + * Call pnp bios with function 0x04, "send message" + */ +#if needed +static int pnp_bios_send_message(u16 message) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(((u32) message << 16) | PNP_SEND_MESSAGE), + "b"(PNP_DS) + :"memory"); + return status; +} +#endif + +/* + * Call pnp bios with function 0x05, "get docking station information" + */ +#if needed +static int pnp_bios_dock_station_info(struct pnp_docking_station_info *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_docking_station_info)); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(PNP_GET_DOCKING_STATION_INFORMATION), + "b"((PNP_TS1 << 16) | PNP_DS) + :"memory"); + return status; +} +#endif + +/* + * Call pnp bios with function 0x09, "set statically allocated resource + * information" + */ +#if needed +static int pnp_bios_set_stat_res(char *info) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, info, *((u16 *) info)); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(PNP_SET_STATIC_ALLOCED_RES_INFO), + "b"((PNP_TS1 << 16) | PNP_DS) + :"memory"); + return status; +} +#endif + +/* + * Call pnp bios with function 0x0a, "get statically allocated resource + * information" + */ +#if needed +static int pnp_bios_get_stat_res(char *info) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, info, 64 * 1024); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(PNP_GET_STATIC_ALLOCED_RES_INFO), + "b"((PNP_TS1 << 16) | PNP_DS) + :"memory"); + return status; +} +#endif + +/* + * Call pnp bios with function 0x0b, "get APM id table" + */ +#if needed +static int pnp_bios_apm_id_table(char *table, u16 *size) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, table, *size); + Q2_SET_SEL(PNP_TS2, size, sizeof(u16)); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(PNP_GET_APM_ID_TABLE), + "b"(PNP_TS2), + "c"((PNP_DS << 16) | PNP_TS1) + :"memory"); + return status; +} +#endif + +/* + * Call pnp bios with function 0x40, "get isa pnp configuration structure" + */ +#if needed +static int pnp_bios_isapnp_config(struct pnp_isa_config_struc *data) +{ + u16 status; + if (!pnp_bios_present ()) + return PNP_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_isa_config_struc)); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(PNP_GET_PNP_ISA_CONFIG_STRUC), + "b"((PNP_DS << 16) | PNP_TS1) + :"memory"); + return status; +} +#endif + +/* + * Call pnp bios with function 0x41, "get ESCD info" + */ +#if needed +static int pnp_bios_escd_info(struct escd_info_struc *data) +{ + u16 status; + if (!pnp_bios_present ()) + return ESCD_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, sizeof(struct escd_info_struc)); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(PNP_GET_ESCD_INFO), + "b"((2 << 16) | PNP_TS1), + "c"((4 << 16) | PNP_TS1), + "d"((PNP_DS << 16) | PNP_TS1) + :"memory"); + return status; +} +#endif + +/* + * Call pnp bios function 0x42, "read ESCD" + * nvram_base is determined by calling escd_info + */ +#if needed +static int pnp_bios_read_escd(char *data, u32 nvram_base) +{ + u16 status; + if (!pnp_bios_present ()) + return ESCD_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, 64 * 1024); + set_base(gdt[PNP_TS2 >> 3], nvram_base); + set_limit(gdt[PNP_TS2 >> 3], 64 * 1024); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(PNP_READ_ESCD), + "b"((PNP_TS2 << 16) | PNP_TS1), + "c"(PNP_DS) + :"memory"); + return status; +} +#endif + +/* + * Call pnp bios function 0x43, "write ESCD" + */ +#if needed +static int pnp_bios_write_escd(char *data, u32 nvram_base) +{ + u16 status; + if (!pnp_bios_present ()) + return ESCD_FUNCTION_NOT_SUPPORTED; + Q2_SET_SEL(PNP_TS1, data, 64 * 1024); + set_base(gdt[PNP_TS2 >> 3], nvram_base); + set_limit(gdt[PNP_TS2 >> 3], 64 * 1024); + __asm__ __volatile__ + ("lcall %%cs:" SYMBOL_NAME_STR(pnp_bios_callpoint) "\n\t" + :"=a"(status) + :"a"(PNP_WRITE_ESCD), + "b"((PNP_TS2 << 16) | PNP_TS1), + "c"(PNP_DS) + :"memory"); + return status; +} +#endif + +int pnp_bios_present(void) +{ + return (pnp_bios_inst_struc != NULL); +} + +/* + * Searches the defined area (0xf0000-0xffff0) for a valid PnP BIOS + * structure and, if found one, sets up the selectors and entry points + */ + +void pnp_bios_init(void) +{ + union pnpbios *check; + u8 sum; + int i, length; + + for (check = (union pnpbios *) __va(0xf0000); + check < (union pnpbios *) __va(0xffff0); + ((void *) (check)) += 16) { + if (check->fields.signature != PNP_SIGNATURE) + continue; + length = check->fields.length; + if (!length) + continue; + for (sum = 0, i = 0; i < length; i++) + sum += check->chars[i]; + if (sum) + continue; + if (check->fields.version < 0x10) { + printk(KERN_WARNING "PnP: unsupported version %d.%d", + check->fields.version >> 4, + check->fields.version & 15); + continue; + } + printk(KERN_INFO "PnP: PNP BIOS installation structure at 0x%p\n", + check); + printk(KERN_INFO "PnP: PNP BIOS version %d.%d, entry at %x:%x, dseg at %x\n", + check->fields.version >> 4, check->fields.version & 15, + check->fields.pm16cseg, check->fields.pm16offset, + check->fields.pm16dseg); + Q2_SET_SEL(PNP_CS32, &pnp_bios_callfunc, + sizeof(pnp_bios_callfunc)); + Q_SET_SEL(PNP_CS16, check->fields.pm16cseg, 64 * 1024); + Q_SET_SEL(PNP_DS, check->fields.pm16dseg, 64 * 1024); +// set_pnp_gdt(); + pnp_bios_callfunc[6] = check->fields.pm16offset & 0xff; + pnp_bios_callfunc[7] = check->fields.pm16offset >> 8; + pnp_bios_callpoint.offset = 0; + pnp_bios_callpoint.segment = PNP_CS32; + pnp_bios_inst_struc = check; + break; + } + pnp_proc_init(); +} + +#ifdef MODULE + +int init_module(void) +{ + pnp_bios_init(); + return(0); +} + +#endif + +EXPORT_SYMBOL(pnp_bios_get_dev_node); +EXPORT_SYMBOL(pnp_bios_present); +EXPORT_SYMBOL(pnp_bios_dev_node_info); + +EXPORT_SYMBOL(pnpbios_find_device); + +/* parse PNPBIOS "Allocated Resources Block" and fill IO,IRQ,DMA into pci_dev */ +static void pnpbios_rawdata_2_pci_dev(struct pnp_bios_node *node, struct pci_dev *pci_dev) +{ + unsigned char *p = node->data, *lastp=NULL; + int mask,i,io,irq=0,len,dma=-1; + + while ( (char *)p < ((char *)node->data + node->size )) { + if(p==lastp) break; + + if( p[0] & 0x80 ) {// large item + lastp = p+3; + p = p + p[1] + p[2]*256 + 3; + continue; + } + if ((p[0]>>3) == 0x0f) // end tag + break; + switch (p[0]>>3) { + case 0x04: // irq + mask= p[1] + p[2]*256; + for (i=0;i<16;i++, mask=mask>>1) + if(mask &0x01) irq=i; + i=0; + while(pci_dev->irq_resource[i].start && iirq_resource[i].start=irq; + break; + case 0x05: // dma + mask = p[1]; + for (i=0;i<8;i++, mask = mask>>1) + if(mask&0x01) dma=i; + i=0; + while(pci_dev->dma_resource[i].start && idma_resource[i].start=dma; + + break; + case 0x08: // io + io= p[2] + p[3] *256; + len= p[6] + p[7] *256; + i=0; + while(pci_dev->resource[i].start && iresource[i].start=io; + pci_dev->resource[i].end=io+len; + pci_dev->resource[i].flags=IORESOURCE_IO; + } + + break; + } + lastp=p+1; + p = p + (p[0] & 0x07) + 1; + + } +} + +static int ascii_to_hex(char a) +{ + if(a>='0' && a<='9') + return a-0x30; + if(a>= 'A' && a <= 'F') + return a-0x40 + 9; + return 0; +} + +/* Compress 7char EISA ID to 32bit, e.g. "PNP0401" -> 0x0104d041 */ +static int pnpid_to_pnpid32(char *pnpid) +{ + int i,letter1,letter2,letter3,hex[4]; + + letter1=(pnpid[0]-0x40) & 0x1f; + letter2=(pnpid[1]-0x40) & 0x1f; + letter3=(pnpid[2]-0x40) & 0x1f; + + for(i=0;i<4;i++) + hex[i]=ascii_to_hex(pnpid[i+3]); + + return ( letter1<<2 | (letter2&0x18)>>3 | + ((letter2 & 0x07) << 5 | letter3) << 8 | + ((hex[0]<<4 | hex[1]) << 16 ) | + ((hex[2]<<4 | hex[3]) << 24 )); +} + +/* + * The public interface to PnP BIOS enumeration + */ + +struct pci_dev *pnpbios_find_device(char *pnpid, struct pci_dev *prev) +{ + struct pnp_bios_node *node; + struct pnp_dev_node_info node_info; + struct pci_dev *dev; + int pnpid32; + int num; + + + if (!pnp_bios_present ()) + return NULL; + + if (pnp_bios_dev_node_info(&node_info) != 0) + return NULL; + + + node = kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) + return NULL; + + + dev = kmalloc(sizeof (struct pci_dev), GFP_KERNEL); + if (!dev) { + kfree(node); + return NULL; + } + memset(dev,0, sizeof (struct pci_dev)); + + pnpid32=pnpid_to_pnpid32(pnpid); + + + if(prev==NULL) + num=0; /* Start from beginning */ + else + num=prev->devfn; /* Encode node number here */ + + for (; num != 0xff; ) { + pnp_bios_get_dev_node((u8 *)&num, (char )0 , node); + + if(node->eisa_id == pnpid32) { + pnpbios_rawdata_2_pci_dev(node,dev); + dev->devfn=num; + memcpy(dev->name,"PNPBIOS",8); + memcpy(dev->slot_name,pnpid,7); + kfree(node); + return dev; + } + } + kfree(dev); + kfree(node); + return NULL; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/pnp/pnp_proc.c linux.ac/drivers/pnp/pnp_proc.c --- linux.vanilla/drivers/pnp/pnp_proc.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/pnp/pnp_proc.c Wed May 9 22:00:31 2001 @@ -0,0 +1,141 @@ +/* + * pnp_proc.c: /proc/bus/pnp interface for Plug and Play devices + * + * Written by David Hinds, dahinds@users.sourceforge.net + */ + +//#include +#define __NO_VERSION__ +//#include + +#include +#include +#include +#include +#include +#include + +static struct proc_dir_entry *proc_pnp = NULL; +static struct proc_dir_entry *proc_pnp_boot = NULL; +static struct pnp_dev_node_info node_info; + +static int proc_read_devices(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct pnp_bios_node *node; + u8 num; + char *p = buf; + + if (pos != 0) { + *eof = 1; + return 0; + } + node = kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + for (num = 0; num != 0xff; ) { + pnp_bios_get_dev_node(&num, 0, node); + p += sprintf(p, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n", + node->handle, node->eisa_id, + node->type_code[0], node->type_code[1], + node->type_code[2], node->flags); + } + kfree(node); + return (p-buf); +} + +static int proc_read_node(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct pnp_bios_node *node; + int boot = (long)data >> 8; + u8 num = (long)data; + int len; + + if (pos != 0) { + *eof = 1; + return 0; + } + node = kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + pnp_bios_get_dev_node(&num, boot, node); + len = node->size - sizeof(struct pnp_bios_node); + memcpy(buf, node->data, len); + kfree(node); + return len; +} + +static int proc_write_node(struct file *file, const char *buf, + unsigned long count, void *data) +{ + struct pnp_bios_node *node; + int boot = (long)data >> 8; + u8 num = (long)data; + + node = kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + pnp_bios_get_dev_node(&num, boot, node); + if (count != node->size - sizeof(struct pnp_bios_node)) + return -EINVAL; + memcpy(node->data, buf, count); + if (pnp_bios_set_dev_node(node->handle, boot, node) != 0) + return -EINVAL; + kfree(node); + return count; +} + +void pnp_proc_init(void) +{ + struct pnp_bios_node *node; + struct proc_dir_entry *ent; + char name[3]; + u8 num; + + if (!pnp_bios_present()) return; + if (pnp_bios_dev_node_info(&node_info) != 0) + return; + + proc_pnp = proc_mkdir("pnp", proc_bus); + if (!proc_pnp) return; + proc_pnp_boot = proc_mkdir("boot", proc_pnp); + if (!proc_pnp_boot) return; + create_proc_read_entry("devices", 0, proc_pnp, + proc_read_devices, NULL); + + node = kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return; + for (num = 0; num != 0xff; ) { + //sprintf(name, "%02x", num); + if (pnp_bios_get_dev_node(&num, 0, node) != 0) + break; + sprintf(name, "%02x", node->handle); + ent = create_proc_entry(name, 0, proc_pnp); + if (ent) { + ent->read_proc = proc_read_node; + ent->write_proc = proc_write_node; + ent->data = (void *)(long)(node->handle); + } + ent = create_proc_entry(name, 0, proc_pnp_boot); + if (ent) { + ent->read_proc = proc_read_node; + ent->write_proc = proc_write_node; + ent->data = (void *)(long)(node->handle+0x100); + } + } + kfree(node); +} + +void pnp_proc_done(void) +{ + u8 num; + char name[3]; + + if (!proc_pnp) return; + for (num = 0; num != 0xff; num++) { + sprintf(name, "%02x", num); + remove_proc_entry(name, proc_pnp); + remove_proc_entry(name, proc_pnp_boot); + } + remove_proc_entry("boot", proc_pnp); + remove_proc_entry("devices", proc_pnp); + remove_proc_entry("pnp", proc_bus); +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sbus/char/sunkbd.c linux.ac/drivers/sbus/char/sunkbd.c --- linux.vanilla/drivers/sbus/char/sunkbd.c Mon Jan 22 21:30:20 2001 +++ linux.ac/drivers/sbus/char/sunkbd.c Tue Apr 3 17:54:59 2001 @@ -514,7 +514,7 @@ } do_poke_blanked_console = 1; - tasklet_schedule(&console_tasklet); + schedule_console_callback(); add_keyboard_randomness(keycode); tty = ttytab? ttytab[fg_console]: NULL; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/3w-xxxx.c linux.ac/drivers/scsi/3w-xxxx.c --- linux.vanilla/drivers/scsi/3w-xxxx.c Mon Apr 30 15:13:23 2001 +++ linux.ac/drivers/scsi/3w-xxxx.c Wed May 23 23:57:59 2001 @@ -76,6 +76,20 @@ Make tw_setfeature() call with interrupts disabled. Register interrupt handler before enabling interrupts. Clear attention interrupt before draining aen queue. + 1.02.00.005 - Allocate bounce buffers and custom queue depth for raid5 for + 6000 and 5000 series controllers. + Reduce polling mdelays causing problems on some systems. + Fix use_sg = 1 calculation bug. + Check for scsi_register returning NULL. + Add aen count to /proc/scsi/3w-xxxx. + Remove aen code unit masking in tw_aen_complete(). + 1.02.00.006 - Remove unit from printk in tw_scsi_eh_abort(), causing + possible oops. + Fix possible null pointer dereference in tw_scsi_queue() + if done function pointer was invalid. + 1.02.00.007 - Fix possible null pointer dereferences in tw_ioctl(). + Remove check for invalid done function pointer from + tw_scsi_queue(). */ #include @@ -121,7 +135,7 @@ }; /* Globals */ -char *tw_driver_version="1.02.00.004"; +char *tw_driver_version="1.02.00.007"; TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT]; int tw_device_extension_count = 0; @@ -131,7 +145,7 @@ int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id) { TW_Param *param; - unsigned short aen, aen_code; + unsigned short aen; if (tw_dev->alignment_virtual_address[request_id] == NULL) { printk(KERN_WARNING "3w-xxxx: tw_aen_complete(): Bad alignment virtual address.\n"); @@ -139,10 +153,9 @@ } param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; aen = *(unsigned short *)(param->data); - aen_code = (aen & 0x0ff); - dprintk(KERN_NOTICE "3w-xxxx: tw_aen_complete(): Queue'd code 0x%x\n", aen_code); + dprintk(KERN_NOTICE "3w-xxxx: tw_aen_complete(): Queue'd code 0x%x\n", aen); /* Now queue the code */ - tw_dev->aen_queue[tw_dev->aen_tail] = aen_code; + tw_dev->aen_queue[tw_dev->aen_tail] = aen; if (tw_dev->aen_tail == TW_Q_LENGTH - 1) { tw_dev->aen_tail = TW_Q_START; } else { @@ -241,7 +254,7 @@ /* Now poll for completion */ for (i=0;icommand_packet_virtual_address[request_id] = virt_addr; - tw_dev->command_packet_physical_address[request_id] = - virt_to_bus(virt_addr); - } else { + tw_dev->command_packet_physical_address[request_id] = virt_to_bus(virt_addr); + break; + case 1: tw_dev->alignment_virtual_address[request_id] = virt_addr; tw_dev->alignment_physical_address[request_id] = virt_to_bus(virt_addr); + break; + case 2: + tw_dev->bounce_buffer[request_id] = virt_addr; + break; + default: + printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n"); + return 1; } return 0; } /* End tw_allocate_memory() */ @@ -709,8 +730,8 @@ /* Register the card with the kernel SCSI layer */ host = scsi_register(tw_host, sizeof(TW_Device_Extension)); - if( host == NULL) - { + if (host == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", numcards-1); release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE); tw_free_device_extension(tw_dev); kfree(tw_dev); @@ -788,6 +809,9 @@ if (tw_dev->alignment_virtual_address[i]) kfree(tw_dev->alignment_virtual_address[i]); + + if (tw_dev->bounce_buffer[i]) + kfree(tw_dev->bounce_buffer[i]); } } /* End tw_free_device_extension() */ @@ -853,7 +877,7 @@ /* Poll for completion */ imax = TW_POLL_MAX_RETRIES; for (i=0;iaen_queue[i] = 0; } - for (i=0;iis_unit_present[i] = 0; + tw_dev->is_raid_five[i] = 0; + } tw_dev->num_units = 0; tw_dev->num_aborts = 0; @@ -928,6 +954,8 @@ tw_dev->aen_tail = 0; tw_dev->sector_count = 0; tw_dev->max_sector_count = 0; + tw_dev->aen_count = 0; + tw_dev->num_raid_five = 0; spin_lock_init(&tw_dev->tw_lock); tw_dev->flags = 0; return 0; @@ -940,13 +968,14 @@ unsigned char request_id = 0; TW_Command *command_packet; TW_Param *param; - int i, imax, num_units = 0; + int i, j, imax, num_units = 0, num_raid_five = 0; u32 status_reg_addr, status_reg_value; u32 command_que_addr, command_que_value; u32 response_que_addr; TW_Response_Queue response_queue; u32 param_value; unsigned char *is_unit_present; + unsigned char *raid_level; dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_units()\n"); @@ -1001,7 +1030,7 @@ /* Poll for completion */ imax = TW_POLL_MAX_RETRIES; for(i=0; iis_unit_present[j] == 0) + continue; + command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; + if (command_packet == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad command packet virtual address.\n"); + return 1; + } + memset(command_packet, 0, sizeof(TW_Sector)); + command_packet->byte0.opcode = TW_OP_GET_PARAM; + command_packet->byte0.sgl_offset = 2; + command_packet->size = 4; + command_packet->request_id = request_id; + command_packet->byte3.unit = 0; + command_packet->byte3.host_id = 0; + command_packet->status = 0; + command_packet->flags = 0; + command_packet->byte6.block_count = 1; + + /* Now setup the param */ + if (tw_dev->alignment_virtual_address[request_id] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad alignment virtual address.\n"); + return 1; + } + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + memset(param, 0, sizeof(TW_Sector)); + param->table_id = 0x300+j; /* unit summary table */ + param->parameter_id = 0x6; /* unit descriptor */ + param->parameter_size_bytes = 0xc; + param_value = tw_dev->alignment_physical_address[request_id]; + if (param_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad alignment physical address.\n"); + return 1; + } + + command_packet->byte8.param.sgl[0].address = param_value; + command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector); + + /* Post the command packet to the board */ + command_que_value = tw_dev->command_packet_physical_address[request_id]; + if (command_que_value == 0) { + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad command packet physical address.\n"); + return 1; + } + outl(command_que_value, command_que_addr); + + /* Poll for completion */ + imax = TW_POLL_MAX_RETRIES; + for(i=0; istatus != 0) { + /* bad response */ + printk(KERN_WARNING "3w-xxxx: tw_initia +lize_units(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags); + return 1; + } + found = 1; + break; + } + } + if (found == 0) { + /* response never received */ + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): No + response.\n"); + return 1; + } + + param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; + raid_level = (unsigned char *)&(param->data[1]); + if (*raid_level == 5) { + dprintk(KERN_WARNING "3w-xxxx: Found unit %d to be a raid5 unit.\n", j); + tw_dev->is_raid_five[j] = 1; + num_raid_five++; + } + } + tw_dev->num_raid_five = num_raid_five; + + /* Now allocate raid5 bounce buffers */ + if ((num_raid_five != 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) { + for (i=0;ibounce_buffer[i] == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bounce buffer allocation failed.\n"); + return 1; + } + memset(tw_dev->bounce_buffer[i], 0, sizeof(TW_Sector)*256); + } + } return 0; } /* End tw_initialize_units() */ @@ -1288,12 +1421,17 @@ case TW_OP_SET_PARAM: dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_SET_PARAM: table_id = %d, parameter_id = %d, parameter_size_bytes = %d.\n", ioctl->table_id, ioctl->parameter_id, ioctl->parameter_size_bytes); - command_packet->byte0.opcode = TW_OP_SET_PARAM; - param->table_id = ioctl->table_id; - param->parameter_id = ioctl->parameter_id; - param->parameter_size_bytes = ioctl->parameter_size_bytes; - memcpy(param->data, ioctl->data, ioctl->parameter_size_bytes); - break; + if (ioctl->data != NULL) { + command_packet->byte0.opcode = TW_OP_SET_PARAM; + param->table_id = ioctl->table_id; + param->parameter_id = ioctl->parameter_id; + param->parameter_size_bytes = ioctl->parameter_size_bytes; + memcpy(param->data, ioctl->data, ioctl->parameter_size_bytes); + break; + } else { + printk(KERN_WARNING "3w-xxxx: tw_ioctl(): ioctl->data NULL.\n"); + return 1; + } case TW_OP_AEN_LISTEN: dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_AEN_LISTEN.\n"); if (tw_dev->aen_head == tw_dev->aen_tail) { @@ -1318,11 +1456,15 @@ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); return 0; case TW_CMD_PACKET: - memcpy(command_packet, ioctl->data, sizeof(TW_Command)); - command_packet->request_id = request_id; - tw_post_command_packet(tw_dev, request_id); - - return 0; + if (ioctl->data != NULL) { + memcpy(command_packet, ioctl->data, sizeof(TW_Command)); + command_packet->request_id = request_id; + tw_post_command_packet(tw_dev, request_id); + return 0; + } else { + printk(KERN_WARNING "3w-xxxx: tw_ioctl(): ioctl->data NULL.\n"); + return 1; + } default: printk(KERN_WARNING "3w-xxxx: Unknown ioctl 0x%x.\n", opcode); tw_dev->state[request_id] = TW_S_COMPLETED; @@ -1602,10 +1744,8 @@ return 0; } - spin_unlock_irq(&io_request_lock); ret = tw_findcards(tw_host); - spin_lock_irq(&io_request_lock); - + return ret; } /* End tw_scsi_detect() */ @@ -1763,6 +1903,7 @@ tw_copy_info(&info, "Max sector count: %3d\n", tw_dev->max_sector_count); tw_copy_info(&info, "Resets: %3d\n", tw_dev->num_resets); tw_copy_info(&info, "Aborts: %3d\n", tw_dev->num_aborts); + tw_copy_info(&info, "AEN's: %3d\n", tw_dev->aen_count); } if (info.position > info.offset) { return (info.position - info.offset); @@ -1780,6 +1921,13 @@ int flags = 0; TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->host->hostdata; + if (tw_dev == NULL) { + printk(KERN_WARNING "3w-xxxx: tw_scsi_queue(): Invalid device extension.\n"); + SCpnt->result = (DID_ERROR << 16); + done(SCpnt); + return 0; + } + spin_lock_irqsave(&tw_dev->tw_lock, flags); dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue()\n"); @@ -1790,20 +1938,6 @@ spin_unlock_irqrestore(&tw_dev->tw_lock, flags); return 0; } - if (done == NULL) { - printk(KERN_WARNING "3w-xxxx: tw_scsi_queue(): Invalid done function.\n"); - SCpnt->result = (DID_ERROR << 16); - done(SCpnt); - spin_unlock_irqrestore(&tw_dev->tw_lock, flags); - return 0; - } - if (tw_dev == NULL) { - printk(KERN_WARNING "3w-xxxx: tw_scsi_queue(): Invalid device extension.\n"); - SCpnt->result = (DID_ERROR << 16); - done(SCpnt); - spin_unlock_irqrestore(&tw_dev->tw_lock, flags); - return 0; - } /* Save done function into Scsi_Cmnd struct */ SCpnt->scsi_done = done; @@ -2104,7 +2238,7 @@ TW_Command *command_packet; u32 command_que_addr, command_que_value = 0; u32 lba = 0x0, num_sectors = 0x0; - int i; + int i, count = 0; Scsi_Cmnd *srb; struct scatterlist *sglist; @@ -2161,23 +2295,45 @@ command_packet->byte8.io.lba = lba; command_packet->byte6.block_count = num_sectors; - /* Do this if there are no sg list entries */ - if (tw_dev->srb[request_id]->use_sg == 0) { - dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n"); - command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->srb[request_id]->request_buffer); - command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen; - } - - /* Do this if we have multiple sg list entries */ - if (tw_dev->srb[request_id]->use_sg > 0) { - for (i=0;isrb[request_id]->use_sg; i++) { - command_packet->byte8.io.sgl[i].address = virt_to_bus(sglist[i].address); - command_packet->byte8.io.sgl[i].length = sglist[i].length; - command_packet->size+=2; + if ((tw_dev->is_raid_five[tw_dev->srb[request_id]->target] == 0) || (srb->cmnd[0] == READ_6) || (srb->cmnd[0] == READ_10) || (tw_dev->tw_pci_dev->device == TW_DEVICE_ID2)) { + /* Do this if there are no sg list entries */ + if (tw_dev->srb[request_id]->use_sg == 0) { + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n"); + command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->srb[request_id]->request_buffer); + command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen; + } + + /* Do this if we have multiple sg list entries */ + if (tw_dev->srb[request_id]->use_sg > 0) { + for (i=0;isrb[request_id]->use_sg; i++) { + command_packet->byte8.io.sgl[i].address = virt_to_bus(sglist[i].address); + command_packet->byte8.io.sgl[i].length = sglist[i].length; + command_packet->size+=2; + } + if (tw_dev->srb[request_id]->use_sg >= 1) + command_packet->size-=2; } - if (tw_dev->srb[request_id]->use_sg > 1) - command_packet->size-=2; - } + } else { + /* Do this if there are no sg list entries for raid 5 */ + if (tw_dev->srb[request_id]->use_sg == 0) { + dprintk(KERN_WARNING "doing raid 5 write use_sg = 0, bounce_buffer[%d] = 0x%p\n", request_id, tw_dev->bounce_buffer[request_id]); + memcpy(tw_dev->bounce_buffer[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen); + command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->bounce_buffer[request_id]); + command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen; + } + + /* Do this if we have multiple sg list entries for raid 5 */ + if (tw_dev->srb[request_id]->use_sg > 0) { + dprintk(KERN_WARNING "doing raid 5 write use_sg = %d, sglist[0].length = %d\n", tw_dev->srb[request_id]->use_sg, sglist[0].length); + for (i=0;isrb[request_id]->use_sg; i++) { + memcpy((char *)(tw_dev->bounce_buffer[request_id])+count, sglist[i].address, sglist[i].length); + count+=sglist[i].length; + } + command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->bounce_buffer[request_id]); + command_packet->byte8.io.sgl[0].length = count; + command_packet->size = 5; /* single sgl */ + } + } /* Update SG statistics */ tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg; @@ -2287,7 +2443,7 @@ /* Poll for completion */ imax = TW_POLL_MAX_RETRIES; for (i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MODULE +#include +#endif + + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" + +#include "53c700.h" + +#ifdef NCR_700_DEBUG +#define STATIC +#else +#define STATIC static +#endif + +#ifdef MODULE +MODULE_AUTHOR("James Bottomley"); +MODULE_DESCRIPTION("53c700 and 53c700-66 Driver"); +#endif + +/* This is the script */ +#include "53c700_d.h" + + +STATIC int NCR_700_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +STATIC int NCR_700_abort(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_bus_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_dev_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_host_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_proc_directory_info(char *, char **, off_t, int, int, int); +STATIC void NCR_700_chip_setup(struct Scsi_Host *host); +STATIC void NCR_700_chip_reset(struct Scsi_Host *host); + +static char *NCR_700_phase[] = { + "", + "after selection", + "before command phase", + "after command phase", + "after status phase", + "after data in phase", + "after data out phase", + "during data phase", +}; + +static char *NCR_700_condition[] = { + "", + "NOT MSG_OUT", + "UNEXPECTED PHASE", + "NOT MSG_IN", + "UNEXPECTED MSG", + "MSG_IN", + "SDTR_MSG RECEIVED", + "REJECT_MSG RECEIVED", + "DISCONNECT_MSG RECEIVED", + "MSG_OUT", + "DATA_IN", + +}; + +static char *NCR_700_fatal_messages[] = { + "unexpected message after reselection", + "still MSG_OUT after message injection", + "not MSG_IN after selection", + "Illegal message length received", +}; + +static char *NCR_700_SBCL_bits[] = { + "IO ", + "CD ", + "MSG ", + "ATN ", + "SEL ", + "BSY ", + "ACK ", + "REQ ", +}; + +static char *NCR_700_SBCL_to_phase[] = { + "DATA_OUT", + "DATA_IN", + "CMD_OUT", + "STATE", + "ILLEGAL PHASE", + "ILLEGAL PHASE", + "MSG OUT", + "MSG IN", +}; + +static __u8 NCR_700_SDTR_msg[] = { + 0x01, /* Extended message */ + 0x03, /* Extended message Length */ + 0x01, /* SDTR Extended message */ + NCR_700_MIN_PERIOD, + NCR_700_MAX_OFFSET +}; + +struct Scsi_Host * __init +NCR_700_detect(Scsi_Host_Template *tpnt, + struct NCR_700_Host_Parameters *hostdata) +{ + __u32 *script = kmalloc(sizeof(SCRIPT), GFP_KERNEL); + __u32 pScript; + struct Scsi_Host *host; + static int banner = 0; + int j; + + /* Fill in the missing routines from the host template */ + tpnt->queuecommand = NCR_700_queuecommand; + tpnt->eh_abort_handler = NCR_700_abort; + tpnt->eh_device_reset_handler = NCR_700_dev_reset; + tpnt->eh_bus_reset_handler = NCR_700_bus_reset; + tpnt->eh_host_reset_handler = NCR_700_host_reset; + tpnt->can_queue = NCR_700_COMMAND_SLOTS_PER_HOST; + tpnt->sg_tablesize = NCR_700_SG_SEGMENTS; + tpnt->cmd_per_lun = NCR_700_MAX_TAGS; + tpnt->use_clustering = DISABLE_CLUSTERING; + tpnt->use_new_eh_code = 1; + tpnt->proc_info = NCR_700_proc_directory_info; + + if(tpnt->name == NULL) + tpnt->name = "53c700"; + if(tpnt->proc_name == NULL) + tpnt->proc_name = "53c700"; + + + host = scsi_register(tpnt, 4); + if(script == NULL) { + printk(KERN_ERR "53c700: Failed to allocate script, detatching\n"); + scsi_unregister(host); + return NULL; + } + + hostdata->slots = kmalloc(sizeof(struct NCR_700_command_slot) * NCR_700_COMMAND_SLOTS_PER_HOST, GFP_KERNEL); + if(hostdata->slots == NULL) { + printk(KERN_ERR "53c700: Failed to allocate command slots, detatching\n"); + scsi_unregister(host); + return NULL; + } + memset(hostdata->slots, 0, sizeof(struct NCR_700_command_slot) * NCR_700_COMMAND_SLOTS_PER_HOST); + for(j = 0; j < NCR_700_COMMAND_SLOTS_PER_HOST; j++) { + if(j == 0) + hostdata->free_list = &hostdata->slots[j]; + else + hostdata->slots[j-1].ITL_forw = &hostdata->slots[j]; + hostdata->slots[j].state = NCR_700_SLOT_FREE; + } + host->hostdata[0] = (__u32)hostdata; + memcpy(script, SCRIPT, sizeof(SCRIPT)); + /* bus physical address of script */ + pScript = virt_to_bus(script); + /* adjust all labels to be bus physical */ + for(j = 0; j < PATCHES; j++) { + script[LABELPATCHES[j]] += pScript; + } + /* now patch up fixed addresses */ + script_patch_32(script, MessageLocation, + virt_to_bus(&hostdata->msgout[0])); + script_patch_32(script, StatusAddress, + virt_to_bus(&hostdata->status)); + script_patch_32(script, ReceiveMsgAddress, + virt_to_bus(&hostdata->msgin[0])) + hostdata->script = script; + hostdata->pScript = pScript; + hostdata->state = NCR_700_HOST_FREE; + spin_lock_init(&hostdata->lock); + hostdata->cmd = NULL; + host->max_id = 7; + host->max_lun = NCR_700_MAX_LUNS; + host->unique_id = hostdata->base; + host->base = hostdata->base; + host->hostdata[0] = (unsigned long)hostdata; + /* kick the chip */ + outb(0xff, host->base + CTEST9_REG); + hostdata->rev = (inb(host->base + CTEST7_REG)<<4) & 0x0f; + hostdata->fast = (inb(host->base + CTEST9_REG) == 0); + if(banner == 0) { + printk(KERN_NOTICE "53c700: Version " NCR_700_VERSION " By James.Bottomley@HansenPartnership.com\n"); + banner = 1; + } + printk(KERN_NOTICE "scsi%d: %s rev %d %s\n", host->host_no, + hostdata->fast ? "53c700-66" : "53c700", + hostdata->rev, hostdata->differential ? + "(Differential)" : ""); + /* reset the chip */ + NCR_700_chip_reset(host); + outb(ASYNC_OPERATION , host->base + SXFER_REG); + + return host; +} + +int +NCR_700_release(struct Scsi_Host *host) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + kfree(hostdata->script); + return 1; +} + +static inline __u8 +NCR_700_identify(int can_disconnect, __u8 lun) +{ + return IDENTIFY_BASE | + ((can_disconnect) ? 0x40 : 0) | + (lun & NCR_700_LUN_MASK); +} + +/* + * Function : static int datapath_residual (Scsi_Host *host) + * + * Purpose : return residual data count of what's in the chip. If you + * really want to know what this function is doing, it's almost a + * direct transcription of the algorithm described in the 53c710 + * guide, except that the DBC and DFIFO registers are only 6 bits + * wide. + * + * Inputs : host - SCSI host */ +static inline int +NCR_700_data_residual (struct Scsi_Host *host) { + int count, synchronous; + unsigned int ddir; + + count = ((inb(host->base + DFIFO_REG) & 0x3f) - + (inl(host->base + DBC_REG) & 0x3f)) & 0x3f; + + synchronous = inb(host->base + SXFER_REG) & 0x0f; + + /* get the data direction */ + ddir = inb(host->base + CTEST0_REG) & 0x01; + + if (ddir) { + /* Receive */ + if (synchronous) + count += (inb(host->base + SSTAT2_REG) & 0xf0) >> 4; + else + if (inb(host->base + SSTAT1_REG) & SIDL_REG_FULL) + ++count; + } else { + /* Send */ + __u8 sstat = inb(host->base + SSTAT1_REG); + if (sstat & SODL_REG_FULL) + ++count; + if (synchronous && (sstat & SODR_REG_FULL)) + ++count; + } + return count; +} + +/* print out the SCSI wires and corresponding phase from the SBCL register + * in the chip */ +static inline char * +sbcl_to_string(__u8 sbcl) +{ + int i; + static char ret[256]; + + ret[0]='\0'; + for(i=0; i<8; i++) { + if((1<free_list; + + if(slot == NULL) { + /* sanity check */ + if(hostdata->command_slot_count != NCR_700_COMMAND_SLOTS_PER_HOST) + printk(KERN_ERR "SLOTS FULL, but count is %d, should be %d\n", hostdata->command_slot_count, NCR_700_COMMAND_SLOTS_PER_HOST); + return NULL; + } + + if(slot->state != NCR_700_SLOT_FREE) + /* should panic! */ + printk(KERN_ERR "BUSY SLOT ON FREE LIST!!!\n"); + + + hostdata->free_list = slot->ITL_forw; + slot->ITL_forw = NULL; + + + /* NOTE: set the state to busy here, not queued, since this + * indicates the slot is in use and cannot be run by the IRQ + * finish routine. If we cannot queue the command when it + * is properly build, we then change to NCR_700_SLOT_QUEUED */ + slot->state = NCR_700_SLOT_BUSY; + hostdata->command_slot_count++; + + return slot; +} + +STATIC void +free_slot(struct NCR_700_command_slot *slot, + struct NCR_700_Host_Parameters *hostdata) +{ + int hash; + struct NCR_700_command_slot **forw, **back; + + + if((slot->state & NCR_700_SLOT_MASK) != NCR_700_SLOT_MAGIC) { + printk(" SLOT %p is not MAGIC!!!\n", slot); + } + if(slot->state == NCR_700_SLOT_FREE) { + printk(" SLOT %p is FREE!!!\n", slot); + } + /* remove from queues */ + if(slot->tag != NCR_700_NO_TAG) { + hash = hash_ITLQ(slot->cmnd->target, slot->cmnd->lun, + slot->tag); + if(slot->ITLQ_forw == NULL) + back = &hostdata->ITLQ_Hash_back[hash]; + else + back = &slot->ITLQ_forw->ITLQ_back; + + if(slot->ITLQ_back == NULL) + forw = &hostdata->ITLQ_Hash_forw[hash]; + else + forw = &slot->ITLQ_back->ITLQ_forw; + + *forw = slot->ITLQ_forw; + *back = slot->ITLQ_back; + } + hash = hash_ITL(slot->cmnd->target, slot->cmnd->lun); + if(slot->ITL_forw == NULL) + back = &hostdata->ITL_Hash_back[hash]; + else + back = &slot->ITL_forw->ITL_back; + + if(slot->ITL_back == NULL) + forw = &hostdata->ITL_Hash_forw[hash]; + else + forw = &slot->ITL_back->ITL_forw; + + *forw = slot->ITL_forw; + *back = slot->ITL_back; + + slot->resume_offset = 0; + slot->cmnd = NULL; + slot->state = NCR_700_SLOT_FREE; + slot->ITL_forw = hostdata->free_list; + hostdata->free_list = slot; + hostdata->command_slot_count--; +} + + +/* This routine really does very little. The command is indexed on + the ITL and (if tagged) the ITLQ lists in _queuecommand */ +STATIC void +save_for_reselection(struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, __u32 dsp) +{ + /* Its just possible that this gets executed twice */ + if(SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + + slot->resume_offset = dsp; + } + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; +} + +/* Most likely nexus is the oldest in each case */ +STATIC inline struct NCR_700_command_slot * +find_ITL_Nexus(struct NCR_700_Host_Parameters *hostdata, __u8 pun, __u8 lun) +{ + int hash = hash_ITL(pun, lun); + struct NCR_700_command_slot *slot = hostdata->ITL_Hash_back[hash]; + while(slot != NULL && !(slot->cmnd->target == pun && + slot->cmnd->lun == lun)) + slot = slot->ITL_back; + return slot; +} + +STATIC inline struct NCR_700_command_slot * +find_ITLQ_Nexus(struct NCR_700_Host_Parameters *hostdata, __u8 pun, + __u8 lun, __u8 tag) +{ + int hash = hash_ITLQ(pun, lun, tag); + struct NCR_700_command_slot *slot = hostdata->ITLQ_Hash_back[hash]; + + while(slot != NULL && !(slot->cmnd->target == pun + && slot->cmnd->lun == lun && slot->tag == tag)) + slot = slot->ITLQ_back; + +#ifdef NCR_700_TAG_DEBUG + if(slot != NULL) { + struct NCR_700_command_slot *n = slot->ITLQ_back; + while(n != NULL && n->cmnd->target != pun + && n->cmnd->lun != lun && n->tag != tag) + n = n->ITLQ_back; + + if(n != NULL && n->cmnd->target == pun && n->cmnd->lun == lun + && n->tag == tag) { + printk("\n\n**WARNING: DUPLICATE tag %d\n", + tag); + } + } +#endif + return slot; +} + + + +/* This translates the SDTR message offset and period to a value + * which can be loaded into the SXFER_REG. + * + * NOTE: According to SCSI-2, the true transfer period is actually + * four times this period value (in ns) */ +STATIC inline __u8 +NCR_700_offset_period_to_sxfer(struct NCR_700_Host_Parameters *hostdata, + __u8 offset, __u8 period) +{ + int XFERP; + + XFERP = (period*4 * hostdata->clock)/1000 - 4; + if(offset > NCR_700_MAX_OFFSET) { + printk(KERN_WARNING "53c700: Offset %d exceeds maximum, setting to %d\n", + offset, NCR_700_MAX_OFFSET); + offset = NCR_700_MAX_OFFSET; + } + if(XFERP < NCR_700_MIN_XFERP) { + printk(KERN_WARNING "53c700: XFERP %d is less than minium, setting to %d\n", + XFERP, NCR_700_MIN_XFERP); + XFERP = NCR_700_MIN_XFERP; + } + return (offset & 0x0f) | (XFERP & 0x07)<<4; +} + + +STATIC inline void +NCR_700_scsi_done(struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, int result) +{ + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + + if(SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + + if(SCp->cmnd[0] == REQUEST_SENSE && SCp->cmnd[6] == NCR_700_INTERNAL_SENSE_MAGIC) { +#ifdef NCR_700_DEBUG + printk(" ORIGINAL CMD %p RETURNED %d, new return is %d sense is", + SCp, SCp->cmnd[7], result); + print_sense("53c700", SCp); +#endif + if(result == 0) + result = SCp->cmnd[7]; + } + + free_slot(slot, hostdata); + + SCp->host_scribble = NULL; + SCp->result = result; + SCp->scsi_done(SCp); + if(NCR_700_get_depth(SCp->device) == 0 || + NCR_700_get_depth(SCp->device) > NCR_700_MAX_TAGS) + printk(KERN_ERR "Invalid depth in NCR_700_scsi_done(): %d\n", + NCR_700_get_depth(SCp->device)); + NCR_700_set_depth(SCp->device, NCR_700_get_depth(SCp->device) - 1); + } else { + printk(KERN_ERR " SCSI DONE HAS NULL SCp\n"); + } +} + + +STATIC void +NCR_700_internal_bus_reset(struct Scsi_Host *host) +{ + /* Bus reset */ + outb(ASSERT_RST, host->base + SCNTL1_REG); + udelay(50); + outb(0, host->base + SCNTL1_REG); + +} + +STATIC void +NCR_700_chip_setup(struct Scsi_Host *host) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + outb(1 << host->this_id, host->base + SCID_REG); + outb(0, host->base + SBCL_REG); + outb(0, host->base + SXFER_REG); + + outb(PHASE_MM_INT | SEL_TIMEOUT_INT | GROSS_ERR_INT | UX_DISC_INT + | RST_INT | PAR_ERR_INT | SELECT_INT, host->base + SIEN_REG); + + outb(ABORT_INT | INT_INST_INT | ILGL_INST_INT, host->base + DIEN_REG); + outb(BURST_LENGTH_8, host->base + DMODE_REG); + outb(FULL_ARBITRATION | PARITY | AUTO_ATN, host->base + SCNTL0_REG); + outb(LAST_DIS_ENBL | ENABLE_ACTIVE_NEGATION|GENERATE_RECEIVE_PARITY, + host->base + CTEST8_REG); + outb(ENABLE_SELECT, host->base + SCNTL1_REG); + if(hostdata->clock > 50) + printk(KERN_ERR "53c700: Clock speed %dMHz is too high: contact James.Bottomley@HansenPartnership.com to modify the divider\n", hostdata->clock); + /* Set the synchronous SCSI divider to 1 so we drive the + * synchronous command clock at this speed */ + outb(1, host->base + SBCL_REG); + /* set the clock bits to 0 which is a divider of 2 so the + * async frequency is exactly half this speed */ + outb(0x00, host->base + DCNTL_REG); +} + +STATIC void +NCR_700_chip_reset(struct Scsi_Host *host) +{ + /* Chip reset */ + outb(SOFTWARE_RESET, host->base + DCNTL_REG); + udelay(100); + + outb(0, host->base + DCNTL_REG); + + mdelay(1000); + + NCR_700_chip_setup(host); +} + +/* The heart of the message processing engine is that the instruction + * immediately after the INT is the normal case (and so must be CLEAR + * ACK). If we want to do something else, we call that routine in + * scripts and set temp to be the normal case + 8 (skipping the CLEAR + * ACK) so that the routine returns correctly to resume its activity + * */ +STATIC __u32 +process_extended_message(struct Scsi_Host *host, + struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, __u32 dsp, __u32 dsps) +{ + __u32 resume_offset = dsp, temp = dsp + 8; + __u8 pun = 0xff, lun = 0xff; + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + switch(hostdata->msgin[2]) { + case A_SDTR_MSG: + if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) { + __u8 period = hostdata->msgin[3]; + __u8 offset = hostdata->msgin[4]; + __u8 sxfer; + + if(offset != 0 && period != 0) + sxfer = NCR_700_offset_period_to_sxfer(hostdata, offset, period); + else + sxfer = 0; + + if(sxfer != NCR_700_get_SXFER(SCp->device)) { + printk(KERN_INFO "scsi%d: (%d:%d) Synchronous at offset %d, period %dns\n", + host->host_no, pun, lun, + offset, period*4); + + NCR_700_set_SXFER(SCp->device, sxfer); + } + + + NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + + outb(NCR_700_get_SXFER(SCp->device), + host->base + SXFER_REG); + + } else { + /* SDTR message out of the blue, reject it */ + printk(KERN_WARNING "scsi%d Unexpected SDTR msg\n", + host->host_no); + hostdata->msgout[0] = A_REJECT_MSG; + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + } + break; + + case A_WDTR_MSG: + printk(KERN_INFO "scsi%d: (%d:%d), Unsolicited WDTR after CMD, Rejecting\n", + host->host_no, pun, lun); + hostdata->msgout[0] = A_REJECT_MSG; + script_patch_16(hostdata->script, MessageCount, 1); + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + + break; + + default: + printk(KERN_INFO "scsi%d (%d:%d): Unexpected message %s: ", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + print_msg(hostdata->msgin); + printk("\n"); + /* just reject it */ + hostdata->msgout[0] = A_REJECT_MSG; + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + } + outl(temp, host->base + TEMP_REG); + return resume_offset; +} + +STATIC __u32 +process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, __u32 dsp, __u32 dsps) +{ + /* work out where to return to */ + __u32 temp = dsp + 8, resume_offset = dsp; + __u8 pun = 0xff, lun = 0xff; + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + +#ifdef NCR_700_DEBUG + printk("scsi%d (%d:%d): message %s: ", host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + print_msg(hostdata->msgin); + printk("\n"); +#endif + + switch(hostdata->msgin[0]) { + + case A_EXTENDED_MSG: + return process_extended_message(host, hostdata, SCp, + dsp, dsps); + + case A_REJECT_MSG: + if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) { + /* Rejected our sync negotiation attempt */ + NCR_700_set_SXFER(SCp->device, 0); + NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + } else { + printk(KERN_WARNING "scsi%d (%d:%d) Unexpected REJECT Message %s\n", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + /* however, just ignore it */ + } + break; + + case A_PARITY_ERROR_MSG: + printk("scsi%d (%d:%d) Parity Error!\n", host->host_no, + pun, lun); + NCR_700_internal_bus_reset(host); + break; + case A_SIMPLE_TAG_MSG: + printk("scsi%d (%d:%d) SIMPLE TAG %d %s\n", host->host_no, + pun, lun, hostdata->msgin[1], + NCR_700_phase[(dsps & 0xf00) >> 8]); + /* just ignore it */ + break; + default: + printk(KERN_INFO "scsi%d (%d:%d): Unexpected message %s: ", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + + print_msg(hostdata->msgin); + printk("\n"); + /* just reject it */ + hostdata->msgout[0] = A_REJECT_MSG; + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + + break; + } + outl(temp, host->base + TEMP_REG); + return resume_offset; +} + +STATIC __u32 +process_script_interrupt(__u32 dsps, __u32 dsp, Scsi_Cmnd *SCp, + struct Scsi_Host *host, + struct NCR_700_Host_Parameters *hostdata) +{ + __u32 resume_offset = 0; + __u8 pun = 0xff, lun=0xff; + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + if(dsps == A_GOOD_STATUS_AFTER_STATUS) { + DEBUG((" COMMAND COMPLETE, status=%02x\n", + hostdata->status)); + /* check for contingent allegiance contitions */ + if(status_byte(hostdata->status) == CHECK_CONDITION || + status_byte(hostdata->status) == COMMAND_TERMINATED) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + + DEBUG((" cmd %p has status %d, requesting sense\n", + SCp, hostdata->status)); + /* we can destroy the command here because the + * contingent allegiance condition will cause a + * retry which will re-copy the command from the + * saved data_cmnd */ + SCp->cmnd[0] = REQUEST_SENSE; + SCp->cmnd[1] = (SCp->lun & 0x7) << 5; + SCp->cmnd[2] = 0; + SCp->cmnd[3] = 0; + SCp->cmnd[4] = sizeof(SCp->sense_buffer); + SCp->cmnd[5] = 0; + SCp->cmd_len = 6; + /* Here's a quiet hack: the REQUEST_SENSE command is + * six bytes, so store a flag indicating that this + * was an internal sense request and the original + * status at the end of the command */ + SCp->cmnd[6] = NCR_700_INTERNAL_SENSE_MAGIC; + SCp->cmnd[7] = hostdata->status; + slot->SG[0].ins = SCRIPT_MOVE_DATA_IN | sizeof(SCp->sense_buffer); + slot->SG[0].pAddr = virt_to_bus(SCp->sense_buffer); + slot->SG[1].ins = SCRIPT_RETURN; + slot->SG[1].pAddr = 0; + slot->resume_offset = hostdata->pScript; + + /* queue the command for reissue */ + slot->state = NCR_700_SLOT_QUEUED; + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + } else { + if(status_byte(hostdata->status) == GOOD && + SCp->cmnd[0] == INQUIRY && SCp->use_sg == 0) { + /* Piggy back the tag queueing support + * on this command */ + if(((char *)SCp->request_buffer)[7] & 0x02) { + printk(KERN_INFO "scsi%d: (%d:%d) Enabling Tag Command Queuing\n", host->host_no, pun, lun); + hostdata->tag_negotiated |= (1<target); + } else { + hostdata->tag_negotiated &= ~(1<target); + } + } + NCR_700_scsi_done(hostdata, SCp, hostdata->status); + } + } else if((dsps & 0xfffff0f0) == A_UNEXPECTED_PHASE) { + __u8 i = (dsps & 0xf00) >> 8; + + printk(KERN_ERR "scsi%d: (%d:%d), UNEXPECTED PHASE %s (%s)\n", + host->host_no, pun, lun, + NCR_700_phase[i], + sbcl_to_string(inb(host->base + SBCL_REG))); + printk(" len = %d, cmd =", SCp->cmd_len); + print_command(SCp->cmnd); + + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff000) == A_FATAL) { + int i = (dsps & 0xfff); + + printk(KERN_ERR "scsi%d: (%d:%d) FATAL ERROR: %s\n", + host->host_no, pun, lun, NCR_700_fatal_messages[i]); + if(dsps == A_FATAL_ILLEGAL_MSG_LENGTH) { + printk(KERN_ERR " msg begins %02x %02x\n", + hostdata->msgin[0], hostdata->msgin[1]); + } + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff0f0) == A_DISCONNECT) { +#ifdef NCR_700_DEBUG + __u8 i = (dsps & 0xf00) >> 8; + + printk("scsi%d: (%d:%d), DISCONNECTED (%d) %s\n", + host->host_no, pun, lun, + i, NCR_700_phase[i]); +#endif + save_for_reselection(hostdata, SCp, dsp); + + } else if(dsps == A_RESELECTION_IDENTIFIED) { + __u8 lun = hostdata->msgin[0] & 0x1f; + struct NCR_700_command_slot *slot; + __u8 reselection_id = hostdata->reselection_id; + + hostdata->reselection_id = 0xff; + DEBUG(("scsi%d: (%d:%d) RESELECTED!\n", + host->host_no, reselection_id, lun)); + /* clear the reselection indicator */ + if(hostdata->msgin[1] == A_SIMPLE_TAG_MSG) { + slot = find_ITLQ_Nexus(hostdata, reselection_id, + lun, hostdata->msgin[2]); + } else { + slot = find_ITL_Nexus(hostdata, reselection_id, lun); + } + retry: + if(slot == NULL) { + struct NCR_700_command_slot *s = find_ITL_Nexus(hostdata, reselection_id, lun); + printk(KERN_ERR "scsi%d: (%d:%d) RESELECTED but no saved command (MSG = %02x %02x %02x)!!\n", + host->host_no, reselection_id, lun, + hostdata->msgin[0], hostdata->msgin[1], + hostdata->msgin[2]); + printk(KERN_ERR " OUTSTANDING TAGS:"); + while(s != NULL) { + if(s->cmnd->target == reselection_id && + s->cmnd->lun == lun) { + printk("%d ", s->tag); + if(s->tag == hostdata->msgin[2]) { + printk(" ***FOUND*** \n"); + slot = s; + goto retry; + } + + } + s = s->ITL_back; + } + printk("\n"); + } else { + if(hostdata->state != NCR_700_HOST_BUSY) + printk(KERN_ERR "scsi%d: FATAL, host not busy during valid reselection!\n", + host->host_no); + resume_offset = slot->resume_offset; + hostdata->cmd = slot->cmnd; + + /* re-patch for this command */ + script_patch_32_abs(hostdata->script, CommandAddress, + virt_to_bus(slot->cmnd->cmnd)); + script_patch_16(hostdata->script, + CommandCount, slot->cmnd->cmd_len); + script_patch_32_abs(hostdata->script, SGScriptStartAddress, + virt_to_bus(&slot->SG[0].ins)); + + /* Note: setting SXFER only works if we're + * still in the MESSAGE phase, so it is vital + * that ACK is still asserted when we process + * the reselection message. The resume offset + * should therefore always clear ACK */ + outb(NCR_700_get_SXFER(hostdata->cmd->device), + host->base + SXFER_REG); + + } + } else if(dsps == A_RESELECTED_DURING_SELECTION) { + + /* This section is full of debugging code because I've + * never managed to reach it. I think what happens is + * that, because the 700 runs with selection + * interrupts enabled the whole time that we take a + * selection interrupt before we manage to get to the + * reselected script interrupt */ + + __u8 reselection_id = inb(host->base + SFBR_REG); + struct NCR_700_command_slot *slot; + + /* Take out our own ID */ + reselection_id &= ~(1<this_id); + + printk("scsi%d: (%d:%d) RESELECTION DURING SELECTION, dsp=%p[%04x] state=%d, count=%d\n", + host->host_no, reselection_id, lun, (void *)dsp, dsp - hostdata->pScript, hostdata->state, hostdata->command_slot_count); + + { + /* FIXME: DEBUGGING CODE */ + __u32 SG = (__u32)bus_to_virt(hostdata->script[A_SGScriptStartAddress_used[0]]); + int i; + + for(i=0; i< NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + if(SG >= (__u32)(&hostdata->slots[i].SG[0]) + && SG <= (__u32)(&hostdata->slots[i].SG[NCR_700_SG_SEGMENTS])) + break; + } + printk("IDENTIFIED SG segment as being %p in slot %p, cmd %p, slot->resume_offset=%p\n", (void *)SG, &hostdata->slots[i], hostdata->slots[i].cmnd, (void *)hostdata->slots[i].resume_offset); + SCp = hostdata->slots[i].cmnd; + } + + if(SCp != NULL) { + slot = (struct NCR_700_command_slot *)SCp->host_scribble; + /* change slot from busy to queued to redo command */ + slot->state = NCR_700_SLOT_QUEUED; + } + hostdata->cmd = NULL; + + if(reselection_id == 0) { + if(hostdata->reselection_id == 0xff) { + printk(KERN_ERR "scsi%d: Invalid reselection during selection!!\n", host->host_no); + return 0; + } else { + printk(KERN_ERR "scsi%d: script reselected and we took a selection interrupt\n", + host->host_no); + reselection_id = hostdata->reselection_id; + } + } else { + + /* convert to real ID */ + reselection_id = bitmap_to_number(reselection_id); + } + hostdata->reselection_id = reselection_id; + hostdata->msgin[1] = 0; + if(hostdata->tag_negotiated & (1<pScript + Ent_GetReselectionWithTag; + } else { + resume_offset = hostdata->pScript + Ent_GetReselectionData; + } + } else if(dsps == A_COMPLETED_SELECTION_AS_TARGET) { + /* we've just disconnected from the bus, do nothing since + * a return here will re-run the queued command slot + * that may have been interrupted by the initial selection */ + DEBUG((" SELECTION COMPLETED\n")); + } else if((dsps & 0xfffff0f0) == A_MSG_IN) { + resume_offset = process_message(host, hostdata, SCp, + dsp, dsps); + } else if((dsps & 0xfffff000) == 0) { + __u8 i = (dsps & 0xf0) >> 4, j = (dsps & 0xf00) >> 8; + printk(KERN_ERR "scsi%d: (%d:%d), unhandled script condition %s %s at %04x\n", + host->host_no, pun, lun, NCR_700_condition[i], + NCR_700_phase[j], dsp - hostdata->pScript); + if(SCp != NULL) { + print_command(SCp->cmnd); + + if(SCp->use_sg) { + for(i = 0; i < SCp->use_sg + 1; i++) { + printk(" SG[%d].length = %d, move_insn=%08x, addr %08x\n", i, ((struct scatterlist *)SCp->buffer)[i].length, ((struct NCR_700_command_slot *)SCp->host_scribble)->SG[i].ins, ((struct NCR_700_command_slot *)SCp->host_scribble)->SG[i].pAddr); + } + } + } + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff000) == A_DEBUG_INTERRUPT) { + printk("scsi%d (%d:%d) DEBUG INTERRUPT %d AT %p[%04x], continuing\n", + host->host_no, pun, lun, dsps & 0xfff, (void *)dsp, dsp - hostdata->pScript); + resume_offset = dsp; + } else { + printk(KERN_ERR "scsi%d: (%d:%d), unidentified script interrupt 0x%x at %04x\n", + host->host_no, pun, lun, dsps, dsp - hostdata->pScript); + NCR_700_internal_bus_reset(host); + } + return resume_offset; +} + +/* We run the 53c700 with selection interrupts always enabled. This + * means that the chip may be selected as soon as the bus frees. On a + * busy bus, this can be before the scripts engine finishes its + * processing. Therefore, part of the selection processing has to be + * to find out what the scripts engine is doing and complete the + * function if necessary (i.e. process the pending disconnect or save + * the interrupted initial selection */ +STATIC inline __u32 +process_selection(struct Scsi_Host *host, __u32 dsp) +{ + __u8 id; + int count = 0; + __u32 resume_offset = 0; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + Scsi_Cmnd *SCp = hostdata->cmd; + __u8 sbcl; + + for(count = 0; count < 5; count++) { + id = inb(host->base + SFBR_REG); + + /* Take out our own ID */ + id &= ~(1<this_id); + if(id != 0) + break; + udelay(5); + } + sbcl = inb(host->base + SBCL_REG); + if((sbcl & SBCL_IO) == 0) { + /* mark as having been selected rather than reselected */ + id = 0xff; + } else { + /* convert to real ID */ + hostdata->reselection_id = id = bitmap_to_number(id); + DEBUG(("scsi%d: Reselected by %d\n", + host->host_no, id)); + } + if(hostdata->state == NCR_700_HOST_BUSY && SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + DEBUG((" ID %d WARNING: RESELECTION OF BUSY HOST, saving cmd %p, slot %p, addr %p [%04x], resume %p!\n", id, hostdata->cmd, slot, dsp, dsp - hostdata->pScript, resume_offset)); + + switch(dsp - hostdata->pScript) { + case Ent_Disconnect1: + case Ent_Disconnect2: + save_for_reselection(hostdata, SCp, Ent_Disconnect2 + hostdata->pScript); + break; + case Ent_Disconnect3: + case Ent_Disconnect4: + save_for_reselection(hostdata, SCp, Ent_Disconnect4 + hostdata->pScript); + break; + case Ent_Disconnect5: + case Ent_Disconnect6: + save_for_reselection(hostdata, SCp, Ent_Disconnect6 + hostdata->pScript); + break; + case Ent_Disconnect7: + case Ent_Disconnect8: + save_for_reselection(hostdata, SCp, Ent_Disconnect8 + hostdata->pScript); + break; + case Ent_Finish1: + case Ent_Finish2: + process_script_interrupt(A_GOOD_STATUS_AFTER_STATUS, dsp, SCp, host, hostdata); + break; + + default: + slot->state = NCR_700_SLOT_QUEUED; + break; + } + } + hostdata->state = NCR_700_HOST_BUSY; + hostdata->cmd = NULL; + hostdata->msgin[1] = 0; + + if(id == 0xff) { + /* Selected as target, Ignore */ + resume_offset = hostdata->pScript + Ent_SelectedAsTarget; + } else if(hostdata->tag_negotiated & (1<pScript + Ent_GetReselectionWithTag; + } else { + resume_offset = hostdata->pScript + Ent_GetReselectionData; + } + return resume_offset; +} + + +STATIC int +NCR_700_start_command(Scsi_Cmnd *SCp) +{ + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + unsigned long flags; + __u16 count = 1; /* for IDENTIFY message */ + + save_flags(flags); + cli(); + if(hostdata->state != NCR_700_HOST_FREE) { + /* keep this inside the lock to close the race window where + * the running command finishes on another CPU while we don't + * change the state to queued on this one */ + slot->state = NCR_700_SLOT_QUEUED; + restore_flags(flags); + + DEBUG(("scsi%d: host busy, queueing command %p, slot %p\n", + SCp->host->host_no, slot->cmnd, slot)); + return 0; + } + hostdata->state = NCR_700_HOST_BUSY; + hostdata->cmd = SCp; + slot->state = NCR_700_SLOT_BUSY; + /* keep interrupts disabled until we have the command correctly + * set up so we cannot take a selection interrupt */ + + hostdata->msgout[0] = NCR_700_identify(SCp->cmnd[0] != REQUEST_SENSE, + SCp->lun); + /* for INQUIRY or REQUEST_SENSE commands, we cannot be sure + * if the negotiated transfer parameters still hold, so + * always renegotiate them */ + if(SCp->cmnd[0] == INQUIRY || SCp->cmnd[0] == REQUEST_SENSE) { + NCR_700_clear_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + } + + /* REQUEST_SENSE is asking for contingent I_T_L status. If a + * contingent allegiance condition exists, the device will + * refuse all tags, so send the request sense as untagged */ + if((hostdata->tag_negotiated & (1<target)) + && (slot->tag != NCR_700_NO_TAG && SCp->cmnd[0] != REQUEST_SENSE)) { + hostdata->msgout[count++] = A_SIMPLE_TAG_MSG; + hostdata->msgout[count++] = slot->tag; + } + + if(hostdata->fast && + NCR_700_is_flag_clear(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC)) { + memcpy(&hostdata->msgout[count], NCR_700_SDTR_msg, + sizeof(NCR_700_SDTR_msg)); + count += sizeof(NCR_700_SDTR_msg); + NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + } + + script_patch_16(hostdata->script, MessageCount, count); + + + script_patch_ID(hostdata->script, + Device_ID, 1<target); + + script_patch_32_abs(hostdata->script, CommandAddress, + virt_to_bus(SCp->cmnd)); + script_patch_16(hostdata->script, CommandCount, SCp->cmd_len); + /* finally plumb the beginning of the SG list into the script + * */ + script_patch_32_abs(hostdata->script, SGScriptStartAddress, + virt_to_bus(&slot->SG[0].ins)); + outb(CLR_FIFO, SCp->host->base + DFIFO_REG); + + /* set the synchronous period/offset */ + if(slot->resume_offset == 0) + slot->resume_offset = hostdata->pScript; + outb(NCR_700_get_SXFER(SCp->device), + SCp->host->base + SXFER_REG); + /* allow interrupts here so that if we're selected we can take + * a selection interrupt. The script start may not be + * effective in this case, but the selection interrupt will + * save our command in that case */ + outl(slot->temp, SCp->host->base + TEMP_REG); + outl(slot->resume_offset, SCp->host->base + DSP_REG); + restore_flags(flags); + + return 1; +} + +void +NCR_700_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct Scsi_Host *host = (struct Scsi_Host *)dev_id; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + __u8 istat; + __u32 resume_offset = 0; + __u8 pun = 0xff, lun = 0xff; + unsigned long flags; + + /* Unfortunately, we have to take the io_request_lock here + * rather than the host lock hostdata->lock because we're + * looking to exclude queuecommand from messing with the + * registers while we're processing the interrupt. Since + * queuecommand is called holding io_request_lock, and we have + * to take io_request_lock before we call the command + * scsi_done, we would get a deadlock if we took + * hostdata->lock here and in queuecommand (because the order + * of locking in queuecommand: 1) io_request_lock then 2) + * hostdata->lock would be the reverse of taking it in this + * routine */ + spin_lock_irqsave(&io_request_lock, flags); + if((istat = inb(host->base + ISTAT_REG)) + & (SCSI_INT_PENDING | DMA_INT_PENDING)) { + __u32 dsps; + __u8 sstat0 = 0, dstat = 0; + __u32 dsp; + Scsi_Cmnd *SCp = hostdata->cmd; + enum NCR_700_Host_State state; + + state = hostdata->state; + SCp = hostdata->cmd; + + if(istat & SCSI_INT_PENDING) { + udelay(10); + + sstat0 = inb(host->base + SSTAT0_REG); + } + + if(istat & DMA_INT_PENDING) { + udelay(10); + + dstat = inb(host->base + DSTAT_REG); + } + + dsps = inl(host->base + DSPS_REG); + dsp = inl(host->base + DSP_REG); + + DEBUG(("scsi%d: istat %02x sstat0 %02x dstat %02x dsp %04x[%08lx] dsps 0x%x\n", + host->host_no, istat, sstat0, dstat, + (dsp - (__u32)virt_to_bus(hostdata->script))/4, + dsp, dsps)); + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + if(sstat0 & SCSI_RESET_DETECTED) { + Scsi_Device *SDp; + int i; + + hostdata->state = NCR_700_HOST_BUSY; + + printk(KERN_ERR "scsi%d: Bus Reset detected, executing command %p, slot %p, dsp %p[%04x]\n", + host->host_no, SCp, SCp == NULL ? NULL : SCp->host_scribble, (void *)dsp, dsp - hostdata->pScript); + + /* clear all the negotiated parameters */ + for(SDp = host->host_queue; SDp != NULL; SDp = SDp->next) + SDp->hostdata = 0; + + /* clear all the slots and their pending commands */ + for(i = 0; i < NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + Scsi_Cmnd *SCp; + struct NCR_700_command_slot *slot = + &hostdata->slots[i]; + + if(slot->state == NCR_700_SLOT_FREE) + continue; + + SCp = slot->cmnd; + printk(KERN_ERR " failing command because of reset, slot %p, cmnd %p\n", + slot, SCp); + free_slot(slot, hostdata); + SCp->host_scribble = NULL; + NCR_700_set_depth(SCp->device, 0); + /* NOTE: deadlock potential here: we + * rely on mid-layer guarantees that + * scsi_done won't try to issue the + * command again otherwise we'll + * deadlock on the + * hostdata->state_lock */ + SCp->result = DID_RESET << 16; + SCp->scsi_done(SCp); + } + mdelay(25); + NCR_700_chip_setup(host); + + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + goto out_unlock; + } else if(sstat0 & SELECTION_TIMEOUT) { + DEBUG(("scsi%d: (%d:%d) selection timeout\n", + host->host_no, pun, lun)); + NCR_700_scsi_done(hostdata, SCp, DID_NO_CONNECT<<16); + } else if(sstat0 & PHASE_MISMATCH) { + struct NCR_700_command_slot *slot = (SCp == NULL) ? NULL : + (struct NCR_700_command_slot *)SCp->host_scribble; + + if(dsp == Ent_SendMessage + 8 + hostdata->pScript) { + /* It wants to reply to some part of + * our message */ + __u32 temp = inl(host->base + TEMP_REG); + + int count = (hostdata->script[Ent_SendMessage/4] & 0xffffff) - ((inl(host->base + DBC_REG) & 0xffffff) + NCR_700_data_residual(host)); + printk("scsi%d (%d:%d) PHASE MISMATCH IN SEND MESSAGE %d remain, return %p[%04x], phase %s\n", host->host_no, pun, lun, count, (void *)temp, temp - hostdata->pScript, sbcl_to_string(inb(host->base + SBCL_REG))); + resume_offset = hostdata->pScript + Ent_SendMessagePhaseMismatch; + } else if(dsp >= virt_to_bus(&slot->SG[0].ins) && + dsp <= virt_to_bus(&slot->SG[NCR_700_SG_SEGMENTS].ins)) { + int data_transfer = inl(host->base + DBC_REG) & 0xffffff; + int SGcount = (dsp - virt_to_bus(&slot->SG[0].ins))/sizeof(struct NCR_700_SG_List); + int residual = NCR_700_data_residual(host); + int i; +#ifdef NCR_700_DEBUG + printk("scsi%d: (%d:%d) Expected phase mismatch in slot->SG[%d], transferred 0x%x\n", + host->host_no, pun, lun, + SGcount, data_transfer); + print_command(SCp->cmnd); + if(residual) { + printk("scsi%d: (%d:%d) Expected phase mismatch in slot->SG[%d], transferred 0x%x, residual %d\n", + host->host_no, pun, lun, + SGcount, data_transfer, residual); + } +#endif + data_transfer += residual; + + if(data_transfer != 0) { + int count; + SGcount--; + + count = (slot->SG[SGcount].ins & 0x00ffffff); + DEBUG(("DATA TRANSFER MISMATCH, count = %d, transferred %d\n", count, count-data_transfer)); + slot->SG[SGcount].ins &= 0xff000000; + slot->SG[SGcount].ins |= data_transfer; + slot->SG[SGcount].pAddr += (count - data_transfer); + } + /* set the executed moves to nops */ + for(i=0; iSG[i].ins = SCRIPT_NOP; + slot->SG[i].pAddr = 0; + } + /* and pretend we disconnected after + * the command phase */ + resume_offset = hostdata->pScript + Ent_MsgInDuringData; + } else { + __u8 sbcl = inb(host->base + SBCL_REG); + printk(KERN_ERR "scsi%d: (%d:%d) phase mismatch at %04x, phase %s\n", + host->host_no, pun, lun, dsp - hostdata->pScript, sbcl_to_string(sbcl)); + NCR_700_internal_bus_reset(host); + } + + } else if(sstat0 & SCSI_GROSS_ERROR) { + printk(KERN_ERR "scsi%d: (%d:%d) GROSS ERROR\n", + host->host_no, pun, lun); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } else if(dstat & SCRIPT_INT_RECEIVED) { + DEBUG(("scsi%d: (%d:%d) ====>SCRIPT INTERRUPT<====\n", + host->host_no, pun, lun)); + resume_offset = process_script_interrupt(dsps, dsp, SCp, host, hostdata); + } else if(dstat & (ILGL_INST_DETECTED)) { + printk(KERN_ERR "scsi%d: (%d:%d) Illegal Instruction detected at 0x%p[0x%x]!!!\n" + " Please email James.Bottomley@HansenPartnership.com with the details\n", + host->host_no, pun, lun, + (void *)dsp, dsp - hostdata->pScript); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } else if(dstat & (WATCH_DOG_INTERRUPT|ABORTED)) { + printk(KERN_ERR "scsi%d: (%d:%d) serious DMA problem, dstat=%02x\n", + host->host_no, pun, lun, dstat); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } + + + /* NOTE: selection interrupt processing MUST occur + * after script interrupt processing to correctly cope + * with the case where we process a disconnect and + * then get reselected before we process the + * disconnection */ + if(sstat0 & SELECTED) { + /* FIXME: It currently takes at least FOUR + * interrupts to complete a command that + * disconnects: one for the disconnect, one + * for the reselection, one to get the + * reselection data and one to complete the + * command. If we guess the reselected + * command here and prepare it, we only need + * to get a reselection data interrupt if we + * guessed wrongly. Since the interrupt + * overhead is much greater than the command + * setup, this would be an efficient + * optimisation particularly as we probably + * only have one outstanding command on a + * target most of the time */ + + resume_offset = process_selection(host, dsp); + + } + + } + + if(resume_offset) { + if(hostdata->state != NCR_700_HOST_BUSY) { + printk(KERN_ERR "scsi%d: Driver error: resume at %p [%04x] with non busy host!\n", + host->host_no, (void *)resume_offset, resume_offset - hostdata->pScript); + hostdata->state = NCR_700_HOST_BUSY; + } + + DEBUG(("Attempting to resume at %x\n", resume_offset)); + outb(CLR_FIFO, host->base + DFIFO_REG); + outl(resume_offset, host->base + DSP_REG); + } + /* There is probably a technical no-no about this: If we're a + * shared interrupt and we got this interrupt because the + * other device needs servicing not us, we're still going to + * check our queued commands here---of course, there shouldn't + * be any outstanding.... */ + if(hostdata->state == NCR_700_HOST_FREE) { + int i; + + for(i = 0; i < NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + /* fairness: always run the queue from the last + * position we left off */ + int j = (i + hostdata->saved_slot_position) + % NCR_700_COMMAND_SLOTS_PER_HOST; + + if(hostdata->slots[j].state != NCR_700_SLOT_QUEUED) + continue; + if(NCR_700_start_command(hostdata->slots[j].cmnd)) { + DEBUG(("scsi%d: Issuing saved command slot %p, cmd %p\t\n", + host->host_no, &hostdata->slots[j], + hostdata->slots[j].cmnd)); + hostdata->saved_slot_position = j + 1; + } + + break; + } + } + out_unlock: + spin_unlock_irqrestore(&io_request_lock, flags); +} + +/* FIXME: Need to put some proc information in and plumb it + * into the scsi proc system */ +STATIC int +NCR_700_proc_directory_info(char *proc_buf, char **startp, + off_t offset, int bytes_available, + int host_no, int write) +{ + static char buf[4096]; /* 1 page should be sufficient */ + int len = 0; + struct Scsi_Host *host = scsi_hostlist; + struct NCR_700_Host_Parameters *hostdata; + Scsi_Device *SDp; + + while(host != NULL && host->host_no != host_no) + host = host->next; + + if(host == NULL) + return 0; + + if(write) { + /* FIXME: Clear internal statistics here */ + return 0; + } + hostdata = (struct NCR_700_Host_Parameters *)host->hostdata[0]; + len += sprintf(&buf[len], "Total commands outstanding: %d\n", hostdata->command_slot_count); + len += sprintf(&buf[len],"\ +Target Depth Active Next Tag\n\ +====== ===== ====== ========\n"); + for(SDp = host->host_queue; SDp != NULL; SDp = SDp->next) { + len += sprintf(&buf[len],"%2d:%2d %4d %4d %4d\n", SDp->id, SDp->lun, SDp->queue_depth, NCR_700_get_depth(SDp), SDp->current_tag); + } + if((len -= offset) <= 0) + return 0; + if(len > bytes_available) + len = bytes_available; + memcpy(proc_buf, buf + offset, len); + return len; +} + +STATIC int +NCR_700_queuecommand(Scsi_Cmnd *SCp, void (*done)(Scsi_Cmnd *)) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + __u32 move_ins; + struct NCR_700_command_slot *slot; + int hash; + + if(hostdata->command_slot_count >= NCR_700_COMMAND_SLOTS_PER_HOST) { + /* We're over our allocation, this should never happen + * since we report the max allocation to the mid layer */ + printk(KERN_WARNING "scsi%d: Command depth has gone over queue depth\n", SCp->host->host_no); + return 1; + } + if(NCR_700_get_depth(SCp->device) != 0 && !(hostdata->tag_negotiated & (1<target))) { + DEBUG((KERN_ERR "scsi%d (%d:%d) has non zero depth %d\n", + SCp->host->host_no, SCp->target, SCp->lun, + NCR_700_get_depth(SCp->device))); + return 1; + } + if(NCR_700_get_depth(SCp->device) >= NCR_700_MAX_TAGS) { + DEBUG((KERN_ERR "scsi%d (%d:%d) has max tag depth %d\n", + SCp->host->host_no, SCp->target, SCp->lun, + NCR_700_get_depth(SCp->device))); + return 1; + } + NCR_700_set_depth(SCp->device, NCR_700_get_depth(SCp->device) + 1); + + /* begin the command here */ + /* no need to check for NULL, test for command_slot_cound above + * ensures a slot is free */ + slot = find_empty_slot(hostdata); + + slot->cmnd = SCp; + + SCp->scsi_done = done; + SCp->host_scribble = (unsigned char *)slot; + SCp->SCp.ptr = NULL; + SCp->SCp.buffer = NULL; + +#ifdef NCR_700_DEBUG + printk("53c700: scsi%d, command ", SCp->host->host_no); + print_command(SCp->cmnd); +#endif + + if(hostdata->tag_negotiated &(1<target)) { + + struct NCR_700_command_slot *old = + find_ITL_Nexus(hostdata, SCp->target, SCp->lun); +#ifdef NCR_700_TAG_DEBUG + struct NCR_700_command_slot *found; +#endif + + if(old != NULL && old->tag == SCp->device->current_tag) { + printk(KERN_WARNING "scsi%d (%d:%d) Tag clock back to current, queueing\n", SCp->host->host_no, SCp->target, SCp->lun); + return 1; + } + slot->tag = SCp->device->current_tag++; +#ifdef NCR_700_TAG_DEBUG + while((found = find_ITLQ_Nexus(hostdata, SCp->target, SCp->lun, slot->tag)) != NULL) { + printk("\n\n**ERROR** already using tag %d, but oldest is %d\n", slot->tag, (old == NULL) ? -1 : old->tag); + printk(" FOUND = %p, tag = %d, pun = %d, lun = %d\n", + found, found->tag, found->cmnd->target, found->cmnd->lun); + slot->tag = SCp->device->current_tag++; + printk(" Tag list is: "); + while(old != NULL) { + if(old->cmnd->target == SCp->target && + old->cmnd->lun == SCp->lun) + printk("%d ", old->tag); + old = old->ITL_back; + } + printk("\n\n"); + } +#endif + hash = hash_ITLQ(SCp->target, SCp->lun, slot->tag); + /* link into the ITLQ hash queues */ + slot->ITLQ_forw = hostdata->ITLQ_Hash_forw[hash]; + hostdata->ITLQ_Hash_forw[hash] = slot; +#ifdef NCR_700_TAG_DEBUG + if(slot->ITLQ_forw != NULL && slot->ITLQ_forw->ITLQ_back != NULL) { + printk(KERN_ERR "scsi%d (%d:%d) ITLQ_back is not NULL!!!!\n", SCp->host->host_no, SCp->target, SCp->lun); + } +#endif + if(slot->ITLQ_forw != NULL) + slot->ITLQ_forw->ITLQ_back = slot; + else + hostdata->ITLQ_Hash_back[hash] = slot; + slot->ITLQ_back = NULL; + } else { + slot->tag = NCR_700_NO_TAG; + } + /* link into the ITL hash queues */ + hash = hash_ITL(SCp->target, SCp->lun); + slot->ITL_forw = hostdata->ITL_Hash_forw[hash]; + hostdata->ITL_Hash_forw[hash] = slot; +#ifdef NCR_700_TAG_DEBUG + if(slot->ITL_forw != NULL && slot->ITL_forw->ITL_back != NULL) { + printk(KERN_ERR "scsi%d (%d:%d) ITL_back is not NULL!!!!\n", + SCp->host->host_no, SCp->target, SCp->lun); + } +#endif + if(slot->ITL_forw != NULL) + slot->ITL_forw->ITL_back = slot; + else + hostdata->ITL_Hash_back[hash] = slot; + slot->ITL_back = NULL; + + + /* This is f****g ridiculous; every low level HBA driver has + * to determine the direction of the commands, why isn't this + * done inside the scsi_lib !!??? */ + switch (SCp->cmnd[0]) { + case REQUEST_SENSE: + /* clear the internal sense magic */ + SCp->cmnd[6] = 0; + /* fall through */ + case INQUIRY: + case MODE_SENSE: + case READ_6: + case READ_10: + case READ_12: + case READ_CAPACITY: + case READ_BLOCK_LIMITS: + case READ_TOC: + move_ins = SCRIPT_MOVE_DATA_IN; + break; + case MODE_SELECT: + case WRITE_6: + case WRITE_10: + case WRITE_12: + move_ins = SCRIPT_MOVE_DATA_OUT; + break; + case TEST_UNIT_READY: + case ALLOW_MEDIUM_REMOVAL: + case START_STOP: + move_ins = 0; + break; + default: + /* OK, get it from the command */ + switch(SCp->sc_data_direction) { + case SCSI_DATA_UNKNOWN: + default: + printk(KERN_ERR "53c700: Unknown command for data direction "); + print_command(SCp->cmnd); + + move_ins = 0; + break; + case SCSI_DATA_NONE: + move_ins = 0; + break; + case SCSI_DATA_READ: + move_ins = SCRIPT_MOVE_DATA_IN; + break; + case SCSI_DATA_WRITE: + move_ins = SCRIPT_MOVE_DATA_OUT; + break; + } + } + + /* now build the scatter gather list */ + if(move_ins != 0) { + int i; + + for(i = 0; i < (SCp->use_sg ? SCp->use_sg : 1); i++) { + void *vPtr; + __u32 count; + + if(SCp->use_sg) { + vPtr = (((struct scatterlist *)SCp->buffer)[i].address); + count = ((struct scatterlist *)SCp->buffer)[i].length; + } else { + vPtr = SCp->request_buffer; + count = SCp->request_bufflen; + } + slot->SG[i].ins = move_ins | count; + DEBUG((" scatter block %d: move %d[%08lx] from 0x%lx\n", + i, count, slot->SG[i].move_ins, + virt_to_bus(vPtr))); + slot->SG[i].pAddr = virt_to_bus(vPtr); + } + slot->SG[i].ins = SCRIPT_RETURN; + slot->SG[i].pAddr = 0; + DEBUG((" SETTING %08x to %x\n", + virt_to_bus(&slot->SG[i].ins), + slot->SG[i].ins)); + } + slot->resume_offset = 0; + NCR_700_start_command(SCp); + return 0; +} + +STATIC int +NCR_700_abort(Scsi_Cmnd * SCp) +{ + struct NCR_700_command_slot *slot; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + + printk("scsi%d (%d:%d) New error handler wants to abort command\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + slot = find_ITL_Nexus(hostdata, SCp->target, SCp->lun); + while(slot != NULL && slot->cmnd != SCp) + slot = slot->ITL_back; + + if(slot == NULL) + /* no outstanding command to abort */ + return SUCCESS; + if(SCp->cmnd[0] == TEST_UNIT_READY) { + /* FIXME: This is because of a problem in the new + * error handler. When it is in error recovery, it + * will send a TUR to a device it thinks may still be + * showing a problem. If the TUR isn't responded to, + * it will abort it and mark the device off line. + * Unfortunately, it does no other error recovery, so + * this would leave us with an outstanding command + * occupying a slot. Rather than allow this to + * happen, we issue a bus reset to force all + * outstanding commands to terminate here. */ + NCR_700_internal_bus_reset(SCp->host); + /* still drop through and return failed */ + } + return FAILED; + +} + +STATIC int +NCR_700_bus_reset(Scsi_Cmnd * SCp) +{ + printk("scsi%d (%d:%d) New error handler wants BUS reset, cmd %p\n\t", + SCp->host->host_no, SCp->target, SCp->lun, SCp); + print_command(SCp->cmnd); + NCR_700_internal_bus_reset(SCp->host); + return SUCCESS; +} + +STATIC int +NCR_700_dev_reset(Scsi_Cmnd * SCp) +{ + printk("scsi%d (%d:%d) New error handler wants device reset\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + return FAILED; +} + +STATIC int +NCR_700_host_reset(Scsi_Cmnd * SCp) +{ + printk("scsi%d (%d:%d) New error handler wants HOST reset\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + NCR_700_internal_bus_reset(SCp->host); + NCR_700_chip_reset(SCp->host); + return SUCCESS; +} + +EXPORT_SYMBOL(NCR_700_detect); +EXPORT_SYMBOL(NCR_700_release); +EXPORT_SYMBOL(NCR_700_intr); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/53c700.h linux.ac/drivers/scsi/53c700.h --- linux.vanilla/drivers/scsi/53c700.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/53c700.h Sun May 13 20:25:46 2001 @@ -0,0 +1,391 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* Driver for 53c700 and 53c700-66 chips from NCR and Symbios + * + * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com + */ + +#ifndef _53C700_H +#define _53C700_H + +/* Turn on for general debugging---too verbose for normal use */ +#undef NCR_700_DEBUG +/* Debug the tag queues, checking hash queue allocation and deallocation + * and search for duplicate tags */ +#undef NCR_700_TAG_DEBUG + +#ifdef NCR_700_DEBUG +#define DEBUG(x) printk x +#else +#define DEBUG(x) +#endif + +/* The number of available command slots */ +#define NCR_700_COMMAND_SLOTS_PER_HOST 64 +/* The maximum number of Scatter Gathers we allow */ +#define NCR_700_SG_SEGMENTS 32 +/* The maximum number of luns (make this of the form 2^n) */ +#define NCR_700_MAX_LUNS 32 +#define NCR_700_LUN_MASK (NCR_700_MAX_LUNS - 1) +/* Alter this with care: too many tags won't give the elevator a chance to + * work; too few will cause the device to operate less efficiently */ +#define NCR_700_MAX_TAGS 16 +/* magic byte identifying an internally generated REQUEST_SENSE command */ +#define NCR_700_INTERNAL_SENSE_MAGIC 0x42 + + +struct NCR_700_Host_Parameters; + +/* These are the externally used routines */ +struct Scsi_Host *NCR_700_detect(Scsi_Host_Template *, struct NCR_700_Host_Parameters *); +int NCR_700_release(struct Scsi_Host *host); +void NCR_700_intr(int, void *, struct pt_regs *); + + +enum NCR_700_Host_State { + NCR_700_HOST_BUSY, + NCR_700_HOST_FREE, +}; + +struct NCR_700_SG_List { + /* The following is a script fragment to move the buffer onto the + * bus and then link the next fragment or return */ + #define SCRIPT_MOVE_DATA_IN 0x09000000 + #define SCRIPT_MOVE_DATA_OUT 0x08000000 + __u32 ins; + __u32 pAddr; + #define SCRIPT_NOP 0x80000000 + #define SCRIPT_RETURN 0x90080000 +}; + +/* We use device->hostdata to store negotiated parameters. This is + * supposed to be a pointer to a device private area, but we cannot + * really use it as such since it will never be freed, so just use the + * 32 bits to cram the information. The SYNC negotiation sequence looks + * like: + * + * If DEV_NEGOTIATED_SYNC not set, tack and SDTR message on to the + * initial identify for the device and set DEV_BEGIN_SYNC_NEGOTATION + * If we get an SDTR reply, work out the SXFER parameters, squirrel + * them away here, clear DEV_BEGIN_SYNC_NEGOTIATION and set + * DEV_NEGOTIATED_SYNC. If we get a REJECT msg, squirrel + * + * + * 0:7 SXFER_REG negotiated value for this device + * 8:15 Current queue depth + * 16 negotiated SYNC flag + * 17 begin SYNC negotiation flag + * 18 device supports tag queueing */ +#define NCR_700_DEV_NEGOTIATED_SYNC (1<<16) +#define NCR_700_DEV_BEGIN_SYNC_NEGOTIATION (1<<17) +#define NCR_700_DEV_USES_TAG_QUEUEING (1<<18) + +static inline void +NCR_700_set_SXFER(Scsi_Device *SDp, __u8 sxfer) +{ + ((__u32)SDp->hostdata) &= 0xffffff00; + ((__u32)SDp->hostdata) |= sxfer & 0xff; +} +static inline __u8 NCR_700_get_SXFER(Scsi_Device *SDp) +{ + return (((__u32)SDp->hostdata) & 0xff); +} +static inline void +NCR_700_set_depth(Scsi_Device *SDp, __u8 depth) +{ + ((__u32)SDp->hostdata) &= 0xffff00ff; + ((__u32)SDp->hostdata) |= (0xff00 & (depth << 8)); +} +static inline __u8 +NCR_700_get_depth(Scsi_Device *SDp) +{ + return ((((__u32)SDp->hostdata) & 0xff00)>>8); +} +static inline int +NCR_700_is_flag_set(Scsi_Device *SDp, __u32 flag) +{ + return (((__u32)SDp->hostdata) & flag) == flag; +} +static inline int +NCR_700_is_flag_clear(Scsi_Device *SDp, __u32 flag) +{ + return (((__u32)SDp->hostdata) & flag) == 0; +} +static inline void +NCR_700_set_flag(Scsi_Device *SDp, __u32 flag) +{ + ((__u32)SDp->hostdata) |= (flag & 0xffff0000); +} +static inline void +NCR_700_clear_flag(Scsi_Device *SDp, __u32 flag) +{ + ((__u32)SDp->hostdata) &= ~(flag & 0xffff0000); +} + +/* These represent the Nexus hashing functions. A Nexus in SCSI terms + * just means the identification of an outstanding command, by ITL + * (Initiator Target Lun) or ITLQ (Initiator Target Lun Tag). I'm not + * very keen on XOR based hashes, so these are based on number theory + * instead. All you need to do is to fix your hash bucket size and + * then choose reasonable strides which are coprime with the chosen + * bucket size + * + * Note: this mathematical hash can be made very efficient, if the + * compiler is good at optimising: Choose the number of buckets to be + * 2^n and the modulo becomes a logical and with (2^n-1). + * Additionally, if you chose the coprimes of the form 2^n-2^n the + * multiplication can be done by a shift and an addition. */ +#define MAX_ITL_HASH_BUCKETS 16 +#define ITL_HASH_PRIME 7 + +#define MAX_ITLQ_HASH_BUCKETS 64 +#define ITLQ_PUN_PRIME 7 +#define ITLQ_LUN_PRIME 3 + +static inline int +hash_ITL(__u8 pun, __u8 lun) +{ + return (pun*ITL_HASH_PRIME + lun) % MAX_ITL_HASH_BUCKETS; +} + +static inline int +hash_ITLQ(__u8 pun, __u8 lun, __u8 tag) +{ + return (pun*ITLQ_PUN_PRIME + lun*ITLQ_LUN_PRIME + tag) % MAX_ITLQ_HASH_BUCKETS; +} + +struct NCR_700_command_slot { + #define NCR_700_SLOT_MASK 0xFC + #define NCR_700_SLOT_MAGIC 0xb8 + #define NCR_700_SLOT_FREE (0|NCR_700_SLOT_MAGIC) /* slot may be used */ + #define NCR_700_SLOT_BUSY (1|NCR_700_SLOT_MAGIC) /* slot has command active on HA */ + #define NCR_700_SLOT_QUEUED (2|NCR_700_SLOT_MAGIC) /* slot has command to be made active on HA */ + __u8 state; + #define NCR_700_NO_TAG 0xdead + __u16 tag; + struct NCR_700_SG_List SG[NCR_700_SG_SEGMENTS+1]; + __u32 resume_offset; + Scsi_Cmnd *cmnd; + __u32 temp; + /* Doubly linked ITL/ITLQ list kept in strict time order + * (latest at the back) */ + struct NCR_700_command_slot *ITL_forw; + struct NCR_700_command_slot *ITL_back; + struct NCR_700_command_slot *ITLQ_forw; + struct NCR_700_command_slot *ITLQ_back; +}; + +struct NCR_700_Host_Parameters { + /* These must be filled in by the calling driver */ + int clock; /* board clock speed in MHz */ + __u32 base; /* the base for the port (copied to host) */ + __u8 differential:1; /* if we are differential */ + + /* NOTHING BELOW HERE NEEDS ALTERING */ + __u8 fast:1; /* if we can alter the SCSI bus clock + speed (so can negiotiate sync) */ + + __u32 *script; /* pointer to script location */ + __u32 pScript; /* physical mem addr of script */ + + /* This will be the host lock. Unfortunately, we can't use it + * at the moment because of the necessity of holding the + * io_request_lock */ + spinlock_t lock; + enum NCR_700_Host_State state; /* protected by state lock */ + Scsi_Cmnd *cmd; + + __u8 msgout[8]; + __u8 msgout_size; + __u8 tag_negotiated; + __u8 status; + __u8 msgin[8]; + struct NCR_700_command_slot *slots; + int saved_slot_position; + int command_slot_count; /* protected by state lock */ + __u8 rev; + __u8 reselection_id; + /* flags for the host */ + + /* ITL list. ALL outstanding commands are hashed here in strict + * order, latest at the back */ + struct NCR_700_command_slot *ITL_Hash_forw[MAX_ITL_HASH_BUCKETS]; + struct NCR_700_command_slot *ITL_Hash_back[MAX_ITL_HASH_BUCKETS]; + + /* Only tagged outstanding commands are hashed here (also latest + * at the back) */ + struct NCR_700_command_slot *ITLQ_Hash_forw[MAX_ITLQ_HASH_BUCKETS]; + struct NCR_700_command_slot *ITLQ_Hash_back[MAX_ITLQ_HASH_BUCKETS]; + + /* Free list, singly linked by ITL_forw elements */ + struct NCR_700_command_slot *free_list; +}; + +/* + * 53C700 Register Interface - the offset from the Selected base + * I/O address */ + +#define SCNTL0_REG 0x00 +#define FULL_ARBITRATION 0xc0 +#define PARITY 0x08 +#define ENABLE_PARITY 0x04 +#define AUTO_ATN 0x02 +#define SCNTL1_REG 0x01 +#define SLOW_BUS 0x80 +#define ENABLE_SELECT 0x20 +#define ASSERT_RST 0x08 +#define ASSERT_EVEN_PARITY 0x04 +#define SDID_REG 0x02 +#define SIEN_REG 0x03 +#define PHASE_MM_INT 0x80 +#define FUNC_COMP_INT 0x40 +#define SEL_TIMEOUT_INT 0x20 +#define SELECT_INT 0x10 +#define GROSS_ERR_INT 0x08 +#define UX_DISC_INT 0x04 +#define RST_INT 0x02 +#define PAR_ERR_INT 0x01 +#define SCID_REG 0x04 +#define SXFER_REG 0x05 +#define ASYNC_OPERATION 0x00 +#define SODL_REG 0x06 +#define SOCL_REG 0x07 +#define SFBR_REG 0x08 +#define SIDL_REG 0x09 +#define SBDL_REG 0x0A +#define SBCL_REG 0x0B +#define SBCL_IO 0x01 +#define DSTAT_REG 0x0C +#define ILGL_INST_DETECTED 0x01 +#define WATCH_DOG_INTERRUPT 0x02 +#define SCRIPT_INT_RECEIVED 0x04 +#define ABORTED 0x10 +#define SSTAT0_REG 0x0D +#define PARITY_ERROR 0x01 +#define SCSI_RESET_DETECTED 0x02 +#define UNEXPECTED_DISCONNECT 0x04 +#define SCSI_GROSS_ERROR 0x08 +#define SELECTED 0x10 +#define SELECTION_TIMEOUT 0x20 +#define FUNCTION_COMPLETE 0x40 +#define PHASE_MISMATCH 0x80 +#define SSTAT1_REG 0x0E +#define SIDL_REG_FULL 0x80 +#define SODR_REG_FULL 0x40 +#define SODL_REG_FULL 0x20 +#define SSTAT2_REG 0x0F +#define CTEST0_REG 0x14 +#define CTEST1_REG 0x15 +#define CTEST2_REG 0x16 +#define CTEST3_REG 0x17 +#define CTEST4_REG 0x18 +#define DISABLE_FIFO 0x00 +#define SLBE 0x10 +#define SFWR 0x08 +#define BYTE_LANE0 0x04 +#define BYTE_LANE1 0x05 +#define BYTE_LANE2 0x06 +#define BYTE_LANE3 0x07 +#define SCSI_ZMODE 0x20 +#define ZMODE 0x40 +#define CTEST5_REG 0x19 +#define MASTER_CONTROL 0x10 +#define DMA_DIRECTION 0x08 +#define CTEST7_REG 0x1B +#define DFP 0x08 +#define EVP 0x04 +#define DIFF 0x01 +#define CTEST6_REG 0x1A +#define TEMP_REG 0x1C +#define DFIFO_REG 0x20 +#define FLUSH_DMA_FIFO 0x80 +#define CLR_FIFO 0x40 +#define ISTAT_REG 0x21 +#define ABORT_OPERATION 0x80 +#define DMA_INT_PENDING 0x01 +#define SCSI_INT_PENDING 0x02 +#define CONNECTED 0x08 +#define CTEST8_REG 0x22 +#define LAST_DIS_ENBL 0x01 +#define SHORTEN_FILTERING 0x04 +#define ENABLE_ACTIVE_NEGATION 0x10 +#define GENERATE_RECEIVE_PARITY 0x20 +#define CTEST9_REG 0x23 +#define DBC_REG 0x24 +#define DCMD_REG 0x27 +#define DNAD_REG 0x28 +#define DIEN_REG 0x39 +#define ABORT_INT 0x10 +#define INT_INST_INT 0x04 +#define WD_INT 0x02 +#define ILGL_INST_INT 0x01 +#define DCNTL_REG 0x3B +#define SOFTWARE_RESET 0x01 +#define SCRPTS_16BITS 0x20 +#define DMODE_REG 0x34 +#define BURST_LENGTH_1 0x00 +#define BURST_LENGTH_2 0x40 +#define BURST_LENGTH_4 0x80 +#define BURST_LENGTH_8 0xC0 +#define BW16 32 +#define MODE_286 16 +#define IO_XFER 8 +#define FIXED_ADDR 4 + +#define DSP_REG 0x2C +#define DSPS_REG 0x30 + +/* Parameters to begin SDTR negotiations. Empirically, I find that + * the 53c700-66 cannot handle an offset >8, so don't change this */ +#define NCR_700_MAX_OFFSET 8 +#define NCR_700_MIN_XFERP 1 +#define NCR_700_MIN_PERIOD 25 /* for SDTR message, 100ns */ + +#define script_patch_32(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + (script)[A_##symbol##_used[i]] += (value); \ + DEBUG((" script, patching %s at %ld to 0x%lx\n", \ + #symbol, A_##symbol##_used[i], (value))); \ + } \ +} + +#define script_patch_32_abs(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + (script)[A_##symbol##_used[i]] = (value); \ + DEBUG((" script, patching %s at %ld to 0x%lx\n", \ + #symbol, A_##symbol##_used[i], (value))); \ + } \ +} + +/* Used for patching the SCSI ID in the SELECT instruction */ +#define script_patch_ID(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + __u32 val = (script)[A_##symbol##_used[i]]; \ + val &= 0xff00ffff; \ + val |= ((value) & 0xff) << 16; \ + (script)[A_##symbol##_used[i]] = val; \ + DEBUG((" script, patching ID field %s at %ld to 0x%lx\n", \ + #symbol, A_##symbol##_used[i], val)); \ + } \ +} + +#define script_patch_16(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + __u32 val = (script)[A_##symbol##_used[i]]; \ + val &= 0xffff0000; \ + val |= ((value) & 0xffff); \ + (script)[A_##symbol##_used[i]] = val; \ + DEBUG((" script, patching ID field %s at %ld to 0x%lx\n", \ + #symbol, A_##symbol##_used[i], val)); \ + } \ +} + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/53c700.scr linux.ac/drivers/scsi/53c700.scr --- linux.vanilla/drivers/scsi/53c700.scr Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/53c700.scr Sun May 13 20:25:46 2001 @@ -0,0 +1,404 @@ +; Script for the NCR (or symbios) 53c700 and 53c700-66 chip +; +; Copyright (C) 2001 James.Bottomley@HansenPartnership.com +;;----------------------------------------------------------------------------- +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; 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. +;; +;;----------------------------------------------------------------------------- +; +; This script is designed to be modified for the particular command in +; operation. The particular variables pertaining to the commands are: +; +ABSOLUTE Device_ID = 0 ; ID of target for command +ABSOLUTE MessageCount = 0 ; Number of bytes in message +ABSOLUTE MessageLocation = 0 ; Addr of message +ABSOLUTE CommandCount = 0 ; Number of bytes in command +ABSOLUTE CommandAddress = 0 ; Addr of Command +ABSOLUTE StatusAddress = 0 ; Addr to receive status return +ABSOLUTE ReceiveMsgAddress = 0 ; Addr to receive msg +; +; This is the magic component for handling scatter-gather. Each of the +; SG components is preceeded by a script fragment which moves the +; necessary amount of data and jumps to the next SG segment. The final +; SG segment jumps back to . However, this address is the first SG script +; segment. +; +ABSOLUTE SGScriptStartAddress = 0 + +; The following represent status interrupts we use 3 hex digits for +; this: 0xPRS where + +; P: +ABSOLUTE AFTER_SELECTION = 0x100 +ABSOLUTE BEFORE_CMD = 0x200 +ABSOLUTE AFTER_CMD = 0x300 +ABSOLUTE AFTER_STATUS = 0x400 +ABSOLUTE AFTER_DATA_IN = 0x500 +ABSOLUTE AFTER_DATA_OUT = 0x600 +ABSOLUTE DURING_DATA_IN = 0x700 + +; R: +ABSOLUTE NOT_MSG_OUT = 0x10 +ABSOLUTE UNEXPECTED_PHASE = 0x20 +ABSOLUTE NOT_MSG_IN = 0x30 +ABSOLUTE UNEXPECTED_MSG = 0x40 +ABSOLUTE MSG_IN = 0x50 +ABSOLUTE SDTR_MSG_R = 0x60 +ABSOLUTE REJECT_MSG_R = 0x70 +ABSOLUTE DISCONNECT = 0x80 +ABSOLUTE MSG_OUT = 0x90 +ABSOLUTE WDTR_MSG_R = 0xA0 + +; S: +ABSOLUTE GOOD_STATUS = 0x1 + +; Combinations, since the script assembler can't process | +ABSOLUTE NOT_MSG_OUT_AFTER_SELECTION = 0x110 +ABSOLUTE UNEXPECTED_PHASE_BEFORE_CMD = 0x220 +ABSOLUTE UNEXPECTED_PHASE_AFTER_CMD = 0x320 +ABSOLUTE NOT_MSG_IN_AFTER_STATUS = 0x430 +ABSOLUTE GOOD_STATUS_AFTER_STATUS = 0x401 +ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_IN = 0x520 +ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_OUT = 0x620 +ABSOLUTE UNEXPECTED_MSG_BEFORE_CMD = 0x240 +ABSOLUTE MSG_IN_BEFORE_CMD = 0x250 +ABSOLUTE MSG_IN_AFTER_CMD = 0x350 +ABSOLUTE SDTR_MSG_BEFORE_CMD = 0x260 +ABSOLUTE REJECT_MSG_BEFORE_CMD = 0x270 +ABSOLUTE DISCONNECT_AFTER_CMD = 0x380 +ABSOLUTE SDTR_MSG_AFTER_CMD = 0x360 +ABSOLUTE WDTR_MSG_AFTER_CMD = 0x3A0 +ABSOLUTE DISCONNECT_AFTER_DATA = 0x580 +ABSOLUTE MSG_IN_AFTER_DATA_IN = 0x550 +ABSOLUTE MSG_IN_AFTER_DATA_OUT = 0x650 +ABSOLUTE MSG_OUT_AFTER_DATA_IN = 0x590 +ABSOLUTE DATA_IN_AFTER_DATA_IN = 0x5a0 +ABSOLUTE MSG_IN_DURING_DATA_IN = 0x750 +ABSOLUTE DISCONNECT_DURING_DATA = 0x780 + +; +; Other interrupt conditions +; +ABSOLUTE RESELECTED_DURING_SELECTION = 0x1000 +ABSOLUTE COMPLETED_SELECTION_AS_TARGET = 0x1001 +ABSOLUTE RESELECTION_IDENTIFIED = 0x1003 +; +; Fatal interrupt conditions. If you add to this, also add to the +; array of corresponding messages +; +ABSOLUTE FATAL = 0x2000 +ABSOLUTE FATAL_UNEXPECTED_RESELECTION_MSG = 0x2000 +ABSOLUTE FATAL_SEND_MSG = 0x2001 +ABSOLUTE FATAL_NOT_MSG_IN_AFTER_SELECTION = 0x2002 +ABSOLUTE FATAL_ILLEGAL_MSG_LENGTH = 0x2003 + +ABSOLUTE DEBUG_INTERRUPT = 0x3000 +ABSOLUTE DEBUG_INTERRUPT1 = 0x3001 +ABSOLUTE DEBUG_INTERRUPT2 = 0x3002 +ABSOLUTE DEBUG_INTERRUPT3 = 0x3003 +ABSOLUTE DEBUG_INTERRUPT4 = 0x3004 +ABSOLUTE DEBUG_INTERRUPT5 = 0x3005 +ABSOLUTE DEBUG_INTERRUPT6 = 0x3006 + + +; +; SCSI Messages we interpret in the script +; +ABSOLUTE EXTENDED_MSG = 0x01 +ABSOLUTE SDTR_MSG = 0x01 +ABSOLUTE SAVE_DATA_PTRS_MSG = 0x02 +ABSOLUTE RESTORE_DATA_PTRS_MSG = 0x03 +ABSOLUTE WDTR_MSG = 0x03 +ABSOLUTE DISCONNECT_MSG = 0x04 +ABSOLUTE REJECT_MSG = 0x07 +ABSOLUTE PARITY_ERROR_MSG = 0x09 +ABSOLUTE SIMPLE_TAG_MSG = 0x20 +ABSOLUTE IDENTIFY_MSG = 0x80 +ABSOLUTE IDENTIFY_MSG_MASK = 0x7F +ABSOLUTE TWO_BYTE_MSG = 0x20 +ABSOLUTE TWO_BYTE_MSG_MASK = 0x0F + +; This is where the script begins + +ENTRY StartUp + +StartUp: + SELECT ATN Device_ID, Reselect + JUMP Finish, WHEN STATUS + JUMP SendIdentifyMsg, IF MSG_OUT + INT NOT_MSG_OUT_AFTER_SELECTION + +Reselect: + WAIT RESELECT SelectedAsTarget + INT RESELECTED_DURING_SELECTION, WHEN MSG_IN + INT FATAL_NOT_MSG_IN_AFTER_SELECTION + + ENTRY GetReselectionData +GetReselectionData: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + INT RESELECTION_IDENTIFIED + + ENTRY GetReselectionWithTag +GetReselectionWithTag: + MOVE 3, ReceiveMsgAddress, WHEN MSG_IN + INT RESELECTION_IDENTIFIED + + ENTRY SelectedAsTarget +SelectedAsTarget: +; Basically tell the selecting device that there's nothing here + SET TARGET + DISCONNECT + CLEAR TARGET + INT COMPLETED_SELECTION_AS_TARGET +; +; These are the messaging entries +; +; Send a message. Message count should be correctly patched + ENTRY SendMessage +SendMessage: + MOVE MessageCount, MessageLocation, WHEN MSG_OUT +ResumeSendMessage: + RETURN, WHEN NOT MSG_OUT + INT FATAL_SEND_MSG + + ENTRY SendMessagePhaseMismatch +SendMessagePhaseMismatch: + CLEAR ACK + JUMP ResumeSendMessage +; +; Receive a message. Need to identify the message to +; receive it correctly + ENTRY ReceiveMessage +ReceiveMessage: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN +; +; Use this entry if we've just tried to look at the first byte +; of the message and want to process it further +ProcessReceiveMessage: + JUMP ReceiveExtendedMessage, IF EXTENDED_MSG + RETURN, IF NOT TWO_BYTE_MSG, AND MASK TWO_BYTE_MSG_MASK + CLEAR ACK + MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN + RETURN +ReceiveExtendedMessage: + CLEAR ACK + MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN + JUMP Receive1Byte, IF 0x01 + JUMP Receive2Byte, IF 0x02 + JUMP Receive3Byte, IF 0x03 + JUMP Receive4Byte, IF 0x04 + JUMP Receive5Byte, IF 0x05 + INT FATAL_ILLEGAL_MSG_LENGTH +Receive1Byte: + CLEAR ACK + MOVE 1, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive2Byte: + CLEAR ACK + MOVE 2, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive3Byte: + CLEAR ACK + MOVE 3, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive4Byte: + CLEAR ACK + MOVE 4, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive5Byte: + CLEAR ACK + MOVE 5, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +; +; Come here from the message processor to ignore the message +; + ENTRY IgnoreMessage +IgnoreMessage: + CLEAR ACK + RETURN +; +; Come here to send a reply to a message +; + ENTRY SendMessageWithATN +SendMessageWithATN: + SET ATN + CLEAR ACK + JUMP SendMessage + +SendIdentifyMsg: + CALL SendMessage + JUMP SendCommand + +IgnoreMsgBeforeCommand: + CLEAR ACK + ENTRY SendCommand +SendCommand: + JUMP Finish, WHEN STATUS + JUMP MsgInBeforeCommand, IF MSG_IN + INT UNEXPECTED_PHASE_BEFORE_CMD, IF NOT CMD + MOVE CommandCount, CommandAddress, WHEN CMD +ResumeSendCommand: + JUMP Finish, WHEN STATUS + JUMP MsgInAfterCmd, IF MSG_IN + JUMP DataIn, IF DATA_IN + JUMP DataOut, IF DATA_OUT + INT UNEXPECTED_PHASE_AFTER_CMD + +IgnoreMsgDuringData: + CLEAR ACK + ; fall through to MsgInDuringData + +Entry MsgInDuringData +MsgInDuringData: +; +; Could be we have nothing more to transfer +; + JUMP Finish, WHEN STATUS + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectDuringDataIn, IF DISCONNECT_MSG + JUMP IgnoreMsgDuringData, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgDuringData, IF RESTORE_DATA_PTRS_MSG + INT MSG_IN_DURING_DATA_IN + +MsgInAfterCmd: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectAfterCmd, IF DISCONNECT_MSG + JUMP IgnoreMsgInAfterCmd, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgInAfterCmd, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_AFTER_CMD + CLEAR ACK + JUMP ResumeSendCommand + +IgnoreMsgInAfterCmd: + CLEAR ACK + JUMP ResumeSendCommand + +DisconnectAfterCmd: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect1 +Disconnect1: + INT DISCONNECT_AFTER_CMD + ENTRY Disconnect2 +Disconnect2: +; We return here after a reselection + CLEAR ACK + JUMP ResumeSendCommand + +MsgInBeforeCommand: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP IgnoreMsgBeforeCommand, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgBeforeCommand, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_BEFORE_CMD + CLEAR ACK + JUMP SendCommand + +DataIn: + CALL SGScriptStartAddress +ResumeDataIn: + JUMP Finish, WHEN STATUS + JUMP MsgInAfterDataIn, IF MSG_IN + JUMP DataInAfterDataIn, if DATA_IN + INT MSG_OUT_AFTER_DATA_IN, if MSG_OUT + INT UNEXPECTED_PHASE_AFTER_DATA_IN + +DataInAfterDataIn: + INT DATA_IN_AFTER_DATA_IN + JUMP ResumeDataIn + +DataOut: + CALL SGScriptStartAddress +ResumeDataOut: + JUMP Finish, WHEN STATUS + JUMP MsgInAfterDataOut, IF MSG_IN + INT UNEXPECTED_PHASE_AFTER_DATA_OUT + +MsgInAfterDataIn: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectAfterDataIn, IF DISCONNECT_MSG + JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_AFTER_DATA_IN + CLEAR ACK + JUMP ResumeDataIn + +DisconnectDuringDataIn: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect3 +Disconnect3: + INT DISCONNECT_DURING_DATA + ENTRY Disconnect4 +Disconnect4: +; we return here after a reselection + CLEAR ACK + JUMP ResumeSendCommand + + +DisconnectAfterDataIn: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect5 +Disconnect5: + INT DISCONNECT_AFTER_DATA + ENTRY Disconnect6 +Disconnect6: +; we return here after a reselection + CLEAR ACK + JUMP ResumeDataIn + +MsgInAfterDataOut: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectAfterDataOut, if DISCONNECT_MSG + JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_AFTER_DATA_OUT + CLEAR ACK + JUMP ResumeDataOut + +IgnoreMsgAfterData: + CLEAR ACK +; Data in and out do the same thing on resume, so pick one + JUMP ResumeDataIn + +DisconnectAfterDataOut: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect7 +Disconnect7: + INT DISCONNECT_AFTER_DATA + ENTRY Disconnect8 +Disconnect8: +; we return here after a reselection + CLEAR ACK + JUMP ResumeDataOut + +Finish: + MOVE 1, StatusAddress, WHEN STATUS + INT NOT_MSG_IN_AFTER_STATUS, WHEN NOT MSG_IN + CALL ReceiveMessage + CLEAR ACK + WAIT DISCONNECT + ENTRY Finish1 +Finish1: + INT GOOD_STATUS_AFTER_STATUS + ENTRY Finish2 +Finish2: + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/53c700_d.h linux.ac/drivers/scsi/53c700_d.h --- linux.vanilla/drivers/scsi/53c700_d.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/53c700_d.h Sun May 13 22:20:12 2001 @@ -0,0 +1,1301 @@ +/* DO NOT EDIT - Generated automatically by script_asm.pl */ +static u32 SCRIPT[] = { +/* +; Script for the NCR (or symbios) 53c700 and 53c700-66 chip +; +; Copyright (C) 2001 James.Bottomley@HansenPartnership.com +;;----------------------------------------------------------------------------- +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; 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. +;; +;;----------------------------------------------------------------------------- +; +; This script is designed to be modified for the particular command in +; operation. The particular variables pertaining to the commands are: +; +ABSOLUTE Device_ID = 0 ; ID of target for command +ABSOLUTE MessageCount = 0 ; Number of bytes in message +ABSOLUTE MessageLocation = 0 ; Addr of message +ABSOLUTE CommandCount = 0 ; Number of bytes in command +ABSOLUTE CommandAddress = 0 ; Addr of Command +ABSOLUTE StatusAddress = 0 ; Addr to receive status return +ABSOLUTE ReceiveMsgAddress = 0 ; Addr to receive msg +; +; This is the magic component for handling scatter-gather. Each of the +; SG components is preceeded by a script fragment which moves the +; necessary amount of data and jumps to the next SG segment. The final +; SG segment jumps back to . However, this address is the first SG script +; segment. +; +ABSOLUTE SGScriptStartAddress = 0 + +; The following represent status interrupts we use 3 hex digits for +; this: 0xPRS where + +; P: +ABSOLUTE AFTER_SELECTION = 0x100 +ABSOLUTE BEFORE_CMD = 0x200 +ABSOLUTE AFTER_CMD = 0x300 +ABSOLUTE AFTER_STATUS = 0x400 +ABSOLUTE AFTER_DATA_IN = 0x500 +ABSOLUTE AFTER_DATA_OUT = 0x600 +ABSOLUTE DURING_DATA_IN = 0x700 + +; R: +ABSOLUTE NOT_MSG_OUT = 0x10 +ABSOLUTE UNEXPECTED_PHASE = 0x20 +ABSOLUTE NOT_MSG_IN = 0x30 +ABSOLUTE UNEXPECTED_MSG = 0x40 +ABSOLUTE MSG_IN = 0x50 +ABSOLUTE SDTR_MSG_R = 0x60 +ABSOLUTE REJECT_MSG_R = 0x70 +ABSOLUTE DISCONNECT = 0x80 +ABSOLUTE MSG_OUT = 0x90 +ABSOLUTE WDTR_MSG_R = 0xA0 + +; S: +ABSOLUTE GOOD_STATUS = 0x1 + +; Combinations, since the script assembler can't process | +ABSOLUTE NOT_MSG_OUT_AFTER_SELECTION = 0x110 +ABSOLUTE UNEXPECTED_PHASE_BEFORE_CMD = 0x220 +ABSOLUTE UNEXPECTED_PHASE_AFTER_CMD = 0x320 +ABSOLUTE NOT_MSG_IN_AFTER_STATUS = 0x430 +ABSOLUTE GOOD_STATUS_AFTER_STATUS = 0x401 +ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_IN = 0x520 +ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_OUT = 0x620 +ABSOLUTE UNEXPECTED_MSG_BEFORE_CMD = 0x240 +ABSOLUTE MSG_IN_BEFORE_CMD = 0x250 +ABSOLUTE MSG_IN_AFTER_CMD = 0x350 +ABSOLUTE SDTR_MSG_BEFORE_CMD = 0x260 +ABSOLUTE REJECT_MSG_BEFORE_CMD = 0x270 +ABSOLUTE DISCONNECT_AFTER_CMD = 0x380 +ABSOLUTE SDTR_MSG_AFTER_CMD = 0x360 +ABSOLUTE WDTR_MSG_AFTER_CMD = 0x3A0 +ABSOLUTE DISCONNECT_AFTER_DATA = 0x580 +ABSOLUTE MSG_IN_AFTER_DATA_IN = 0x550 +ABSOLUTE MSG_IN_AFTER_DATA_OUT = 0x650 +ABSOLUTE MSG_OUT_AFTER_DATA_IN = 0x590 +ABSOLUTE DATA_IN_AFTER_DATA_IN = 0x5a0 +ABSOLUTE MSG_IN_DURING_DATA_IN = 0x750 +ABSOLUTE DISCONNECT_DURING_DATA = 0x780 + +; +; Other interrupt conditions +; +ABSOLUTE RESELECTED_DURING_SELECTION = 0x1000 +ABSOLUTE COMPLETED_SELECTION_AS_TARGET = 0x1001 +ABSOLUTE RESELECTION_IDENTIFIED = 0x1003 +; +; Fatal interrupt conditions. If you add to this, also add to the +; array of corresponding messages +; +ABSOLUTE FATAL = 0x2000 +ABSOLUTE FATAL_UNEXPECTED_RESELECTION_MSG = 0x2000 +ABSOLUTE FATAL_SEND_MSG = 0x2001 +ABSOLUTE FATAL_NOT_MSG_IN_AFTER_SELECTION = 0x2002 +ABSOLUTE FATAL_ILLEGAL_MSG_LENGTH = 0x2003 + +ABSOLUTE DEBUG_INTERRUPT = 0x3000 +ABSOLUTE DEBUG_INTERRUPT1 = 0x3001 +ABSOLUTE DEBUG_INTERRUPT2 = 0x3002 +ABSOLUTE DEBUG_INTERRUPT3 = 0x3003 +ABSOLUTE DEBUG_INTERRUPT4 = 0x3004 +ABSOLUTE DEBUG_INTERRUPT5 = 0x3005 +ABSOLUTE DEBUG_INTERRUPT6 = 0x3006 + + +; +; SCSI Messages we interpret in the script +; +ABSOLUTE EXTENDED_MSG = 0x01 +ABSOLUTE SDTR_MSG = 0x01 +ABSOLUTE SAVE_DATA_PTRS_MSG = 0x02 +ABSOLUTE RESTORE_DATA_PTRS_MSG = 0x03 +ABSOLUTE WDTR_MSG = 0x03 +ABSOLUTE DISCONNECT_MSG = 0x04 +ABSOLUTE REJECT_MSG = 0x07 +ABSOLUTE PARITY_ERROR_MSG = 0x09 +ABSOLUTE SIMPLE_TAG_MSG = 0x20 +ABSOLUTE IDENTIFY_MSG = 0x80 +ABSOLUTE IDENTIFY_MSG_MASK = 0x7F +ABSOLUTE TWO_BYTE_MSG = 0x20 +ABSOLUTE TWO_BYTE_MSG_MASK = 0x0F + +; This is where the script begins + +ENTRY StartUp + +StartUp: + SELECT ATN Device_ID, Reselect + +at 0x00000000 : */ 0x41000000,0x00000020, +/* + JUMP Finish, WHEN STATUS + +at 0x00000002 : */ 0x830b0000,0x00000460, +/* + JUMP SendIdentifyMsg, IF MSG_OUT + +at 0x00000004 : */ 0x860a0000,0x000001b0, +/* + INT NOT_MSG_OUT_AFTER_SELECTION + +at 0x00000006 : */ 0x98080000,0x00000110, +/* + +Reselect: + WAIT RESELECT SelectedAsTarget + +at 0x00000008 : */ 0x50000000,0x00000058, +/* + INT RESELECTED_DURING_SELECTION, WHEN MSG_IN + +at 0x0000000a : */ 0x9f0b0000,0x00001000, +/* + INT FATAL_NOT_MSG_IN_AFTER_SELECTION + +at 0x0000000c : */ 0x98080000,0x00002002, +/* + + ENTRY GetReselectionData +GetReselectionData: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + +at 0x0000000e : */ 0x0f000001,0x00000000, +/* + INT RESELECTION_IDENTIFIED + +at 0x00000010 : */ 0x98080000,0x00001003, +/* + + ENTRY GetReselectionWithTag +GetReselectionWithTag: + MOVE 3, ReceiveMsgAddress, WHEN MSG_IN + +at 0x00000012 : */ 0x0f000003,0x00000000, +/* + INT RESELECTION_IDENTIFIED + +at 0x00000014 : */ 0x98080000,0x00001003, +/* + + ENTRY SelectedAsTarget +SelectedAsTarget: +; Basically tell the selecting device that there's nothing here + SET TARGET + +at 0x00000016 : */ 0x58000200,0x00000000, +/* + DISCONNECT + +at 0x00000018 : */ 0x48000000,0x00000000, +/* + CLEAR TARGET + +at 0x0000001a : */ 0x60000200,0x00000000, +/* + INT COMPLETED_SELECTION_AS_TARGET + +at 0x0000001c : */ 0x98080000,0x00001001, +/* +; +; These are the messaging entries +; +; Send a message. Message count should be correctly patched + ENTRY SendMessage +SendMessage: + MOVE MessageCount, MessageLocation, WHEN MSG_OUT + +at 0x0000001e : */ 0x0e000000,0x00000000, +/* +ResumeSendMessage: + RETURN, WHEN NOT MSG_OUT + +at 0x00000020 : */ 0x96030000,0x00000000, +/* + INT FATAL_SEND_MSG + +at 0x00000022 : */ 0x98080000,0x00002001, +/* + + ENTRY SendMessagePhaseMismatch +SendMessagePhaseMismatch: + CLEAR ACK + +at 0x00000024 : */ 0x60000040,0x00000000, +/* + JUMP ResumeSendMessage + +at 0x00000026 : */ 0x80080000,0x00000080, +/* +; +; Receive a message. Need to identify the message to +; receive it correctly + ENTRY ReceiveMessage +ReceiveMessage: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + +at 0x00000028 : */ 0x0f000001,0x00000000, +/* +; +; Use this entry if we've just tried to look at the first byte +; of the message and want to process it further +ProcessReceiveMessage: + JUMP ReceiveExtendedMessage, IF EXTENDED_MSG + +at 0x0000002a : */ 0x800c0001,0x000000d0, +/* + RETURN, IF NOT TWO_BYTE_MSG, AND MASK TWO_BYTE_MSG_MASK + +at 0x0000002c : */ 0x90040f20,0x00000000, +/* + CLEAR ACK + +at 0x0000002e : */ 0x60000040,0x00000000, +/* + MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN + +at 0x00000030 : */ 0x0f000001,0x00000001, +/* + RETURN + +at 0x00000032 : */ 0x90080000,0x00000000, +/* +ReceiveExtendedMessage: + CLEAR ACK + +at 0x00000034 : */ 0x60000040,0x00000000, +/* + MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN + +at 0x00000036 : */ 0x0f000001,0x00000001, +/* + JUMP Receive1Byte, IF 0x01 + +at 0x00000038 : */ 0x800c0001,0x00000110, +/* + JUMP Receive2Byte, IF 0x02 + +at 0x0000003a : */ 0x800c0002,0x00000128, +/* + JUMP Receive3Byte, IF 0x03 + +at 0x0000003c : */ 0x800c0003,0x00000140, +/* + JUMP Receive4Byte, IF 0x04 + +at 0x0000003e : */ 0x800c0004,0x00000158, +/* + JUMP Receive5Byte, IF 0x05 + +at 0x00000040 : */ 0x800c0005,0x00000170, +/* + INT FATAL_ILLEGAL_MSG_LENGTH + +at 0x00000042 : */ 0x98080000,0x00002003, +/* +Receive1Byte: + CLEAR ACK + +at 0x00000044 : */ 0x60000040,0x00000000, +/* + MOVE 1, ReceiveMsgAddress + 2, WHEN MSG_IN + +at 0x00000046 : */ 0x0f000001,0x00000002, +/* + RETURN + +at 0x00000048 : */ 0x90080000,0x00000000, +/* +Receive2Byte: + CLEAR ACK + +at 0x0000004a : */ 0x60000040,0x00000000, +/* + MOVE 2, ReceiveMsgAddress + 2, WHEN MSG_IN + +at 0x0000004c : */ 0x0f000002,0x00000002, +/* + RETURN + +at 0x0000004e : */ 0x90080000,0x00000000, +/* +Receive3Byte: + CLEAR ACK + +at 0x00000050 : */ 0x60000040,0x00000000, +/* + MOVE 3, ReceiveMsgAddress + 2, WHEN MSG_IN + +at 0x00000052 : */ 0x0f000003,0x00000002, +/* + RETURN + +at 0x00000054 : */ 0x90080000,0x00000000, +/* +Receive4Byte: + CLEAR ACK + +at 0x00000056 : */ 0x60000040,0x00000000, +/* + MOVE 4, ReceiveMsgAddress + 2, WHEN MSG_IN + +at 0x00000058 : */ 0x0f000004,0x00000002, +/* + RETURN + +at 0x0000005a : */ 0x90080000,0x00000000, +/* +Receive5Byte: + CLEAR ACK + +at 0x0000005c : */ 0x60000040,0x00000000, +/* + MOVE 5, ReceiveMsgAddress + 2, WHEN MSG_IN + +at 0x0000005e : */ 0x0f000005,0x00000002, +/* + RETURN + +at 0x00000060 : */ 0x90080000,0x00000000, +/* +; +; Come here from the message processor to ignore the message +; + ENTRY IgnoreMessage +IgnoreMessage: + CLEAR ACK + +at 0x00000062 : */ 0x60000040,0x00000000, +/* + RETURN + +at 0x00000064 : */ 0x90080000,0x00000000, +/* +; +; Come here to send a reply to a message +; + ENTRY SendMessageWithATN +SendMessageWithATN: + SET ATN + +at 0x00000066 : */ 0x58000008,0x00000000, +/* + CLEAR ACK + +at 0x00000068 : */ 0x60000040,0x00000000, +/* + JUMP SendMessage + +at 0x0000006a : */ 0x80080000,0x00000078, +/* + +SendIdentifyMsg: + CALL SendMessage + +at 0x0000006c : */ 0x88080000,0x00000078, +/* + JUMP SendCommand + +at 0x0000006e : */ 0x80080000,0x000001c8, +/* + +IgnoreMsgBeforeCommand: + CLEAR ACK + +at 0x00000070 : */ 0x60000040,0x00000000, +/* + ENTRY SendCommand +SendCommand: + JUMP Finish, WHEN STATUS + +at 0x00000072 : */ 0x830b0000,0x00000460, +/* + JUMP MsgInBeforeCommand, IF MSG_IN + +at 0x00000074 : */ 0x870a0000,0x000002c0, +/* + INT UNEXPECTED_PHASE_BEFORE_CMD, IF NOT CMD + +at 0x00000076 : */ 0x9a020000,0x00000220, +/* + MOVE CommandCount, CommandAddress, WHEN CMD + +at 0x00000078 : */ 0x0a000000,0x00000000, +/* +ResumeSendCommand: + JUMP Finish, WHEN STATUS + +at 0x0000007a : */ 0x830b0000,0x00000460, +/* + JUMP MsgInAfterCmd, IF MSG_IN + +at 0x0000007c : */ 0x870a0000,0x00000248, +/* + JUMP DataIn, IF DATA_IN + +at 0x0000007e : */ 0x810a0000,0x000002f8, +/* + JUMP DataOut, IF DATA_OUT + +at 0x00000080 : */ 0x800a0000,0x00000338, +/* + INT UNEXPECTED_PHASE_AFTER_CMD + +at 0x00000082 : */ 0x98080000,0x00000320, +/* + +IgnoreMsgDuringData: + CLEAR ACK + +at 0x00000084 : */ 0x60000040,0x00000000, +/* + ; fall through to MsgInDuringData + +Entry MsgInDuringData +MsgInDuringData: +; +; Could be we have nothing more to transfer +; + JUMP Finish, WHEN STATUS + +at 0x00000086 : */ 0x830b0000,0x00000460, +/* + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + +at 0x00000088 : */ 0x0f000001,0x00000000, +/* + JUMP DisconnectDuringDataIn, IF DISCONNECT_MSG + +at 0x0000008a : */ 0x800c0004,0x00000398, +/* + JUMP IgnoreMsgDuringData, IF SAVE_DATA_PTRS_MSG + +at 0x0000008c : */ 0x800c0002,0x00000210, +/* + JUMP IgnoreMsgDuringData, IF RESTORE_DATA_PTRS_MSG + +at 0x0000008e : */ 0x800c0003,0x00000210, +/* + INT MSG_IN_DURING_DATA_IN + +at 0x00000090 : */ 0x98080000,0x00000750, +/* + +MsgInAfterCmd: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + +at 0x00000092 : */ 0x0f000001,0x00000000, +/* + JUMP DisconnectAfterCmd, IF DISCONNECT_MSG + +at 0x00000094 : */ 0x800c0004,0x00000298, +/* + JUMP IgnoreMsgInAfterCmd, IF SAVE_DATA_PTRS_MSG + +at 0x00000096 : */ 0x800c0002,0x00000288, +/* + JUMP IgnoreMsgInAfterCmd, IF RESTORE_DATA_PTRS_MSG + +at 0x00000098 : */ 0x800c0003,0x00000288, +/* + CALL ProcessReceiveMessage + +at 0x0000009a : */ 0x88080000,0x000000a8, +/* + INT MSG_IN_AFTER_CMD + +at 0x0000009c : */ 0x98080000,0x00000350, +/* + CLEAR ACK + +at 0x0000009e : */ 0x60000040,0x00000000, +/* + JUMP ResumeSendCommand + +at 0x000000a0 : */ 0x80080000,0x000001e8, +/* + +IgnoreMsgInAfterCmd: + CLEAR ACK + +at 0x000000a2 : */ 0x60000040,0x00000000, +/* + JUMP ResumeSendCommand + +at 0x000000a4 : */ 0x80080000,0x000001e8, +/* + +DisconnectAfterCmd: + CLEAR ACK + +at 0x000000a6 : */ 0x60000040,0x00000000, +/* + WAIT DISCONNECT + +at 0x000000a8 : */ 0x48000000,0x00000000, +/* + ENTRY Disconnect1 +Disconnect1: + INT DISCONNECT_AFTER_CMD + +at 0x000000aa : */ 0x98080000,0x00000380, +/* + ENTRY Disconnect2 +Disconnect2: +; We return here after a reselection + CLEAR ACK + +at 0x000000ac : */ 0x60000040,0x00000000, +/* + JUMP ResumeSendCommand + +at 0x000000ae : */ 0x80080000,0x000001e8, +/* + +MsgInBeforeCommand: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + +at 0x000000b0 : */ 0x0f000001,0x00000000, +/* + JUMP IgnoreMsgBeforeCommand, IF SAVE_DATA_PTRS_MSG + +at 0x000000b2 : */ 0x800c0002,0x000001c0, +/* + JUMP IgnoreMsgBeforeCommand, IF RESTORE_DATA_PTRS_MSG + +at 0x000000b4 : */ 0x800c0003,0x000001c0, +/* + CALL ProcessReceiveMessage + +at 0x000000b6 : */ 0x88080000,0x000000a8, +/* + INT MSG_IN_BEFORE_CMD + +at 0x000000b8 : */ 0x98080000,0x00000250, +/* + CLEAR ACK + +at 0x000000ba : */ 0x60000040,0x00000000, +/* + JUMP SendCommand + +at 0x000000bc : */ 0x80080000,0x000001c8, +/* + +DataIn: + CALL SGScriptStartAddress + +at 0x000000be : */ 0x88080000,0x00000000, +/* +ResumeDataIn: + JUMP Finish, WHEN STATUS + +at 0x000000c0 : */ 0x830b0000,0x00000460, +/* + JUMP MsgInAfterDataIn, IF MSG_IN + +at 0x000000c2 : */ 0x870a0000,0x00000358, +/* + JUMP DataInAfterDataIn, if DATA_IN + +at 0x000000c4 : */ 0x810a0000,0x00000328, +/* + INT MSG_OUT_AFTER_DATA_IN, if MSG_OUT + +at 0x000000c6 : */ 0x9e0a0000,0x00000590, +/* + INT UNEXPECTED_PHASE_AFTER_DATA_IN + +at 0x000000c8 : */ 0x98080000,0x00000520, +/* + +DataInAfterDataIn: + INT DATA_IN_AFTER_DATA_IN + +at 0x000000ca : */ 0x98080000,0x000005a0, +/* + JUMP ResumeDataIn + +at 0x000000cc : */ 0x80080000,0x00000300, +/* + +DataOut: + CALL SGScriptStartAddress + +at 0x000000ce : */ 0x88080000,0x00000000, +/* +ResumeDataOut: + JUMP Finish, WHEN STATUS + +at 0x000000d0 : */ 0x830b0000,0x00000460, +/* + JUMP MsgInAfterDataOut, IF MSG_IN + +at 0x000000d2 : */ 0x870a0000,0x000003e8, +/* + INT UNEXPECTED_PHASE_AFTER_DATA_OUT + +at 0x000000d4 : */ 0x98080000,0x00000620, +/* + +MsgInAfterDataIn: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + +at 0x000000d6 : */ 0x0f000001,0x00000000, +/* + JUMP DisconnectAfterDataIn, IF DISCONNECT_MSG + +at 0x000000d8 : */ 0x800c0004,0x000003c0, +/* + JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG + +at 0x000000da : */ 0x800c0002,0x00000428, +/* + JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG + +at 0x000000dc : */ 0x800c0003,0x00000428, +/* + CALL ProcessReceiveMessage + +at 0x000000de : */ 0x88080000,0x000000a8, +/* + INT MSG_IN_AFTER_DATA_IN + +at 0x000000e0 : */ 0x98080000,0x00000550, +/* + CLEAR ACK + +at 0x000000e2 : */ 0x60000040,0x00000000, +/* + JUMP ResumeDataIn + +at 0x000000e4 : */ 0x80080000,0x00000300, +/* + +DisconnectDuringDataIn: + CLEAR ACK + +at 0x000000e6 : */ 0x60000040,0x00000000, +/* + WAIT DISCONNECT + +at 0x000000e8 : */ 0x48000000,0x00000000, +/* + ENTRY Disconnect3 +Disconnect3: + INT DISCONNECT_DURING_DATA + +at 0x000000ea : */ 0x98080000,0x00000780, +/* + ENTRY Disconnect4 +Disconnect4: +; we return here after a reselection + CLEAR ACK + +at 0x000000ec : */ 0x60000040,0x00000000, +/* + JUMP ResumeSendCommand + +at 0x000000ee : */ 0x80080000,0x000001e8, +/* + + +DisconnectAfterDataIn: + CLEAR ACK + +at 0x000000f0 : */ 0x60000040,0x00000000, +/* + WAIT DISCONNECT + +at 0x000000f2 : */ 0x48000000,0x00000000, +/* + ENTRY Disconnect5 +Disconnect5: + INT DISCONNECT_AFTER_DATA + +at 0x000000f4 : */ 0x98080000,0x00000580, +/* + ENTRY Disconnect6 +Disconnect6: +; we return here after a reselection + CLEAR ACK + +at 0x000000f6 : */ 0x60000040,0x00000000, +/* + JUMP ResumeDataIn + +at 0x000000f8 : */ 0x80080000,0x00000300, +/* + +MsgInAfterDataOut: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + +at 0x000000fa : */ 0x0f000001,0x00000000, +/* + JUMP DisconnectAfterDataOut, if DISCONNECT_MSG + +at 0x000000fc : */ 0x800c0004,0x00000438, +/* + JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG + +at 0x000000fe : */ 0x800c0002,0x00000428, +/* + JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG + +at 0x00000100 : */ 0x800c0003,0x00000428, +/* + CALL ProcessReceiveMessage + +at 0x00000102 : */ 0x88080000,0x000000a8, +/* + INT MSG_IN_AFTER_DATA_OUT + +at 0x00000104 : */ 0x98080000,0x00000650, +/* + CLEAR ACK + +at 0x00000106 : */ 0x60000040,0x00000000, +/* + JUMP ResumeDataOut + +at 0x00000108 : */ 0x80080000,0x00000340, +/* + +IgnoreMsgAfterData: + CLEAR ACK + +at 0x0000010a : */ 0x60000040,0x00000000, +/* +; Data in and out do the same thing on resume, so pick one + JUMP ResumeDataIn + +at 0x0000010c : */ 0x80080000,0x00000300, +/* + +DisconnectAfterDataOut: + CLEAR ACK + +at 0x0000010e : */ 0x60000040,0x00000000, +/* + WAIT DISCONNECT + +at 0x00000110 : */ 0x48000000,0x00000000, +/* + ENTRY Disconnect7 +Disconnect7: + INT DISCONNECT_AFTER_DATA + +at 0x00000112 : */ 0x98080000,0x00000580, +/* + ENTRY Disconnect8 +Disconnect8: +; we return here after a reselection + CLEAR ACK + +at 0x00000114 : */ 0x60000040,0x00000000, +/* + JUMP ResumeDataOut + +at 0x00000116 : */ 0x80080000,0x00000340, +/* + +Finish: + MOVE 1, StatusAddress, WHEN STATUS + +at 0x00000118 : */ 0x0b000001,0x00000000, +/* + INT NOT_MSG_IN_AFTER_STATUS, WHEN NOT MSG_IN + +at 0x0000011a : */ 0x9f030000,0x00000430, +/* + CALL ReceiveMessage + +at 0x0000011c : */ 0x88080000,0x000000a0, +/* + CLEAR ACK + +at 0x0000011e : */ 0x60000040,0x00000000, +/* + WAIT DISCONNECT + +at 0x00000120 : */ 0x48000000,0x00000000, +/* + ENTRY Finish1 +Finish1: + INT GOOD_STATUS_AFTER_STATUS + +at 0x00000122 : */ 0x98080000,0x00000401, +}; + +#define A_AFTER_CMD 0x00000300 +static u32 A_AFTER_CMD_used[] __attribute((unused)) = { +}; + +#define A_AFTER_DATA_IN 0x00000500 +static u32 A_AFTER_DATA_IN_used[] __attribute((unused)) = { +}; + +#define A_AFTER_DATA_OUT 0x00000600 +static u32 A_AFTER_DATA_OUT_used[] __attribute((unused)) = { +}; + +#define A_AFTER_SELECTION 0x00000100 +static u32 A_AFTER_SELECTION_used[] __attribute((unused)) = { +}; + +#define A_AFTER_STATUS 0x00000400 +static u32 A_AFTER_STATUS_used[] __attribute((unused)) = { +}; + +#define A_BEFORE_CMD 0x00000200 +static u32 A_BEFORE_CMD_used[] __attribute((unused)) = { +}; + +#define A_COMPLETED_SELECTION_AS_TARGET 0x00001001 +static u32 A_COMPLETED_SELECTION_AS_TARGET_used[] __attribute((unused)) = { + 0x0000001d, +}; + +#define A_CommandAddress 0x00000000 +static u32 A_CommandAddress_used[] __attribute((unused)) = { + 0x00000079, +}; + +#define A_CommandCount 0x00000000 +static u32 A_CommandCount_used[] __attribute((unused)) = { + 0x00000078, +}; + +#define A_DATA_IN_AFTER_DATA_IN 0x000005a0 +static u32 A_DATA_IN_AFTER_DATA_IN_used[] __attribute((unused)) = { + 0x000000cb, +}; + +#define A_DEBUG_INTERRUPT 0x00003000 +static u32 A_DEBUG_INTERRUPT_used[] __attribute((unused)) = { +}; + +#define A_DEBUG_INTERRUPT1 0x00003001 +static u32 A_DEBUG_INTERRUPT1_used[] __attribute((unused)) = { +}; + +#define A_DEBUG_INTERRUPT2 0x00003002 +static u32 A_DEBUG_INTERRUPT2_used[] __attribute((unused)) = { +}; + +#define A_DEBUG_INTERRUPT3 0x00003003 +static u32 A_DEBUG_INTERRUPT3_used[] __attribute((unused)) = { +}; + +#define A_DEBUG_INTERRUPT4 0x00003004 +static u32 A_DEBUG_INTERRUPT4_used[] __attribute((unused)) = { +}; + +#define A_DEBUG_INTERRUPT5 0x00003005 +static u32 A_DEBUG_INTERRUPT5_used[] __attribute((unused)) = { +}; + +#define A_DEBUG_INTERRUPT6 0x00003006 +static u32 A_DEBUG_INTERRUPT6_used[] __attribute((unused)) = { +}; + +#define A_DISCONNECT 0x00000080 +static u32 A_DISCONNECT_used[] __attribute((unused)) = { +}; + +#define A_DISCONNECT_AFTER_CMD 0x00000380 +static u32 A_DISCONNECT_AFTER_CMD_used[] __attribute((unused)) = { + 0x000000ab, +}; + +#define A_DISCONNECT_AFTER_DATA 0x00000580 +static u32 A_DISCONNECT_AFTER_DATA_used[] __attribute((unused)) = { + 0x000000f5, + 0x00000113, +}; + +#define A_DISCONNECT_DURING_DATA 0x00000780 +static u32 A_DISCONNECT_DURING_DATA_used[] __attribute((unused)) = { + 0x000000eb, +}; + +#define A_DISCONNECT_MSG 0x00000004 +static u32 A_DISCONNECT_MSG_used[] __attribute((unused)) = { + 0x0000008a, + 0x00000094, + 0x000000d8, + 0x000000fc, +}; + +#define A_DURING_DATA_IN 0x00000700 +static u32 A_DURING_DATA_IN_used[] __attribute((unused)) = { +}; + +#define A_Device_ID 0x00000000 +static u32 A_Device_ID_used[] __attribute((unused)) = { + 0x00000000, +}; + +#define A_EXTENDED_MSG 0x00000001 +static u32 A_EXTENDED_MSG_used[] __attribute((unused)) = { + 0x0000002a, +}; + +#define A_FATAL 0x00002000 +static u32 A_FATAL_used[] __attribute((unused)) = { +}; + +#define A_FATAL_ILLEGAL_MSG_LENGTH 0x00002003 +static u32 A_FATAL_ILLEGAL_MSG_LENGTH_used[] __attribute((unused)) = { + 0x00000043, +}; + +#define A_FATAL_NOT_MSG_IN_AFTER_SELECTION 0x00002002 +static u32 A_FATAL_NOT_MSG_IN_AFTER_SELECTION_used[] __attribute((unused)) = { + 0x0000000d, +}; + +#define A_FATAL_SEND_MSG 0x00002001 +static u32 A_FATAL_SEND_MSG_used[] __attribute((unused)) = { + 0x00000023, +}; + +#define A_FATAL_UNEXPECTED_RESELECTION_MSG 0x00002000 +static u32 A_FATAL_UNEXPECTED_RESELECTION_MSG_used[] __attribute((unused)) = { +}; + +#define A_GOOD_STATUS 0x00000001 +static u32 A_GOOD_STATUS_used[] __attribute((unused)) = { +}; + +#define A_GOOD_STATUS_AFTER_STATUS 0x00000401 +static u32 A_GOOD_STATUS_AFTER_STATUS_used[] __attribute((unused)) = { + 0x00000123, +}; + +#define A_IDENTIFY_MSG 0x00000080 +static u32 A_IDENTIFY_MSG_used[] __attribute((unused)) = { +}; + +#define A_IDENTIFY_MSG_MASK 0x0000007f +static u32 A_IDENTIFY_MSG_MASK_used[] __attribute((unused)) = { +}; + +#define A_MSG_IN 0x00000050 +static u32 A_MSG_IN_used[] __attribute((unused)) = { +}; + +#define A_MSG_IN_AFTER_CMD 0x00000350 +static u32 A_MSG_IN_AFTER_CMD_used[] __attribute((unused)) = { + 0x0000009d, +}; + +#define A_MSG_IN_AFTER_DATA_IN 0x00000550 +static u32 A_MSG_IN_AFTER_DATA_IN_used[] __attribute((unused)) = { + 0x000000e1, +}; + +#define A_MSG_IN_AFTER_DATA_OUT 0x00000650 +static u32 A_MSG_IN_AFTER_DATA_OUT_used[] __attribute((unused)) = { + 0x00000105, +}; + +#define A_MSG_IN_BEFORE_CMD 0x00000250 +static u32 A_MSG_IN_BEFORE_CMD_used[] __attribute((unused)) = { + 0x000000b9, +}; + +#define A_MSG_IN_DURING_DATA_IN 0x00000750 +static u32 A_MSG_IN_DURING_DATA_IN_used[] __attribute((unused)) = { + 0x00000091, +}; + +#define A_MSG_OUT 0x00000090 +static u32 A_MSG_OUT_used[] __attribute((unused)) = { +}; + +#define A_MSG_OUT_AFTER_DATA_IN 0x00000590 +static u32 A_MSG_OUT_AFTER_DATA_IN_used[] __attribute((unused)) = { + 0x000000c7, +}; + +#define A_MessageCount 0x00000000 +static u32 A_MessageCount_used[] __attribute((unused)) = { + 0x0000001e, +}; + +#define A_MessageLocation 0x00000000 +static u32 A_MessageLocation_used[] __attribute((unused)) = { + 0x0000001f, +}; + +#define A_NOT_MSG_IN 0x00000030 +static u32 A_NOT_MSG_IN_used[] __attribute((unused)) = { +}; + +#define A_NOT_MSG_IN_AFTER_STATUS 0x00000430 +static u32 A_NOT_MSG_IN_AFTER_STATUS_used[] __attribute((unused)) = { + 0x0000011b, +}; + +#define A_NOT_MSG_OUT 0x00000010 +static u32 A_NOT_MSG_OUT_used[] __attribute((unused)) = { +}; + +#define A_NOT_MSG_OUT_AFTER_SELECTION 0x00000110 +static u32 A_NOT_MSG_OUT_AFTER_SELECTION_used[] __attribute((unused)) = { + 0x00000007, +}; + +#define A_PARITY_ERROR_MSG 0x00000009 +static u32 A_PARITY_ERROR_MSG_used[] __attribute((unused)) = { +}; + +#define A_REJECT_MSG 0x00000007 +static u32 A_REJECT_MSG_used[] __attribute((unused)) = { +}; + +#define A_REJECT_MSG_BEFORE_CMD 0x00000270 +static u32 A_REJECT_MSG_BEFORE_CMD_used[] __attribute((unused)) = { +}; + +#define A_REJECT_MSG_R 0x00000070 +static u32 A_REJECT_MSG_R_used[] __attribute((unused)) = { +}; + +#define A_RESELECTED_DURING_SELECTION 0x00001000 +static u32 A_RESELECTED_DURING_SELECTION_used[] __attribute((unused)) = { + 0x0000000b, +}; + +#define A_RESELECTION_IDENTIFIED 0x00001003 +static u32 A_RESELECTION_IDENTIFIED_used[] __attribute((unused)) = { + 0x00000011, + 0x00000015, +}; + +#define A_RESTORE_DATA_PTRS_MSG 0x00000003 +static u32 A_RESTORE_DATA_PTRS_MSG_used[] __attribute((unused)) = { + 0x0000008e, + 0x00000098, + 0x000000b4, + 0x000000dc, + 0x00000100, +}; + +#define A_ReceiveMsgAddress 0x00000000 +static u32 A_ReceiveMsgAddress_used[] __attribute((unused)) = { + 0x0000000f, + 0x00000013, + 0x00000029, + 0x00000031, + 0x00000037, + 0x00000047, + 0x0000004d, + 0x00000053, + 0x00000059, + 0x0000005f, + 0x00000089, + 0x00000093, + 0x000000b1, + 0x000000d7, + 0x000000fb, +}; + +#define A_SAVE_DATA_PTRS_MSG 0x00000002 +static u32 A_SAVE_DATA_PTRS_MSG_used[] __attribute((unused)) = { + 0x0000008c, + 0x00000096, + 0x000000b2, + 0x000000da, + 0x000000fe, +}; + +#define A_SDTR_MSG 0x00000001 +static u32 A_SDTR_MSG_used[] __attribute((unused)) = { +}; + +#define A_SDTR_MSG_AFTER_CMD 0x00000360 +static u32 A_SDTR_MSG_AFTER_CMD_used[] __attribute((unused)) = { +}; + +#define A_SDTR_MSG_BEFORE_CMD 0x00000260 +static u32 A_SDTR_MSG_BEFORE_CMD_used[] __attribute((unused)) = { +}; + +#define A_SDTR_MSG_R 0x00000060 +static u32 A_SDTR_MSG_R_used[] __attribute((unused)) = { +}; + +#define A_SGScriptStartAddress 0x00000000 +static u32 A_SGScriptStartAddress_used[] __attribute((unused)) = { + 0x000000bf, + 0x000000cf, +}; + +#define A_SIMPLE_TAG_MSG 0x00000020 +static u32 A_SIMPLE_TAG_MSG_used[] __attribute((unused)) = { +}; + +#define A_StatusAddress 0x00000000 +static u32 A_StatusAddress_used[] __attribute((unused)) = { + 0x00000119, +}; + +#define A_TWO_BYTE_MSG 0x00000020 +static u32 A_TWO_BYTE_MSG_used[] __attribute((unused)) = { + 0x0000002c, +}; + +#define A_TWO_BYTE_MSG_MASK 0x0000000f +static u32 A_TWO_BYTE_MSG_MASK_used[] __attribute((unused)) = { + 0x0000002c, +}; + +#define A_UNEXPECTED_MSG 0x00000040 +static u32 A_UNEXPECTED_MSG_used[] __attribute((unused)) = { +}; + +#define A_UNEXPECTED_MSG_BEFORE_CMD 0x00000240 +static u32 A_UNEXPECTED_MSG_BEFORE_CMD_used[] __attribute((unused)) = { +}; + +#define A_UNEXPECTED_PHASE 0x00000020 +static u32 A_UNEXPECTED_PHASE_used[] __attribute((unused)) = { +}; + +#define A_UNEXPECTED_PHASE_AFTER_CMD 0x00000320 +static u32 A_UNEXPECTED_PHASE_AFTER_CMD_used[] __attribute((unused)) = { + 0x00000083, +}; + +#define A_UNEXPECTED_PHASE_AFTER_DATA_IN 0x00000520 +static u32 A_UNEXPECTED_PHASE_AFTER_DATA_IN_used[] __attribute((unused)) = { + 0x000000c9, +}; + +#define A_UNEXPECTED_PHASE_AFTER_DATA_OUT 0x00000620 +static u32 A_UNEXPECTED_PHASE_AFTER_DATA_OUT_used[] __attribute((unused)) = { + 0x000000d5, +}; + +#define A_UNEXPECTED_PHASE_BEFORE_CMD 0x00000220 +static u32 A_UNEXPECTED_PHASE_BEFORE_CMD_used[] __attribute((unused)) = { + 0x00000077, +}; + +#define A_WDTR_MSG 0x00000003 +static u32 A_WDTR_MSG_used[] __attribute((unused)) = { +}; + +#define A_WDTR_MSG_AFTER_CMD 0x000003a0 +static u32 A_WDTR_MSG_AFTER_CMD_used[] __attribute((unused)) = { +}; + +#define A_WDTR_MSG_R 0x000000a0 +static u32 A_WDTR_MSG_R_used[] __attribute((unused)) = { +}; + +#define Ent_Disconnect1 0x000002a8 +#define Ent_Disconnect2 0x000002b0 +#define Ent_Disconnect3 0x000003a8 +#define Ent_Disconnect4 0x000003b0 +#define Ent_Disconnect5 0x000003d0 +#define Ent_Disconnect6 0x000003d8 +#define Ent_Disconnect7 0x00000448 +#define Ent_Disconnect8 0x00000450 +#define Ent_Finish1 0x00000488 +#define Ent_Finish2 0x00000490 +#define Ent_GetReselectionData 0x00000038 +#define Ent_GetReselectionWithTag 0x00000048 +#define Ent_IgnoreMessage 0x00000188 +#define Ent_MsgInDuringData 0x00000218 +#define Ent_ReceiveMessage 0x000000a0 +#define Ent_SelectedAsTarget 0x00000058 +#define Ent_SendCommand 0x000001c8 +#define Ent_SendMessage 0x00000078 +#define Ent_SendMessagePhaseMismatch 0x00000090 +#define Ent_SendMessageWithATN 0x00000198 +#define Ent_StartUp 0x00000000 +static u32 LABELPATCHES[] __attribute((unused)) = { + 0x00000001, + 0x00000003, + 0x00000005, + 0x00000009, + 0x00000027, + 0x0000002b, + 0x00000039, + 0x0000003b, + 0x0000003d, + 0x0000003f, + 0x00000041, + 0x0000006b, + 0x0000006d, + 0x0000006f, + 0x00000073, + 0x00000075, + 0x0000007b, + 0x0000007d, + 0x0000007f, + 0x00000081, + 0x00000087, + 0x0000008b, + 0x0000008d, + 0x0000008f, + 0x00000095, + 0x00000097, + 0x00000099, + 0x0000009b, + 0x000000a1, + 0x000000a5, + 0x000000af, + 0x000000b3, + 0x000000b5, + 0x000000b7, + 0x000000bd, + 0x000000c1, + 0x000000c3, + 0x000000c5, + 0x000000cd, + 0x000000d1, + 0x000000d3, + 0x000000d9, + 0x000000db, + 0x000000dd, + 0x000000df, + 0x000000e5, + 0x000000ef, + 0x000000f9, + 0x000000fd, + 0x000000ff, + 0x00000101, + 0x00000103, + 0x00000109, + 0x0000010d, + 0x00000117, + 0x0000011d, +}; + +static struct { + u32 offset; + void *address; +} EXTERNAL_PATCHES[] __attribute((unused)) = { +}; + +static u32 INSTRUCTIONS __attribute((unused)) = 146; +static u32 PATCHES __attribute((unused)) = 56; +static u32 EXTERNAL_PATCHES_LEN __attribute((unused)) = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/53c700_u.h linux.ac/drivers/scsi/53c700_u.h --- linux.vanilla/drivers/scsi/53c700_u.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/53c700_u.h Sun May 13 22:20:12 2001 @@ -0,0 +1,97 @@ +#undef A_AFTER_CMD +#undef A_AFTER_DATA_IN +#undef A_AFTER_DATA_OUT +#undef A_AFTER_SELECTION +#undef A_AFTER_STATUS +#undef A_BEFORE_CMD +#undef A_COMPLETED_SELECTION_AS_TARGET +#undef A_CommandAddress +#undef A_CommandCount +#undef A_DATA_IN_AFTER_DATA_IN +#undef A_DEBUG_INTERRUPT +#undef A_DEBUG_INTERRUPT1 +#undef A_DEBUG_INTERRUPT2 +#undef A_DEBUG_INTERRUPT3 +#undef A_DEBUG_INTERRUPT4 +#undef A_DEBUG_INTERRUPT5 +#undef A_DEBUG_INTERRUPT6 +#undef A_DISCONNECT +#undef A_DISCONNECT_AFTER_CMD +#undef A_DISCONNECT_AFTER_DATA +#undef A_DISCONNECT_DURING_DATA +#undef A_DISCONNECT_MSG +#undef A_DURING_DATA_IN +#undef A_Device_ID +#undef A_EXTENDED_MSG +#undef A_FATAL +#undef A_FATAL_ILLEGAL_MSG_LENGTH +#undef A_FATAL_NOT_MSG_IN_AFTER_SELECTION +#undef A_FATAL_SEND_MSG +#undef A_FATAL_UNEXPECTED_RESELECTION_MSG +#undef A_GOOD_STATUS +#undef A_GOOD_STATUS_AFTER_STATUS +#undef A_IDENTIFY_MSG +#undef A_IDENTIFY_MSG_MASK +#undef A_MSG_IN +#undef A_MSG_IN_AFTER_CMD +#undef A_MSG_IN_AFTER_DATA_IN +#undef A_MSG_IN_AFTER_DATA_OUT +#undef A_MSG_IN_BEFORE_CMD +#undef A_MSG_IN_DURING_DATA_IN +#undef A_MSG_OUT +#undef A_MSG_OUT_AFTER_DATA_IN +#undef A_MessageCount +#undef A_MessageLocation +#undef A_NOT_MSG_IN +#undef A_NOT_MSG_IN_AFTER_STATUS +#undef A_NOT_MSG_OUT +#undef A_NOT_MSG_OUT_AFTER_SELECTION +#undef A_PARITY_ERROR_MSG +#undef A_REJECT_MSG +#undef A_REJECT_MSG_BEFORE_CMD +#undef A_REJECT_MSG_R +#undef A_RESELECTED_DURING_SELECTION +#undef A_RESELECTION_IDENTIFIED +#undef A_RESTORE_DATA_PTRS_MSG +#undef A_ReceiveMsgAddress +#undef A_SAVE_DATA_PTRS_MSG +#undef A_SDTR_MSG +#undef A_SDTR_MSG_AFTER_CMD +#undef A_SDTR_MSG_BEFORE_CMD +#undef A_SDTR_MSG_R +#undef A_SGScriptStartAddress +#undef A_SIMPLE_TAG_MSG +#undef A_StatusAddress +#undef A_TWO_BYTE_MSG +#undef A_TWO_BYTE_MSG_MASK +#undef A_UNEXPECTED_MSG +#undef A_UNEXPECTED_MSG_BEFORE_CMD +#undef A_UNEXPECTED_PHASE +#undef A_UNEXPECTED_PHASE_AFTER_CMD +#undef A_UNEXPECTED_PHASE_AFTER_DATA_IN +#undef A_UNEXPECTED_PHASE_AFTER_DATA_OUT +#undef A_UNEXPECTED_PHASE_BEFORE_CMD +#undef A_WDTR_MSG +#undef A_WDTR_MSG_AFTER_CMD +#undef A_WDTR_MSG_R +#undef Ent_Disconnect1 +#undef Ent_Disconnect2 +#undef Ent_Disconnect3 +#undef Ent_Disconnect4 +#undef Ent_Disconnect5 +#undef Ent_Disconnect6 +#undef Ent_Disconnect7 +#undef Ent_Disconnect8 +#undef Ent_Finish1 +#undef Ent_Finish2 +#undef Ent_GetReselectionData +#undef Ent_GetReselectionWithTag +#undef Ent_IgnoreMessage +#undef Ent_MsgInDuringData +#undef Ent_ReceiveMessage +#undef Ent_SelectedAsTarget +#undef Ent_SendCommand +#undef Ent_SendMessage +#undef Ent_SendMessagePhaseMismatch +#undef Ent_SendMessageWithATN +#undef Ent_StartUp diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/BusLogic.c linux.ac/drivers/scsi/BusLogic.c --- linux.vanilla/drivers/scsi/BusLogic.c Sun Nov 12 03:01:11 2000 +++ linux.ac/drivers/scsi/BusLogic.c Tue Apr 3 17:54:59 2001 @@ -770,15 +770,19 @@ BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest; unsigned char Bus = PCI_Device->bus->number; unsigned char Device = PCI_Device->devfn >> 3; - unsigned int IRQ_Channel = PCI_Device->irq; - unsigned long BaseAddress0 = pci_resource_start(PCI_Device, 0); - unsigned long BaseAddress1 = pci_resource_start(PCI_Device, 1); - BusLogic_IO_Address_T IO_Address = BaseAddress0; - BusLogic_PCI_Address_T PCI_Address = BaseAddress1; + unsigned int IRQ_Channel; + unsigned long BaseAddress0; + unsigned long BaseAddress1; + BusLogic_IO_Address_T IO_Address; + BusLogic_PCI_Address_T PCI_Address; if (pci_enable_device(PCI_Device)) continue; + IRQ_Channel = PCI_Device->irq; + IO_Address = BaseAddress0 = pci_resource_start(PCI_Device, 0); + PCI_Address = BaseAddress1 = pci_resource_start(PCI_Device, 1); + if (pci_resource_flags(PCI_Device, 0) & IORESOURCE_MEM) { BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for " @@ -2547,7 +2551,7 @@ int SynchronousTransferRate = 0; if (BusLogic_FlashPointHostAdapterP(HostAdapter)) { - boolean WideTransfersActive; + unsigned char WideTransfersActive; FlashPoint_InquireTargetInfo( HostAdapter->CardHandle, TargetID, &HostAdapter->SynchronousPeriod[TargetID], diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/BusLogic.h linux.ac/drivers/scsi/BusLogic.h --- linux.vanilla/drivers/scsi/BusLogic.h Mon Dec 11 21:18:58 2000 +++ linux.ac/drivers/scsi/BusLogic.h Tue Apr 3 18:15:45 2001 @@ -78,6 +78,7 @@ reset: BusLogic_ResetCommand, /* Reset Command Function */ \ bios_param: BusLogic_BIOSDiskParameters, /* BIOS Disk Parameters */ \ unchecked_isa_dma: 1, /* Default Initial Value */ \ + max_sectors: 128, /* I/O queue len limit */ \ use_clustering: ENABLE_CLUSTERING } /* Enable Clustering */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ChangeLog.ncr53c8xx linux.ac/drivers/scsi/ChangeLog.ncr53c8xx --- linux.vanilla/drivers/scsi/ChangeLog.ncr53c8xx Tue Apr 3 17:32:18 2001 +++ linux.ac/drivers/scsi/ChangeLog.ncr53c8xx Thu May 24 00:02:48 2001 @@ -1,3 +1,9 @@ +Sat May 12 12:00 2001 Gerard Roudier (groudier@club-internet.fr) + * version ncr53c8xx-3.4.3b + - Ensure LEDC bit in GPCNTL is cleared when reading the NVRAM. + Fix sent by Stig Telfer . + - Define scsi_set_pci_device() as nil for kernel < 2.4.4. + Mon Feb 12 22:30 2001 Gerard Roudier (groudier@club-internet.fr) * version ncr53c8xx-3.4.3 - Call pci_enable_device() as AC wants this to be done. @@ -238,7 +244,7 @@ - Changes from Eddie Dost for Sparc and Alpha: ioremap/iounmap support for Sparc. pcivtophys changed to bus_dvma_to_phys. - - Add the 53c876 description to the chip table. This is only usefull + - Add the 53c876 description to the chip table. This is only useful for printing the right name of the controller. - DEL-441 Item 2 work-around for the 53c876 rev <= 5 (0x15). - Add additionnal checking of INQUIRY data: @@ -304,7 +310,7 @@ Sat Jun 20 20:00 1998 Gerard Roudier (groudier@club-internet.fr) * revision 3.0c - Add a boot setup option that allows to set up device queue depths - at boot-up. This option is very usefull since Linux does not + at boot-up. This option is very useful since Linux does not allow to change scsi device queue depth once the system has been booted up. @@ -333,7 +339,7 @@ kernel version >= 2.1.105. - Replace all printf(s) by printk(s). After all, the ncr53c8xx is a driver for Linux. - - Perform auto-sense on COMMAND TERMINATED. Not sure it is usefull. + - Perform auto-sense on COMMAND TERMINATED. Not sure it is useful. - Some other minor changes. Tue Jun 4 23:00 1998 Gerard Roudier (groudier@club-internet.fr) @@ -341,7 +347,7 @@ - Code cleanup and simplification: Remove kernel 1.2.X and 1.3.X support. Remove the _old_ target capabilities table. - Remove the error recovery code that have'nt been really usefull. + Remove the error recovery code that have'nt been really useful. Use a single alignment boundary (CACHE_LINE_SIZE) for data structures. - Several aggressive SCRIPTS optimizations and changes: diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ChangeLog.sym53c8xx linux.ac/drivers/scsi/ChangeLog.sym53c8xx --- linux.vanilla/drivers/scsi/ChangeLog.sym53c8xx Tue Apr 3 17:32:18 2001 +++ linux.ac/drivers/scsi/ChangeLog.sym53c8xx Thu May 24 00:03:23 2001 @@ -1,3 +1,24 @@ +Sat May 12 12:00 2001 Gerard Roudier (groudier@club-internet.fr) + * version sym53c8xx-1.7.3c + - Ensure LEDC bit in GPCNTL is cleared when reading the NVRAM. + Fix sent by Stig Telfer . + - Backport from SYM-2 the work-around that allows to support + hardwares that fail PCI parity checking. + - Check that we received at least 8 bytes of INQUIRY response + for byte 7, that contains device capabilities, to be valid. + - Define scsi_set_pci_device() as nil for kernel < 2.4.4. + - + A couple of minor changes. + +Sat Apr 7 19:30 2001 Gerard Roudier (groudier@club-internet.fr) + * version sym53c8xx-1.7.3b + - Fix an unaligned LOAD from scripts (was used as dummy read). + - In ncr_soft_reset(), only try to ABORT the current operation + for chips that support SRUN bit in ISTAT1 and if SCRIPTS are + currently running, as 896 and 1010 manuals suggest. + - In the CCB abort path, donnot assume that the CCB is currently + queued to SCRIPTS. This is not always true, notably after a + QUEUE FULL status or when using untagged commands. + Sun Mar 4 18:30 2001 Gerard Roudier (groudier@club-internet.fr) * version sym53c8xx-1.7.3a - Fix an issue in the ncr_int_udc() (unexpected disconnect) @@ -338,7 +359,7 @@ Tue Apr 15 10:00 1999 Gerard Roudier (groudier@club-internet.fr) * version sym53c8xx-1.3e - Support for any number of LUNs (64) (SPI2-compliant). - (Btw, this may only be ever usefull under linux-2.2 ;-)) + (Btw, this may only be ever useful under linux-2.2 ;-)) Sun Apr 11 10:00 1999 Gerard Roudier (groudier@club-internet.fr) * version sym53c8xx-1.3d diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/Config.in linux.ac/drivers/scsi/Config.in --- linux.vanilla/drivers/scsi/Config.in Tue Apr 3 17:32:18 2001 +++ linux.ac/drivers/scsi/Config.in Sun May 13 20:25:46 2001 @@ -101,7 +101,7 @@ fi fi if [ "$CONFIG_X86" = "y" ]; then - dep_tristate 'IBM ServeRAID support' CONFIG_SCSI_IPS $CONFIG_SCSI + dep_tristate 'IBM ServeRAID support' CONFIG_SCSI_IPS $CONFIG_SCSI $CONFIG_PCI fi dep_tristate 'Initio 9100U(W) support' CONFIG_SCSI_INITIO $CONFIG_SCSI $CONFIG_PCI dep_tristate 'Initio INI-A100U2W support' CONFIG_SCSI_INIA100 $CONFIG_SCSI $CONFIG_PCI @@ -114,6 +114,7 @@ fi fi dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI +dep_tristate 'NCR Dual 700 MCA SCSI support' CONFIG_SCSI_NCR_D700 $CONFIG_SCSI $CONFIG_MCA dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI $CONFIG_PCI if [ "$CONFIG_SCSI_NCR53C7xx" != "n" ]; then bool ' always negotiate synchronous transfers' CONFIG_SCSI_NCR53C7xx_sync @@ -196,6 +197,30 @@ fi if [ "$CONFIG_MIPS_JAZZ" = "y" ]; then bool 'MIPS JAZZ FAS216 SCSI support' CONFIG_JAZZ_ESP +fi + +if [ "$CONFIG_AMIGA" = "y" ]; then + dep_tristate 'A3000 WD33C93A support' CONFIG_A3000_SCSI $CONFIG_SCSI + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'A4000T SCSI support (EXPERIMENTAL)' CONFIG_A4000T_SCSI + fi +fi +if [ "$CONFIG_ZORRO" = "y" ]; then + dep_tristate 'A2091/A590 WD33C93A support' CONFIG_A2091_SCSI $CONFIG_SCSI + dep_tristate 'GVP Series II WD33C93A support' CONFIG_GVP11_SCSI $CONFIG_SCSI + dep_tristate 'CyberStorm SCSI support' CONFIG_CYBERSTORM_SCSI $CONFIG_SCSI + dep_tristate 'CyberStorm Mk II SCSI support' CONFIG_CYBERSTORMII_SCSI $CONFIG_SCSI + dep_tristate 'Blizzard 2060 SCSI support' CONFIG_BLZ2060_SCSI $CONFIG_SCSI + dep_tristate 'Blizzard 1230IV/1260 SCSI support' CONFIG_BLZ1230_SCSI $CONFIG_SCSI + dep_tristate 'Fastlane SCSI support' CONFIG_FASTLANE_SCSI $CONFIG_SCSI + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'A4091 SCSI support (EXPERIMENTAL)' CONFIG_A4091_SCSI + bool 'WarpEngine SCSI support (EXPERIMENTAL)' CONFIG_WARPENGINE_SCSI + bool 'Blizzard PowerUP 603e+ SCSI (EXPERIMENTAL)' CONFIG_BLZ603EPLUS_SCSI + dep_tristate 'BSC Oktagon SCSI support (EXPERIMENTAL)' CONFIG_OKTAGON_SCSI $CONFIG_SCSI +# bool 'Cyberstorm Mk III SCSI support (EXPERIMENTAL)' CONFIG_CYBERSTORMIII_SCSI +# bool 'GVP Turbo 040/060 SCSI support (EXPERIMENTAL)' CONFIG_GVP_TURBO_SCSI + fi fi endmenu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/FlashPoint.c linux.ac/drivers/scsi/FlashPoint.c --- linux.vanilla/drivers/scsi/FlashPoint.c Mon Aug 17 09:01:01 1998 +++ linux.ac/drivers/scsi/FlashPoint.c Tue Apr 3 17:54:59 2001 @@ -629,7 +629,7 @@ #if (FW_TYPE==_UCB_MGR_) #define HBA_AUTO_SENSE_FAIL 0x1B #define HBA_TQ_REJECTED 0x1C - #define HBA_UNSUPORTED_MSG 0x1D + #define HBA_UNSUPPORTED_MSG 0x1D #define HBA_HW_ERROR 0x20 #define HBA_ATN_NOT_RESPONDED 0x21 #define HBA_SCSI_RESET_BY_ADAPTER 0x22 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/Makefile linux.ac/drivers/scsi/Makefile --- linux.vanilla/drivers/scsi/Makefile Sat May 26 16:53:12 2001 +++ linux.ac/drivers/scsi/Makefile Thu May 17 14:12:39 2001 @@ -31,7 +31,7 @@ endif endif -export-objs := scsi_syms.o +export-objs := scsi_syms.o 53c700.o CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS @@ -80,6 +80,7 @@ obj-$(CONFIG_SCSI_IN2000) += in2000.o obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o +obj-$(CONFIG_SCSI_NCR_D700) += NCR_D700.o 53c700.o obj-$(CONFIG_SCSI_SYM53C416) += sym53c416.o obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas.o obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o @@ -193,3 +194,10 @@ sim710_u.h: sim710_d.h sim710.o : sim710_d.h + +53c700_d.h: 53c700.scr script_asm.pl + $(PERL) -s script_asm.pl -ncr7x0_family < 53c700.scr + mv script.h 53c700_d.h + mv scriptu.h 53c700_u.h + +53c700.o: 53c700_d.h diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/NCR53c406a.c linux.ac/drivers/scsi/NCR53c406a.c --- linux.vanilla/drivers/scsi/NCR53c406a.c Sat May 26 16:53:12 2001 +++ linux.ac/drivers/scsi/NCR53c406a.c Wed May 23 00:10:46 2001 @@ -221,7 +221,7 @@ (void *)0xc8000 }; #define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned )) -#endif USE_BIOS +#endif /* USE_BIOS */ /* possible i/o port addresses */ static unsigned short ports[] = @@ -244,7 +244,7 @@ { "Copyright (C) Acculogic, Inc.\r\n2.8M Diskette Extension Bios ver 4.04.03 03/01/1993", 61, 82 }, }; #define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature )) -#endif USE_BIOS +#endif /* USE_BIOS */ /* ============================================================ */ @@ -347,7 +347,7 @@ return tmp; } -#endif USE_DMA +#endif /* USE_DMA */ #if USE_PIO static __inline__ int NCR53c406a_pio_read(unsigned char *request, @@ -455,7 +455,7 @@ } return 0; } -#endif USE_PIO +#endif /* USE_PIO */ int __init NCR53c406a_detect(Scsi_Host_Template * tpnt){ @@ -481,7 +481,7 @@ } DEB(printk("NCR53c406a BIOS found at %X\n", (unsigned int) bios_base);); -#endif USE_BIOS +#endif /* USE_BIOS */ #ifdef PORT_BASE if (!request_region(port_base, 0x10, "NCR53c406a")) /* ports already snatched */ @@ -512,7 +512,7 @@ } } } -#endif PORT_BASE +#endif /* PORT_BASE */ if(!port_base){ /* no ports found */ printk("NCR53c406a: no available ports found\n"); @@ -550,7 +550,7 @@ #if USE_DMA printk("NCR53c406a: No interrupts found and DMA mode defined. Giving up.\n"); goto err_release; -#endif USE_DMA +#endif /* USE_DMA */ } else { DEB(printk("NCR53c406a: Shouldn't get here!\n")); @@ -565,7 +565,7 @@ } DEB(printk("Allocated DMA channel %d\n", dma_chan)); -#endif USE_DMA +#endif /* USE_DMA */ tpnt->present = 1; tpnt->proc_name = "NCR53c406a"; @@ -665,7 +665,7 @@ } -static void wait_intr() { +static void wait_intr(void) { int i = jiffies + WATCHDOG; while(time_after(i,jiffies) && !(inb(STAT_REG)&0xe0)) /* wait for a pseudo-interrupt */ @@ -820,8 +820,8 @@ printk("\n"); #else printk(", pio=%02x\n", pio_status); -#endif USE_DMA -#endif NCR53C406A_DEBUG +#endif /* USE_DMA */ +#endif /* NCR53C406A_DEBUG */ if(int_reg & 0x80){ /* SCSI reset intr */ rtrc(3); @@ -840,7 +840,7 @@ current_SC->scsi_done(current_SC); return; } -#endif USE_PIO +#endif /* USE_PIO */ if(status & 0x20) { /* Parity error */ printk("NCR53c406a: Warning: parity error!\n"); @@ -885,7 +885,7 @@ #if USE_DMA /* No s/g support for DMA */ NCR53c406a_dma_write(current_SC->request_buffer, current_SC->request_bufflen); -#endif USE_DMA +#endif /* USE_DMA */ outb(TRANSFER_INFO | DMA_OP, CMD_REG); #if USE_PIO if (!current_SC->use_sg) /* Don't use scatter-gather */ @@ -900,7 +900,7 @@ } } REG0; -#endif USE_PIO +#endif /* USE_PIO */ } break; @@ -914,7 +914,7 @@ #if USE_DMA /* No s/g support for DMA */ NCR53c406a_dma_read(current_SC->request_buffer, current_SC->request_bufflen); -#endif USE_DMA +#endif /* USE_DMA */ outb(TRANSFER_INFO | DMA_OP, CMD_REG); #if USE_PIO if (!current_SC->use_sg) /* Don't use scatter-gather */ @@ -929,7 +929,7 @@ } } REG0; -#endif USE_PIO +#endif /* USE_PIO */ } break; @@ -981,7 +981,7 @@ } #ifndef IRQ_LEV -static int irq_probe() +static int irq_probe(void) { int irqs, irq; int i; @@ -1011,9 +1011,9 @@ return irq; } -#endif IRQ_LEV +#endif /* IRQ_LEV */ -static void chip_init() +static void chip_init(void) { REG1; #if USE_DMA diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/NCR_D700.c linux.ac/drivers/scsi/NCR_D700.c --- linux.vanilla/drivers/scsi/NCR_D700.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/NCR_D700.c Sun May 13 22:19:40 2001 @@ -0,0 +1,324 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* NCR Dual 700 MCA SCSI Driver + * + * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com +**----------------------------------------------------------------------------- +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** 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. +** +**----------------------------------------------------------------------------- + */ + +/* Notes: + * + * Most of the work is done in the chip specific module, 53c700.o + * + * TODO List: + * + * 1. Extract the SCSI ID from the voyager CMOS table (necessary to + * support multi-host environments. + * + * */ + + +/* CHANGELOG + * + * Version 2.1 + * + * Modularise the driver into a Board piece (this file) and a chip + * piece 53c700.[ch] and 53c700.scr, added module options. You can + * now specify the scsi id by the parameters + * + * NCR_D700=slot: [siop:] id: .... + * + * They need to be comma separated if compiled into the kernel + * + * Version 2.0 + * + * Initial implementation of TCQ (Tag Command Queueing). TCQ is full + * featured and uses the clock algorithm to keep track of outstanding + * tags and guard against individual tag starvation. Also fixed a bug + * in all of the 1.x versions where the D700_data_residue() function + * was returning results off by 32 bytes (and thus causing the same 32 + * bytes to be written twice corrupting the data block). It turns out + * the 53c700 only has a 6 bit DBC and DFIFO registers not 7 bit ones + * like the 53c710 (The 710 is the only data manual still available, + * which I'd been using to program the 700). + * + * Version 1.2 + * + * Much improved message handling engine + * + * Version 1.1 + * + * Add code to handle selection reasonably correctly. By the time we + * get the selection interrupt, we've already responded, but drop off the + * bus and hope the selector will go away. + * + * Version 1.0: + * + * Initial release. Fully functional except for procfs and tag + * command queueing. Has only been tested on cards with 53c700-66 + * chips and only single ended. Features are + * + * 1. Synchronous data transfers to offset 8 (limit of 700-66) and + * 100ns (10MHz) limit of SCSI-2 + * + * 2. Disconnection and reselection + * + * Testing: + * + * I've only really tested this with the 700-66 chip, but have done + * soak tests in multi-device environments to verify that + * disconnections and reselections are being processed correctly. + * */ + +#define NCR_D700_VERSION "2.1" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" + +#include "53c700.h" +#include "NCR_D700.h" + +#ifndef CONFIG_MCA +#error "NCR_D700 driver only compiles for MCA" +#endif + +#ifdef NCR_D700_DEBUG +#define STATIC +#else +#define STATIC static +#endif + +#ifdef MODULE + +char *NCR_D700; /* command line from insmod */ + +MODULE_AUTHOR("James Bottomley"); +MODULE_DESCRIPTION("NCR Dual700 SCSI Driver"); +MODULE_PARM(NCR_D700, "s"); + +#endif + +static __u8 __initdata id_array[2*(MCA_MAX_SLOT_NR + 1)] = + { [0 ... 2*(MCA_MAX_SLOT_NR + 1)-1] = 7 }; + +#ifdef MODULE +#define ARG_SEP ' ' +#else +#define ARG_SEP ',' +#endif + +static int __init +param_setup(char *string) +{ + char *pos = string, *next; + int slot = -1, siop = -1; + + while(pos != NULL && (next = strchr(pos, ':')) != NULL) { + int val = (int)simple_strtoul(++next, NULL, 0); + + if(!strncmp(pos, "slot:", 5)) + slot = val; + else if(!strncmp(pos, "siop:", 5)) + siop = val; + else if(!strncmp(pos, "id:", 3)) { + if(slot == -1) { + printk(KERN_WARNING "NCR D700: Must specify slot for id parameter\n"); + } else if(slot > MCA_MAX_SLOT_NR) { + printk(KERN_WARNING "NCR D700: Illegal slot %d for id %d\n", slot, val); + } else { + if(siop != 0 && siop != 1) { + id_array[slot*2] = val; + id_array[slot*2 + 1] =val; + } else { + id_array[slot*2 + siop] = val; + } + } + } + if((pos = strchr(pos, ARG_SEP)) != NULL) + pos++; + } + return 1; +} + +#ifndef MODULE +__setup("NCR_D700=", param_setup); +#endif + +/* Detect a D700 card. Note, because of the set up---the chips are + * essentially connectecd to the MCA bus independently, it is easier + * to set them up as two separate host adapters, rather than one + * adapter with two channels */ +STATIC int __init +D700_detect(Scsi_Host_Template *tpnt) +{ + int slot = 0; + int found = 0; + int differential; + int banner = 1; + + if(!MCA_bus) + return 0; + +#ifdef MODULE + if(NCR_D700) + param_setup(NCR_D700); +#endif + + for(slot = 0; (slot = mca_find_adapter(NCR_D700_MCA_ID, slot)) != MCA_NOTFOUND; slot++) { + int irq, i; + int pos3j, pos3k, pos3a, pos3b, pos4; + __u32 base_addr, offset_addr; + struct Scsi_Host *host = NULL; + + /* enable board interrupt */ + pos4 = mca_read_pos(slot, 4); + pos4 |= 0x4; + mca_write_pos(slot, 4, pos4); + + mca_write_pos(slot, 6, 9); + pos3j = mca_read_pos(slot, 3); + mca_write_pos(slot, 6, 10); + pos3k = mca_read_pos(slot, 3); + mca_write_pos(slot, 6, 0); + pos3a = mca_read_pos(slot, 3); + mca_write_pos(slot, 6, 1); + pos3b = mca_read_pos(slot, 3); + + base_addr = ((pos3j << 8) | pos3k) & 0xfffffff0; + offset_addr = ((pos3a << 8) | pos3b) & 0xffffff70; + + irq = (pos4 & 0x3) + 11; + if(irq >= 13) + irq++; + if(banner) { + printk(KERN_NOTICE "NCR D700: Driver Version " NCR_D700_VERSION "\n" + "NCR D700: Copyright (c) 2001 by James.Bottomley@HansenPartnership.com\n" + "NCR D700:\n"); + banner = 0; + } + printk(KERN_NOTICE "NCR D700: found in slot %d irq = %d I/O base = 0x%x\n", slot, irq, offset_addr); + + tpnt->proc_name = "NCR_D700"; + + /*outb(BOARD_RESET, base_addr);*/ + + /* clear any pending interrupts */ + (void)inb(base_addr + 0x08); + /* get modctl, used later for setting diff bits */ + switch(differential = (inb(base_addr + 0x08) >> 6)) { + case 0x00: + /* only SIOP1 differential */ + differential = 0x02; + break; + case 0x01: + /* Both SIOPs differential */ + differential = 0x03; + break; + case 0x03: + /* No SIOPs differential */ + differential = 0x00; + break; + default: + printk(KERN_ERR "D700: UNEXPECTED DIFFERENTIAL RESULT 0x%02x\n", + differential); + differential = 0x00; + break; + } + + /* plumb in both 700 chips */ + for(i=0; i<2; i++) { + __u32 region = offset_addr | (0x80 * i); + struct NCR_700_Host_Parameters *hostdata = + kmalloc(sizeof(struct NCR_700_Host_Parameters), + GFP_KERNEL); + if(hostdata == NULL) { + printk(KERN_ERR "NCR D700: Failed to allocate host data for channel %d, detatching\n", i); + continue; + } + memset(hostdata, 0, sizeof(struct NCR_700_Host_Parameters)); + request_region(region, 64, "NCR_D700"); + + /* Fill in the three required pieces of hostdata */ + hostdata->base = region; + hostdata->differential = (((1<clock = NCR_D700_CLOCK_MHZ; + /* and register the chip */ + if((host = NCR_700_detect(tpnt, hostdata)) == NULL) { + kfree(hostdata); + continue; + } + host->irq = irq; + /* FIXME: Read this from SUS */ + host->this_id = id_array[slot * 2 + i]; + printk(KERN_NOTICE "NCR D700: SIOP%d, SCSI id is %d\n", + i, host->this_id); + if(request_irq(irq, NCR_700_intr, SA_SHIRQ, "NCR_D700", host)) { + printk(KERN_ERR "NCR D700, channel %d: irq problem, detatching\n", i); + NCR_700_release(host); + release_region(host->base, 64); + continue; + } + found++; + } + } + + return found; +} + + +STATIC int +D700_release(struct Scsi_Host *host) +{ + struct D700_Host_Parameters *hostdata = + (struct D700_Host_Parameters *)host->hostdata[0]; + + NCR_700_release(host); + kfree(hostdata); + free_irq(host->irq, host); + release_region(host->base, 64); + return 1; +} + + +static Scsi_Host_Template driver_template = NCR_D700_SCSI; + +#include "scsi_module.c" + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/NCR_D700.h linux.ac/drivers/scsi/NCR_D700.h --- linux.vanilla/drivers/scsi/NCR_D700.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/NCR_D700.h Sun May 13 20:25:46 2001 @@ -0,0 +1,45 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* NCR Dual 700 MCA SCSI Driver + * + * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com + */ + +#ifndef _NCR_D700_H +#define _NCR_D700_H + +/* Don't turn on debugging messages */ +#undef NCR_D700_DEBUG + +/* The MCA identifier */ +#define NCR_D700_MCA_ID 0x0092 + +static int D700_detect(Scsi_Host_Template *); +static int D700_release(struct Scsi_Host *host); + + +/* Host template. Note the name and proc_name are optional, all the + * remaining parameters shown below must be filled in. The 53c700 + * routine NCR_700_detect will fill in all of the missing routines */ +#define NCR_D700_SCSI { \ + name: "NCR Dual 700 MCA", \ + proc_name: "NCR_D700", \ + detect: D700_detect, \ + release: D700_release, \ + this_id: 7, \ +} + + +/* Defines for the Board registers */ +#define BOARD_RESET 0x80 /* board level reset */ +#define ADD_PARENB 0x04 /* Address Parity Enabled */ +#define DAT_PARENB 0x01 /* Data Parity Enabled */ +#define SFBK_ENB 0x10 /* SFDBK Interrupt Enabled */ +#define LED0GREEN 0x20 /* Led 0 (red 0; green 1) */ +#define LED1GREEN 0x40 /* Led 1 (red 0; green 1) */ +#define LED0RED 0xDF /* Led 0 (red 0; green 1) */ +#define LED1RED 0xBF /* Led 1 (red 0; green 1) */ + +#define NCR_D700_CLOCK_MHZ 50 + +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/README.53c700 linux.ac/drivers/scsi/README.53c700 --- linux.vanilla/drivers/scsi/README.53c700 Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/README.53c700 Sun May 13 20:25:46 2001 @@ -0,0 +1,17 @@ +This driver supports the 53c700 and 53c700-66 chips only. It is full +featured and does sync (-66 only), disconnects and tag command +queueing. + +Since the 53c700 must be interfaced to a bus, you need to wrapper the +card detector around this driver. For an example, see the +NCR_D700.[ch] files. + +The comments in the 53c700.[ch] files tell you which parts you need to +fill in to get the driver working. + +The driver is currently I/O mapped only, but it should be easy enough +to memory map (just make the port reads #defines with MEM_MAPPED for +memory mapping or nothing for I/O mapping, specify an extra rule for +53c700-mem.o with the -DMEM_MAPPED flag and make your driver use it, +that way the make rules will generate the correct version). + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/README.aic7xxx linux.ac/drivers/scsi/README.aic7xxx --- linux.vanilla/drivers/scsi/README.aic7xxx Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/README.aic7xxx Tue Apr 3 17:54:59 2001 @@ -0,0 +1,477 @@ + AIC7xxx Driver for Linux + +Introduction +---------------------------- +The AIC7xxx SCSI driver adds support for Adaptec (http://www.adaptec.com) +SCSI controllers and chipsets. Major portions of the driver and driver +development are shared between both Linux and FreeBSD. Support for the +AIC-7xxx chipsets have been in the default Linux kernel since approximately +linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD +2.1.0 or later. + + Supported cards/chipsets + ---------------------------- + Adaptec Cards + ---------------------------- + AHA-274x + AHA-274xT + AHA-274xW + AHA-284x + AHA-284xW + All PCI based cards using any of the chipsets listed under motherboard + chipsets. In general, this means *all* of the Adaptec SCSI controllers + except the ones specifically excluded later on in this document. + + Motherboard Chipsets + ---------------------------- + AIC-777x + AIC-785x + AIC-786x + AIC-787x + AIC-788x + AIC-789x + AIC-3860 + + Bus Types + ---------------------------- + W - Wide SCSI, SCSI-3, 16bit bus, 68pin connector, will also support + SCSI-1/SCSI-2 50pin devices, transfer rates up to 20MB/s. + U - Ultra SCSI, transfer rates up to 40MB/s. + U2- Ultra 2 SCSI, transfer rates up to 80MB/s. + U3- Ultra 3 SCSI, transfer rates up to 160MB/s. + D - Differential SCSI. + T - Twin Channel SCSI. Up to 14 SCSI devices. + + AHA-274x - EISA SCSI controller + AHA-284x - VLB SCSI controller + AHA-29xx - PCI SCSI controller + AHA-39xx - PCI controllers with multiple separate SCSI channels on-board. + + Not Supported Devices + ------------------------------ + Adaptec Cards + ---------------------------- + AHA-2920 (Only the cards that use the Future Domain chipset are not + supported, any 2920 cards based on Adaptec AIC chipsets, + such as the 2920C, are supported) + AAA-13x Raid Adapters + AAA-113x Raid Port Card + + Motherboard Chipsets + ---------------------------- + AIC-781x + + Bus Types + ---------------------------- + R - Raid Port busses are not supported. + + The hardware RAID devices sold by Adaptec are *NOT* supported by this + driver (and will people please stop emailing me about them, they are + a totally separate beast from the bare SCSI controllers and this driver + can not be retrofitted in any sane manner to support the hardware RAID + features on those cards - Doug Ledford). + + + People + ------------------------------ + Justin T Gibbs gibbs@plutotech.com + (BSD Driver Author) + Dan Eischen deischen@iworks.InterWorks.org + (Original Linux Driver Co-maintainer) + Dean Gehnert deang@teleport.com + (Original Linux FTP/patch maintainer) + Jess Johnson jester@frenzy.com + (AIC7xxx FAQ author) + Doug Ledford dledford@redhat.com + (Current Linux aic7xxx-5.x.x Driver/Patch/FTP maintainer) + + Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original + author of the driver. John has since retired from the project. Thanks + again for all his work! + + Mailing list + ------------------------------ + There is a mailing list available for users who want to track development + and converse with other users and developers. This list is for both + FreeBSD and Linux support of the AIC7xxx chipsets. + + To subscribe to the AIC7xxx mailing list send mail to the list server, + with "subscribe AIC7xxx" in the body (no Subject: required): + To: majordomo@FreeBSD.ORG + --- + subscribe AIC7xxx + + To unsubscribe from the list, send mail to the list server with: + To: majordomo@FreeBSD.ORG + --- + unsubscribe AIC7xxx + + Send regular messages and replies to: AIC7xxx@FreeBSD.ORG + + Boot Command line options + ------------------------------ + "aic7xxx=no_reset" - Eliminate the SCSI bus reset during startup. + Some SCSI devices need the initial reset that this option disables + in order to work. If you have problems at bootup, please make sure + you aren't using this option. + + "aic7xxx=reverse_scan" - Certain PCI motherboards scan for devices at + bootup by scanning from the highest numbered PCI device to the + lowest numbered PCI device, others do just the opposite and scan + from lowest to highest numbered PCI device. There is no reliable + way to autodetect this ordering. So, we default to the most common + order, which is lowest to highest. Then, in case your motherboard + scans from highest to lowest, we have this option. If your BIOS + finds the drives on controller A before controller B but the linux + kernel finds your drives on controller B before A, then you should + use this option. + + "aic7xxx=extended" - Force the driver to detect extended drive translation + on your controller. This helps those people who have cards without + a SEEPROM make sure that linux and all other operating systems think + the same way about your hard drives. + + "aic7xxx=scbram" - Some cards have external SCB RAM that can be used to + give the card more hardware SCB slots. This allows the driver to use + that SCB RAM. Without this option, the driver won't touch the SCB + RAM because it is known to cause problems on a few cards out there + (such as 3985 class cards). + + "aic7xxx=irq_trigger:x" - Replace x with either 0 or 1 to force the kernel + to use the correct IRQ type for your card. This only applies to EISA + based controllers. On these controllers, 0 is for Edge triggered + interrupts, and 1 is for Level triggered interrupts. If you aren't + sure or don't know which IRQ trigger type your EISA card uses, then + let the kernel autodetect the trigger type. + + "aic7xxx=verbose" - This option can be used in one of two ways. If you + simply specify aic7xxx=verbose, then the kernel will automatically + pick the default set of verbose messages for you to see. + Alternatively, you can specify the command as + "aic7xxx=verbose:0xXXXX" where the X entries are replaced with + hexadecimal digits. This option is a bit field type option. For + a full listing of the available options, search for the + #define VERBOSE_xxxxxx lines in the aic7xxx.c file. If you want + verbose messages, then it is recommended that you simply use the + aic7xxx=verbose variant of this command. + + "aic7xxx=pci_parity:x" - This option controls whether or not the driver + enables PCI parity error checking on the PCI bus. By default, this + checking is disabled. To enable the checks, simply specify pci_parity + with no value afterwords. To reverse the parity from even to odd, + supply any number other than 0 or 255. In short: + pci_parity - Even parity checking (even is the normal PCI parity) + pci_parity:x - Where x > 0, Odd parity checking + pci_parity:0 - No check (default) + NOTE: In order to get Even PCI parity checking, you must use the + version of the option that does not include the : and a number at + the end (unless you want to enter exactly 2^32 - 1 as the number). + + "aic7xxx=no_probe" - This option will disable the probing for any VLB + based 2842 controllers and any EISA based controllers. This is + needed on certain newer motherboards where the normal EISA I/O ranges + have been claimed by other PCI devices. Probing on those machines + will often result in the machine crashing or spontaneously rebooting + during startup. Examples of machines that need this are the + Dell PowerEdge 6300 machines. + + "aic7xxx=seltime:2" - This option controls how long the card waits + during a device selection sequence for the device to respond. + The original SCSI spec says that this "should be" 256ms. This + is generally not required with modern devices. However, some + very old SCSI I devices need the full 256ms. Most modern devices + can run fine with only 64ms. The default for this option is + 64ms. If you need to change this option, then use the following + table to set the proper value in the example above: + 0 - 256ms + 1 - 128ms + 2 - 64ms + 3 - 32ms + + "aic7xxx=panic_on_abort" - This option is for debugging and will cause + the driver to panic the linux kernel and freeze the system the first + time the drivers abort or reset routines are called. This is most + helpful when some problem causes infinite reset loops that scroll too + fast to see. By using this option, you can write down what the errors + actually are and send that information to me so it can be fixed. + + "aic7xxx=dump_card" - This option will print out the *entire* set of + configuration registers on the card during the init sequence. This + is a debugging aid used to see exactly what state the card is in + when we finally finish our initialization routines. If you don't + have documentation on the chipsets, this will do you absolutely + no good unless you are simply trying to write all the information + down in order to send it to me. + + "aic7xxx=dump_sequencer" - This is the same as the above options except + that instead of dumping the register contents on the card, this + option dumps the contents of the sequencer program RAM. This gives + the ability to verify that the instructions downloaded to the + card's sequencer are indeed what they are suppossed to be. Again, + unless you have documentation to tell you how to interpret these + numbers, then it is totally useless. + + "aic7xxx=override_term:0xffffffff" - This option is used to force the + termination on your SCSI controllers to a particular setting. This + is a bit mask variable that applies for up to 8 aic7xxx SCSI channels. + Each channel gets 4 bits, divided as follows: + bit 3 2 1 0 + | | | Enable/Disable Single Ended Low Byte Termination + | | En/Disable Single Ended High Byte Termination + | En/Disable Low Byte LVD Termination + En/Disable High Byte LVD Termination + + The upper 2 bits that deal with LVD termination only apply to Ultra2 + controllers. Futhermore, due to the current Ultra2 controller + designs, these bits are tied together such that setting either bit + enables both low and high byte LVD termination. It is not possible + to only set high or low byte LVD termination in this manner. This is + an artifact of the BIOS definition on Ultra2 controllers. For other + controllers, the only important bits are the two lowest bits. Setting + the higher bits on non-Ultra2 controllers has no effect. A few + examples of how to use this option: + + Enable low and high byte termination on a non-ultra2 controller that + is the first aic7xxx controller (the correct bits are 0011), + aic7xxx=override_term:0x3 + + Enable all termination on the third aic7xxx controller, high byte + termination on the second aic7xxx controller, and low and high byte + SE termination on the first aic7xxx controller + (bits are 1111 0010 0011), + aic7xxx=override_term:0xf23 + + No attempt has been made to make this option non-cryptic. It really + shouldn't be used except in dire circumstances, and if that happens, + I'm probably going to be telling you what to set this to anyway :) + + "aic7xxx=stpwlev:0xffffffff" - This option is used to control the STPWLEV + bit in the DEVCONFIG PCI register. Currently, this is one of the + very few registers that we have absolutely *no* way of detecting + what the variable should be. It depends entirely on how the chipset + and external terminators were coupled by the card/motherboard maker. + Further, a chip reset (at power up) always sets this bit to 0. If + there is no BIOS to run on the chipset/card (such as with a 2910C + or a motherboard controller with the BIOS totally disabled) then + the variable may not get set properly. Of course, if the proper + setting was 0, then that's what it would be after the reset, but if + the proper setting is actually 1.....you get the picture. Now, since + we can't detect this at all, I've added this option to force the + setting. If you have a BIOS on your controller then you should never + need to use this option. However, if you are having lots of SCSI + reset problems and can't seem to get them knocked out, this may help. + + Here's a test to know for certain if you need this option. Make + a boot floppy that you can use to boot your computer up and that + will detect the aic7xxx controller. Next, power down your computer. + While it's down, unplug all SCSI cables from your Adaptec SCSI + controller. Boot the system back up to the Adaptec EZ-SCSI BIOS + and then make sure that termination is enabled on your adapter (if + you have an Adaptec BIOS of course). Next, boot up the floppy you + made and wait for it to detect the aic7xxx controller. If the kernel + finds the controller fine, says scsi : x hosts and then tries to + detect your devices like normal, up to the point where it fails to + mount your root file system and panics, then you're fine. If, on + the other hand, the system goes into an infinite reset loop, then + you need to use this option and/or the previous option to force the + proper termination settings on your controller. If this happens, + then you next need to figure out what your settings should be. + + To find the correct settings, power your machine back down, connect + back up the SCSI cables, and boot back into your machine like normal. + However, boot with the aic7xxx=verbose:0x39 option. Record the + initial DEVCONFIG values for each of your aic7xxx controllers as + they are listed, and also record what the machine is detecting as + the proper termination on your controllers. NOTE: the order in + which the initial DEVCONFIG values are printed out is not gauranteed + to be the same order as the SCSI controllers are registered. The + above option and this option both work on the order of the SCSI + controllers as they are registered, so make sure you match the right + DEVCONFIG values with the right controllers if you have more than + one aic7xxx controller. + + Once you have the detected termination settings and the initial + DEVCONFIG values for each controller, then figure out what the + termination on each of the controllers *should* be. Hopefully, that + part is correct, but it could possibly be wrong if there is + bogus cable detection logic on your controller or something similar. + If all the controllers have the correct termination settings, then + don't set the aic7xxx=override_term variable at all, leave it alone. + Next, on any controllers that go into an infinite reset loop when + you unplug all the SCSI cables, get the starting DEVCONFIG value. + If the initial DEVCONFIG value is divisible by 2, then the correct + setting for that controller is 0. If it's an odd number, then + the correct setting for that controller is 1. For any other + controllers that didn't have an infinite reset problem, then reverse + the above options. If DEVCONFIG was even, then the correct setting + is 1, if not then the correct setting is 0. + + Now that you know what the correct setting was for each controller, + we need to encode that into the aic7xxx=stpwlev:0x... variable. + This variable is a bit field encoded variable. Bit 0 is for the first + aic7xxx controller, bit 1 for the next, etc. Put all these bits + together and you get a number. For example, if the third aic7xxx + needed a 1, but the second and first both needed a 0, then the bits + would be 100 in binary. This then translates to 0x04. You would + therefore set aic7xxx=stpwlev:0x04. This is fairly standard binary + to hexadecimal conversions here. If you aren't up to speed on the + binary->hex conversion then send an email to the aic7xxx mailing + list and someone can help you out. + + "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to disable + or enable Tagged Command Queueing (TCQ) on specific devices. As of + driver version 5.1.11, TCQ is now either on or off by default + according to the setting you choose during the make config process. + In order to en/disable TCQ for certian devices at boot time, a user + may use this boot param. The driver will then parse this message out + and en/disable the specific device entries that are present based upon + the value given. The param line is parsed in the following manner: + + { - first instance indicates the start of this parameter values + second instance is the start of entries for a particular + device entry + } - end the entries for a particular host adapter, or end the entire + set of parameter entries + , - move to next entry. Inside of a set of device entries, this + moves us to the next device on the list. Outside of device + entries, this moves us to the next host adapter + . - Same effect as , but is safe to use with insmod. + x - the number to enter into the array at this position. + 0 = Enable tagged queueing on this device and use the default + queue depth + 1-254 = Enable tagged queueing on this device and use this + number as the queue depth + 255 = Disable tagged queueing on this device. + Note: anything above 32 for an actual queue depth is wasteful + and not recommended. + + A few examples of how this can be used: + + tag_info:{{8,12,,0,,255,4}} + This line will only effect the first aic7xxx card registered. It + will set scsi id 0 to a queue depth of 8, id 1 to 12, leave id 2 + at the default, set id 3 to tagged queueing enabled and use the + default queue depth, id 4 default, id 5 disabled, and id 6 to 4. + Any not specified entries stay at the default value, repeated + commas with no value specified will simply increment to the next id + without changing anything for the missing values. + + tag_info:{,,,{,,,255}} + First, second, and third adapters at default values. Fourth + adapter, id 3 is disabled. Notice that leading commas simply + increment what the first number effects, and there are no need + for trailing commas. When you close out an adapter, or the + entire entry, anything not explicitly set stays at the default + value. + + A final note on this option. The scanner I used for this isn't + perfect or highly robust. If you mess the line up, the worst that + should happen is that the line will get ignored. If you don't + close out the entire entry with the final bracket, then any other + aic7xxx options after this will get ignored. So, in general, be + sure of what you are entering, and after you have it right, just + add it to the lilo.conf file so there won't be any mistakes. As + a means of checking this parser, the entire tag_info array for + each card is now printed out in the /proc/scsi/aic7xxx/x file. You + can use that to verify that your options were parsed correctly. + + Boot command line options may be combined to form the proper set of options + a user might need. For example, the following is valid: + + aic7xxx=verbose,extended,irq_trigger:1 + + The only requirement is that individual options be separated by a comma or + a period on the command line. + + Module Loading command options + ------------------------------ + When loading the aic7xxx driver as a module, the exact same options are + available to the user. However, the syntax to specify the options changes + slightly. For insmod, you need to wrap the aic7xxx= argument in quotes + and replace all ',' with '.'. So, for example, a valid insmod line + would be: + + insmod aic7xxx aic7xxx='verbose.irq_trigger:1.extended' + + This line should result in the *exact* same behaviour as if you typed + it in at the lilo prompt and the driver was compiled into the kernel + instead of being a module. The reason for the single quote is so that + the shell won't try to interpret anything in the line, such as {. + Insmod assumes any options starting with a letter instead of a number + is a character string (which is what we want) and by switching all of + the commas to periods, insmod won't interpret this as more than one + string and write junk into our binary image. I consider it a bug in + the insmod program that even if you wrap your string in quotes (quotes + that pass the shell mind you and that insmod sees) it still treates + a comma inside of those quotes as starting a new variable, resulting + in memory scribbles if you don't switch the commas to periods. + + + Kernel Compile options + ------------------------------ + The various kernel compile time options for this driver are now fairly + well documented in the file Documentation/Configure.help. In order to + see this documentation, you need to use one of the advanced configuration + programs (menuconfig and xconfig). If you are using the "make menuconfig" + method of configuring your kernel, then you would simply highlight the + option in question and hit the ? key. If you are using the "make xconfig" + method of configuring your kernel, then simply click on the help button + next to the option you have questions about. The help information from + the Configure.help file will then get automatically displayed. + + /proc support + ------------------------------ + The /proc support for the AIC7xxx can be found in the /proc/scsi/aic7xxx/ + directory. That directory contains a file for each SCSI controller in + the system. Each file presents the current configuration and transfer + statistics (enabled with #define in aic7xxx.c) for each controller. + + Thanks to Michael Neuffer for his upper-level SCSI help, and + Matthew Jacob for statistics support. + + Debugging the driver + ------------------------------ + Should you have problems with this driver, and would like some help in + getting them solved, there are a couple debugging items built into + the driver to facilitate getting the needed information from the system. + In general, I need a complete description of the problem, with as many + logs as possible concerning what happens. To help with this, there is + a command option aic7xxx=panic_on_abort. This option, when set, forces + the driver to panic the kernel on the first SCSI abort issued by the + mid level SCSI code. If your system is going to reset loops and you + can't read the screen, then this is what you need. Not only will it + stop the system, but it also prints out a large amount of state + information in the process. Second, if you specify the option + "aic7xxx=verbose:0x1ffff", the system will print out *SOOOO* much + information as it runs that you won't be able to see anything. + However, this can actually be very useful if your machine simply + locks up when trying to boot, since it will pin-point what was last + happening (in regards to the aic7xxx driver) immediately prior to + the lockup. This is really only useful if your machine simply can + not boot up successfully. If you can get your machine to run, then + this will produce far too much information. + + FTP sites + ------------------------------ + ftp://ftp.redhat.com/pub/aic/ + - Out of date. I used to keep stuff here, but too many people + complained about having a hard time getting into Red Hat's ftp + server. So use the web site below instead. + ftp://ftp.pcnet.com/users/eischen/Linux/ + - Dan Eischen's driver distribution area + ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/ + - European Linux mirror of Teleport site + + Web sites + ------------------------------ + http://people.redhat.com/dledford/ + - My web site, also the primary aic7xxx site with several related + pages. + +Dean W. Gehnert +deang@teleport.com + +$Revision: 3.0 $ + +Modified by Doug Ledford 1998-2000 + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/README.ncr53c8xx linux.ac/drivers/scsi/README.ncr53c8xx --- linux.vanilla/drivers/scsi/README.ncr53c8xx Tue Jun 20 01:59:41 2000 +++ linux.ac/drivers/scsi/README.ncr53c8xx Tue Apr 3 17:54:59 2001 @@ -508,7 +508,7 @@ clearprof The profile counters are automatically cleared when the amount of - data transfered reaches 1000 GB in order to avoid overflow. + data transferred reaches 1000 GB in order to avoid overflow. The "clearprof" command allows you to clear these counters at any time. @@ -1042,7 +1042,7 @@ The use of IARB needs the SCSI_NCR_IARB_SUPPORT option to have been defined at compile time and the 'iarb' boot option to have been set to a non zero -value at boot time. It is not that usefull for real work, but can be used +value at boot time. It is not that useful for real work, but can be used to stress SCSI devices or for some applications that can gain advantage of it. By the way, if you experience badnesses like 'unexpected disconnections', 'bad reselections', etc... when using IARB on heavy IO load, you should not @@ -1350,7 +1350,7 @@ Bit 0x04 : UDC Undexpected Disconnection Indicates that the device released the SCSI BUS when the chip was not expecting this to happen. A device may behave so to - indicate the SCSI initiator that an error condition not reportable using the SCSI protocol has occured. + indicate the SCSI initiator that an error condition not reportable using the SCSI protocol has occurred. Bit 0x02 : RST SCSI BUS Reset Generally SCSI targets donnot reset the SCSI BUS, although any device on the BUS can reset it at any time. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c linux.ac/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c --- linux.vanilla/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c Sat May 26 16:53:12 2001 +++ linux.ac/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c Tue May 22 18:41:07 2001 @@ -42,9 +42,13 @@ static int ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +#include + static void ahc_linux_pci_dev_remove(struct pci_dev *pdev); -/* We do our own ID filtering. So, grab all SCSI storage class devices. */ +/* We do our own ID filtering. So we grab all Adaptec SCSI storage class + * devices here. + */ static struct pci_device_id ahc_linux_pci_id_table[] = { { 0x9004, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, @@ -56,6 +60,7 @@ }, { 0 } }; +MODULE_DEVICE_TABLE(pci,ahc_linux_pci_id_table); struct pci_driver aic7xxx_pci_driver = { name: "aic7xxx", diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aic7xxx_old/README.aic7xxx linux.ac/drivers/scsi/aic7xxx_old/README.aic7xxx --- linux.vanilla/drivers/scsi/aic7xxx_old/README.aic7xxx Tue Apr 3 17:32:19 2001 +++ linux.ac/drivers/scsi/aic7xxx_old/README.aic7xxx Tue Apr 3 17:55:02 2001 @@ -478,10 +478,10 @@ information in the process. Second, if you specify the option "aic7xxx=verbose:0x1ffff", the system will print out *SOOOO* much information as it runs that you won't be able to see anything. - However, this can actually be very usefull if your machine simply + However, this can actually be very useful if your machine simply locks up when trying to boot, since it will pin-point what was last happening (in regards to the aic7xxx driver) immediately prior to - the lockup. This is really only usefull if your machine simply can + the lockup. This is really only useful if your machine simply can not boot up successfully. If you can get your machine to run, then this will produce far too much information. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aic7xxx_old/aic7xxx.reg linux.ac/drivers/scsi/aic7xxx_old/aic7xxx.reg --- linux.vanilla/drivers/scsi/aic7xxx_old/aic7xxx.reg Mon Apr 30 15:13:24 2001 +++ linux.ac/drivers/scsi/aic7xxx_old/aic7xxx.reg Tue Apr 10 18:16:57 2001 @@ -703,7 +703,12 @@ * it that it can fill the * message buffer. */ - mask TRACEPOINT 0xb0|SEQINT + mask SEQ_SG_FIXUP 0xb0|SEQINT /* need help with fixing up + * the sg array pointer after + * a phasemis with no valid + * sg elements in the shadow + * pipeline. + */ mask TRACEPOINT2 0xc0|SEQINT mask MSGIN_PHASEMIS 0xd0|SEQINT /* * Target changed phase on us diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aic7xxx_old/aic7xxx.seq linux.ac/drivers/scsi/aic7xxx_old/aic7xxx.seq --- linux.vanilla/drivers/scsi/aic7xxx_old/aic7xxx.seq Mon Apr 30 15:13:24 2001 +++ linux.ac/drivers/scsi/aic7xxx_old/aic7xxx.seq Tue Apr 10 18:16:57 2001 @@ -332,12 +332,15 @@ /* clear target specific flags */ clr SEQ_FLAGS ret; + +data_phase_reinit: /* * If we re-enter the data phase after going through another phase, the * STCNT may have been cleared, so restore it from the residual field. + * On Ultra2, we have to put it into the HCNT field because we have to + * drop the data down into the shadow layer via the preload ability. */ -data_phase_reinit: - if ((p->features & AHC_ULTRA2) != 0) { + if ((p->features & AHC_ULTRA2) != 0) { bmov HADDR, SHADDR, 4; bmov HCNT, SCB_RESID_DCNT, 3; } @@ -349,27 +352,24 @@ mvi SCB_RESID_DCNT call bcopy_3; } jmp data_phase_loop; - p_data: - if ((p->features & AHC_ULTRA2) != 0) { + if ((p->features & AHC_ULTRA2) != 0) { mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN; } else { mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; } test LASTPHASE, IOI jnz . + 2; or DMAPARAMS, DIRECTION; - call assert; /* - * Ensure entering a data - * phase is okay - seen identify, etc. - */ + call assert; /* + * Ensure entering a data + * phase is okay - seen identify, etc. + */ if ((p->features & AHC_CMD_CHAN) != 0) { mvi CCSGADDR, CCSGADDR_MAX; } - test SEQ_FLAGS, DPHASE jnz data_phase_reinit; - - /* We have seen a data phase */ - or SEQ_FLAGS, DPHASE; + test SEQ_FLAGS, DPHASE jnz data_phase_reinit; + or SEQ_FLAGS, DPHASE; /* we've seen a data phase */ /* * Initialize the DMA address and counter from the SCB. * Also set SG_COUNT and SG_NEXT in memory since we cannot @@ -378,8 +378,10 @@ */ if ((p->features & AHC_CMD_CHAN) != 0) { bmov HADDR, SCB_DATAPTR, 7; - bmov STCNT, HCNT, 3; bmov SG_COUNT, SCB_SGCOUNT, 5; + if ((p->features & AHC_ULTRA2) == 0) { + bmov STCNT, HCNT, 3; + } } else { mvi DINDEX, HADDR; mvi SCB_DATAPTR call bcopy_7; @@ -387,9 +389,8 @@ mvi DINDEX, SG_COUNT; mvi SCB_SGCOUNT call bcopy_5; } - data_phase_loop: -/* Guard against overruns */ + /* Guard against overruns */ test SG_COUNT, 0xff jnz data_phase_inbounds; /* * Turn on 'Bit Bucket' mode, set the transfer count to @@ -399,66 +400,62 @@ */ or SXFRCTL1,BITBUCKET; and DMAPARAMS, ~(HDMAEN|SDMAEN); - if ((p->features & AHC_CMD_CHAN) != 0) { - if ((p->features & AHC_ULTRA2) != 0) { - bmov HCNT, ALLONES, 3; - } + if ((p->features & AHC_ULTRA2) != 0) { + bmov HCNT, ALLONES, 3; + } + if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { bmov STCNT, ALLONES, 3; - } else { + } + if ((p->features & AHC_CMD_CHAN) == 0) { mvi STCNT[0], 0xFF; mvi STCNT[1], 0xFF; mvi STCNT[2], 0xFF; } + data_phase_inbounds: /* If we are the last SG block, tell the hardware. */ - cmp SG_COUNT,0x01 jne data_phase_wideodd; - if ((p->features & AHC_ULTRA2) == 0) { - and DMAPARAMS, ~WIDEODD; + if ((p->features & AHC_ULTRA2) != 0) { + shl A, 2, SG_COUNT; + cmp SG_COUNT,0x01 jne data_phase_wideodd; + or A, LAST_SEG; } else { - mvi SG_CACHEPTR, LAST_SEG; + cmp SG_COUNT,0x01 jne data_phase_wideodd; + and DMAPARAMS, ~WIDEODD; } data_phase_wideodd: - if ((p->features & AHC_ULTRA2) != 0) { - mov SINDEX, ALLONES; - mov DFCNTRL, DMAPARAMS; - test SSTAT0, SDONE jnz .; -data_phase_dma_loop: - test SSTAT0, SDONE jnz data_phase_dma_done; - test SSTAT1,PHASEMIS jz data_phase_dma_loop; /* ie. underrun */ -data_phase_dma_phasemis: - test SSTAT0,SDONE jnz data_phase_dma_done; - clr SINDEX; /* Remember the phasemiss */ + if ((p->features & AHC_ULTRA2) != 0) { + mov SG_CACHEPTR, A; + mov DFCNTRL, DMAPARAMS; /* start the operation */ + test SXFRCTL1, BITBUCKET jnz data_phase_overrun; +u2_preload_wait: + test SSTAT1, PHASEMIS jnz u2_phasemis; + test DFSTATUS, PRELOAD_AVAIL jz u2_preload_wait; } else { mov DMAPARAMS call dma; - } - data_phase_dma_done: /* Go tell the host about any overruns */ - test SXFRCTL1,BITBUCKET jnz data_phase_overrun; + test SXFRCTL1,BITBUCKET jnz data_phase_overrun; /* Exit if we had an underrun. dma clears SINDEX in this case. */ - test SINDEX,0xff jz data_phase_finish; - + test SINDEX,0xff jz data_phase_finish; + } /* - * Advance the scatter-gather pointers if needed + * Advance the scatter-gather pointers */ sg_advance: - dec SG_COUNT; /* one less segment to go */ + if ((p->features & AHC_ULTRA2) != 0) { + cmp SG_COUNT, 0x01 je u2_data_phase_finish; + } else { + dec SG_COUNT; + test SG_COUNT, 0xff jz data_phase_finish; + } - test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? */ -/* - * Load a struct scatter and set up the data address and length. - * If the working value of the SG count is nonzero, then - * we need to load a new set of values. - * - * This, like all DMA's, assumes little-endian host data storage. - */ -sg_load: if ((p->features & AHC_CMD_CHAN) != 0) { + /* * Do we have any prefetch left??? */ - cmp CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail; + cmp CCSGADDR, CCSGADDR_MAX jne prefetch_avail; /* * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes. @@ -474,10 +471,12 @@ and CCSGCTL, ~CCSGEN; test CCSGCTL, CCSGEN jnz .; mvi CCSGCTL, CCSGRESET; -prefetched_segs_avail: +prefetch_avail: bmov HADDR, CCSGRAM, 8; if ((p->features & AHC_ULTRA2) == 0) { bmov STCNT, HCNT, 3; + } else { + dec SG_COUNT; } } else { mvi DINDEX, HADDR; @@ -491,30 +490,63 @@ call dma_finish; - /* - * Copy data from FIFO into SCB data pointer and data count. - * This assumes that the SG segments are of the form: - * struct ahc_dma_seg { - * u_int32_t addr; four bytes, little-endian order - * u_int32_t len; four bytes, little endian order - * }; - */ - mvi HADDR call dfdat_in_7; +/* + * Copy data from FIFO into SCB data pointer and data count. + * This assumes that the SG segments are of the form: + * struct ahc_dma_seg { + * u_int32_t addr; four bytes, little-endian order + * u_int32_t len; four bytes, little endian order + * }; + */ + mvi DINDEX, HADDR; + call dfdat_in_7; call set_stcnt_from_hcnt; } - /* Advance the SG pointer */ - clr A; /* add sizeof(struct scatter) */ + clr A; /* add sizeof(struct scatter) */ add SG_NEXT[0],SG_SIZEOF; adc SG_NEXT[1],A; - test SSTAT1, REQINIT jz .; - test SSTAT1,PHASEMIS jz data_phase_loop; + if ((p->features & AHC_ULTRA2) != 0) { + jmp data_phase_loop; + } else { + test SSTAT1, REQINIT jz .; + test SSTAT1,PHASEMIS jz data_phase_loop; + } + -/* This drops the last SG segment down to the shadow layer for us */ +/* + * We've loaded all of our segments into the preload layer. Now, we simply + * have to wait for it to finish or for us to get a phasemis. And, since + * we'll get a phasemis if we do finish, all we really need to do is wait + * for a phasemis then check if we did actually complete all the segments. + */ if ((p->features & AHC_ULTRA2) != 0) { - mov DFCNTRL, DMAPARAMS; - test SSTAT0, SDONE jnz .; +u2_data_phase_finish: + test SSTAT1, PHASEMIS jnz u2_phasemis; + test SG_CACHEPTR, LAST_SEG_DONE jz u2_data_phase_finish; + clr SG_COUNT; + test SSTAT1, REQINIT jz .; + test SSTAT1, PHASEMIS jz data_phase_loop; +u2_phasemis: + call ultra2_dmafinish; + test SG_CACHEPTR, LAST_SEG_DONE jnz data_phase_finish; + test SSTAT2, SHVALID jnz u2_fixup_residual; + mvi INTSTAT, SEQ_SG_FIXUP; + jmp data_phase_finish; +u2_fixup_residual: + shr ARG_1, 2, SG_CACHEPTR; +u2_phasemis_loop: + and A, 0x3f, SG_COUNT; + cmp ARG_1, A je data_phase_finish; +/* + * Subtract SG_SIZEOF from the SG_NEXT pointer and add 1 to the SG_COUNT + */ + clr A; + add SG_NEXT[0], -SG_SIZEOF; + adc SG_NEXT[1], 0xff; + inc SG_COUNT; + jmp u2_phasemis_loop; } data_phase_finish: @@ -523,64 +555,83 @@ * We use STCNT instead of HCNT, since it's a reflection of how many bytes * were transferred on the SCSI (as opposed to the host) bus. */ - if ((p->features & AHC_ULTRA2) != 0) { - call ultra2_dmafinish; - } - if ((p->features & AHC_ULTRA2) == 0) { - if ((p->features & AHC_CMD_CHAN) != 0) { - bmov SCB_RESID_DCNT, STCNT, 3; - mov SCB_RESID_SGCNT, SG_COUNT; - } else { - mov SCB_RESID_DCNT[0],STCNT[0]; - mov SCB_RESID_DCNT[1],STCNT[1]; - mov SCB_RESID_DCNT[2],STCNT[2]; - mov SCB_RESID_SGCNT, SG_COUNT; + if ((p->features & AHC_CMD_CHAN) != 0) { + bmov SCB_RESID_DCNT, STCNT, 3; + mov SCB_RESID_SGCNT, SG_COUNT; + if ((p->features & AHC_ULTRA2) != 0) { + or SXFRCTL0, CLRSTCNT|CLRCHN; } + } else { + mov SCB_RESID_DCNT[0],STCNT[0]; + mov SCB_RESID_DCNT[1],STCNT[1]; + mov SCB_RESID_DCNT[2],STCNT[2]; + mov SCB_RESID_SGCNT, SG_COUNT; } jmp ITloop; data_phase_overrun: - if ((p->features & AHC_ULTRA2) != 0) { - call ultra2_dmafinish; - } /* * Turn off BITBUCKET mode and notify the host */ + if ((p->features & AHC_ULTRA2) != 0) { +/* + * Wait for the target to quit transferring data on the SCSI bus + */ + test SSTAT1, PHASEMIS jz .; + call ultra2_dmafinish; + } and SXFRCTL1, ~BITBUCKET; mvi INTSTAT,DATA_OVERRUN; jmp ITloop; -ultra2_dmafinish: + + + +/* + * Actually turn off the DMA hardware, save our current position into the + * proper residual variables, wait for the next REQ signal, then jump to + * the ITloop. Jumping to the ITloop ensures that if we happen to get + * brought into the data phase again (or are still in it after our last + * segment) that we will properly signal an overrun to the kernel. + */ if ((p->features & AHC_ULTRA2) != 0) { +ultra2_dmafinish: test DFCNTRL, DIRECTION jnz ultra2_dmahalt; and DFCNTRL, ~SCSIEN; test DFCNTRL, SCSIEN jnz .; + if ((p->bugs & AHC_BUG_AUTOFLUSH) != 0) { + or DFCNTRL, FIFOFLUSH; + } ultra2_dmafifoflush: - or DFCNTRL, FIFOFLUSH; - test DFSTATUS, FIFOEMP jz . - 1; - /* - * hardware bug alert! This needless set of jumps is to - * protect against a FIFOEMP status bit glitch in the - * silicon. - */ - test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; - test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; - test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; - test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + if ((p->bugs & AHC_BUG_AUTOFLUSH) != 0) { + /* + * hardware bug alert! This needless set of jumps + * works around a glitch in the silicon. When the + * PCI DMA fifo goes empty, but there is still SCSI + * data to be flushed into the PCI DMA fifo (and from + * there on into main memory), the FIFOEMP bit will + * come on between the time when the PCI DMA buffer + * went empty and the next bit of data is copied from + * the SCSI fifo into the PCI fifo. It should only + * come on when both FIFOs (meaning the entire FIFO + * chain) are emtpy. Since it can take up to 4 cycles + * for new data to be copied from the SCSI fifo into + * the PCI fifo, testing for FIFOEMP status for 4 + * extra times gives the needed time for any + * remaining SCSI fifo data to be put in the PCI fifo + * before we declare it *truly* empty. + */ + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; + } test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; test DFSTATUS, MREQPEND jnz .; ultra2_dmahalt: - test SCSIOFFSET, 0x7f jnz ultra2_shutdown; -ultra2_await_nreq: - test SCSISIGI, REQI jz ultra2_shutdown; - test SSTAT1, (PHASEMIS|REQINIT) jz ultra2_await_nreq; -ultra2_shutdown: and DFCNTRL, ~(HDMAEN|SCSIEN); test DFCNTRL, (HDMAEN|SCSIEN) jnz .; - bmov SCB_RESID_DCNT, STCNT, 3; - mov SCB_RESID_SGCNT, SG_COUNT; - or SXFRCTL0, CLRSTCNT|CLRCHN; ret; } @@ -1021,6 +1072,7 @@ inb_last: mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ + mesgin_phasemis: /* * We expected to receive another byte, but the target changed phase @@ -1080,7 +1132,9 @@ * to drain the data fifo until there is space for the input * latch to drain and HDMAEN de-asserts. */ - mov NONE, DFDAT; + if ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0) { + mov NONE, DFDAT; + } test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; } return: @@ -1306,20 +1360,30 @@ cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .; jmp dma_scb_finish; dma_scb_tohost: - if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { + if ((p->features & AHC_ULTRA2) == 0) { mvi CCSCBCTL, CCSCBRESET; bmov CCSCBRAM, SCB_CONTROL, 32; or CCSCBCTL, CCSCBEN|CCSCBRESET; test CCSCBCTL, CCSCBDONE jz .; - } else { - mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET; - cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .; + } + if ((p->features & AHC_ULTRA2) != 0) { + if ((p->bugs & AHC_BUG_SCBCHAN_UPLOAD) != 0) { + mvi CCSCBCTL, CCARREN|CCSCBRESET; + cmp CCSCBCTL, ARRDONE|CCARREN jne .; + mvi CCHCNT, 32; + mvi CCSCBCTL, CCSCBEN|CCSCBRESET; + cmp CCSCBCTL, CCSCBDONE|CCSCBEN jne .; + } else { + mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET; + cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .; + } } dma_scb_finish: clr CCSCBCTL; test CCSCBCTL, CCARREN|CCSCBEN jnz .; ret; - } else { + } + if ((p->features & AHC_CMD_CHAN) == 0) { mvi DINDEX, HADDR; mvi HSCB_ADDR call set_32byte_addr; mvi HCNT[0], 32; @@ -1342,17 +1406,81 @@ mov DFDAT,SINDIR; cmp SINDEX, A jne copy_scb_tofifo_loop; or DFCNTRL, HDMAEN|FIFOFLUSH; + jmp dma_finish; dma_scb_fromhost: - call dma_finish; - /* If we were putting the SCB, we are done */ - test DMAPARAMS, DIRECTION jz return; - mvi SCB_CONTROL call dfdat_in_7; - call dfdat_in_7_continued; - call dfdat_in_7_continued; - jmp dfdat_in_7_continued; + mvi DINDEX, SCB_CONTROL; + if ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0) { + /* + * Set the A to -24. It it hits 0, then we let + * our code fall through to dfdat_in_8 to complete + * the last of the copy. + * + * Also, things happen 8 bytes at a time in this + * case, so we may need to drain the fifo at most + * 3 times to keep things flowing + */ + mvi A, -24; +dma_scb_hang_fifo: + /* Wait for the first bit of data to hit the fifo */ + test DFSTATUS, FIFOEMP jnz .; +dma_scb_hang_wait: + /* OK, now they've started to transfer into the fifo, + * so wait for them to stop trying to transfer any + * more data. + */ + test DFSTATUS, MREQPEND jnz .; + /* + * OK, they started, then they stopped, now see if they + * managed to complete the job before stopping. Try + * it multiple times to give the chip a few cycles to + * set the flag if it did complete. + */ + test DFSTATUS, HDONE jnz dma_scb_hang_dma_done; + test DFSTATUS, HDONE jnz dma_scb_hang_dma_done; + test DFSTATUS, HDONE jnz dma_scb_hang_dma_done; + /* + * Too bad, the chip didn't complete the DMA, but there + * aren't any more memory requests pending, so that + * means it stopped part way through and hung. That's + * our bug, so now we drain what data there is in the + * fifo in order to get things going again. + */ +dma_scb_hang_empty_fifo: + call dfdat_in_8; + add A, 8; + add SINDEX, A, HCNT; + /* + * If there are another 8 bytes of data waiting in the + * fifo, then the carry bit will be set as a result + * of the above add command (unless A is non-negative, + * in which case the carry bit won't be set). + */ + jc dma_scb_hang_empty_fifo; + /* + * We've emptied the fifo now, but we wouldn't have got + * here if the memory transfer hadn't stopped part way + * through, so go back up to the beginning of the + * loop and start over. When it succeeds in getting + * all the data down, HDONE will be set and we'll + * jump to the code just below here. + */ + jmp dma_scb_hang_fifo; +dma_scb_hang_dma_done: + and DFCNTRL, ~HDMAEN; + test DFCNTRL, HDMAEN jnz .; + call dfdat_in_8; + add A, 8; + cmp A, 8 jne . - 2; + ret; + } else { + call dma_finish; + call dfdat_in_8; + call dfdat_in_8; + call dfdat_in_8; + } +dfdat_in_8: + mov DINDIR,DFDAT; dfdat_in_7: - mov DINDEX,SINDEX; -dfdat_in_7_continued: mov DINDIR,DFDAT; mov DINDIR,DFDAT; mov DINDIR,DFDAT; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aic7xxx_old/aic7xxx_proc.c linux.ac/drivers/scsi/aic7xxx_old/aic7xxx_proc.c --- linux.vanilla/drivers/scsi/aic7xxx_old/aic7xxx_proc.c Tue Apr 3 17:32:19 2001 +++ linux.ac/drivers/scsi/aic7xxx_old/aic7xxx_proc.c Tue Apr 3 17:55:02 2001 @@ -162,7 +162,7 @@ size += sprintf(BLS, "%s", AIC7XXX_H_VERSION); size += sprintf(BLS, "\n"); size += sprintf(BLS, "Compile Options:\n"); -#ifdef CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT +#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT size += sprintf(BLS, " TCQ Enabled By Default : Enabled\n"); #else size += sprintf(BLS, " TCQ Enabled By Default : Disabled\n"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aic7xxx_old/aic7xxx_reg.h linux.ac/drivers/scsi/aic7xxx_old/aic7xxx_reg.h --- linux.vanilla/drivers/scsi/aic7xxx_old/aic7xxx_reg.h Tue Apr 3 17:32:19 2001 +++ linux.ac/drivers/scsi/aic7xxx_old/aic7xxx_reg.h Tue Apr 3 17:55:02 2001 @@ -188,8 +188,8 @@ #define BRDRW 0x04 #define BRDRW_ULTRA2 0x02 #define BRDCTL1 0x02 -#define BRDCTL0 0x01 #define BRDSTB_ULTRA2 0x01 +#define BRDCTL0 0x01 #define SEECTL 0x1e #define EXTARBACK 0x80 @@ -397,7 +397,7 @@ #define DATA_OVERRUN 0xe1 #define MSGIN_PHASEMIS 0xd1 #define TRACEPOINT2 0xc1 -#define TRACEPOINT 0xb1 +#define SEQ_SG_FIXUP 0xb1 #define AWAITING_MSG 0xa1 #define RESIDUAL 0x81 #define BAD_STATUS 0x71 @@ -465,8 +465,6 @@ #define TARGCRCENDEN 0x08 #define TARGCRCCNTEN 0x04 -#define QOUTCNT 0x9e - #define SCSIPHASE 0x9e #define SP_STATUS 0x20 #define SP_COMMAND 0x10 @@ -475,6 +473,8 @@ #define SP_DATA_IN 0x02 #define SP_DATA_OUT 0x01 +#define QOUTCNT 0x9e + #define SFUNCT 0x9f #define ALT_MODE 0x80 @@ -595,8 +595,8 @@ #define RD_DFTHRSH_63 0x03 #define RD_DFTHRSH_50 0x02 #define RD_DFTHRSH_25 0x01 -#define RD_DFTHRSH_MIN 0x00 #define WR_DFTHRSH_MIN 0x00 +#define RD_DFTHRSH_MIN 0x00 #define SG_CACHEPTR 0xfc #define SG_USER_DATA 0xfc @@ -604,18 +604,18 @@ #define LAST_SEG_DONE 0x01 -#define CMD_GROUP_CODE_SHIFT 0x05 -#define BUS_8_BIT 0x00 -#define QOUTFIFO_OFFSET 0x01 -#define CCSGRAM_MAXSEGS 0x10 #define CMD_GROUP2_BYTE_DELTA 0xfa #define MAX_OFFSET_8BIT 0x0f #define BUS_16_BIT 0x01 #define QINFIFO_OFFSET 0x02 #define CMD_GROUP5_BYTE_DELTA 0x0b +#define CMD_GROUP_CODE_SHIFT 0x05 #define MAX_OFFSET_ULTRA2 0x7f #define MAX_OFFSET_16BIT 0x08 +#define BUS_8_BIT 0x00 +#define QOUTFIFO_OFFSET 0x01 #define UNTAGGEDSCB_OFFSET 0x00 +#define CCSGRAM_MAXSEGS 0x10 #define SCB_LIST_NULL 0xff #define SG_SIZEOF 0x08 #define CMD_GROUP4_BYTE_DELTA 0x04 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aic7xxx_old/aic7xxx_seq.c linux.ac/drivers/scsi/aic7xxx_old/aic7xxx_seq.c --- linux.vanilla/drivers/scsi/aic7xxx_old/aic7xxx_seq.c Tue Apr 3 17:32:19 2001 +++ linux.ac/drivers/scsi/aic7xxx_old/aic7xxx_seq.c Tue Apr 3 17:55:02 2001 @@ -25,12 +25,12 @@ 0x00, 0x4d, 0x10, 0x70, 0x01, 0x4e, 0x9c, 0x18, 0xbf, 0x60, 0xc0, 0x08, - 0x00, 0x6a, 0x3e, 0x5c, + 0x00, 0x6a, 0x86, 0x5c, 0xff, 0x4e, 0xc8, 0x18, - 0x02, 0x6a, 0x54, 0x5b, + 0x02, 0x6a, 0x70, 0x5b, 0xff, 0x52, 0x20, 0x09, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0x52, 0xca, 0x5b, + 0x00, 0x52, 0xe6, 0x5b, 0x03, 0xb0, 0x52, 0x31, 0xff, 0xb0, 0x52, 0x09, 0xff, 0xb1, 0x54, 0x09, @@ -87,13 +87,13 @@ 0x08, 0x6a, 0x66, 0x58, 0x80, 0x6a, 0x68, 0x00, 0x80, 0x36, 0x6c, 0x00, - 0x00, 0x65, 0x9e, 0x5b, + 0x00, 0x65, 0xba, 0x5b, 0xff, 0x3d, 0xc8, 0x08, 0xbf, 0x64, 0xe2, 0x78, - 0x80, 0x64, 0xac, 0x71, - 0xa0, 0x64, 0xdc, 0x71, - 0xc0, 0x64, 0xd4, 0x71, - 0xe0, 0x64, 0x1c, 0x72, + 0x80, 0x64, 0xc8, 0x71, + 0xa0, 0x64, 0xf8, 0x71, + 0xc0, 0x64, 0xf0, 0x71, + 0xe0, 0x64, 0x38, 0x72, 0x01, 0x6a, 0x22, 0x01, 0x00, 0x65, 0xaa, 0x40, 0xf7, 0x11, 0x22, 0x08, @@ -113,24 +113,24 @@ 0x03, 0xa9, 0x18, 0x31, 0x03, 0xa9, 0x10, 0x30, 0x08, 0x6a, 0xcc, 0x00, - 0xa9, 0x6a, 0xb4, 0x5b, + 0xa9, 0x6a, 0xd0, 0x5b, 0x00, 0x65, 0x02, 0x41, 0xa8, 0x6a, 0x6a, 0x00, 0x79, 0x6a, 0x6a, 0x00, 0x40, 0x3d, 0xea, 0x68, 0x04, 0x35, 0x6a, 0x00, - 0x00, 0x65, 0x0e, 0x5b, + 0x00, 0x65, 0x2a, 0x5b, 0x80, 0x6a, 0xd4, 0x01, 0x10, 0x36, 0xd6, 0x68, 0x10, 0x36, 0x6c, 0x00, 0x07, 0xac, 0x10, 0x31, - 0x03, 0x8c, 0x10, 0x30, 0x05, 0xa3, 0x70, 0x30, + 0x03, 0x8c, 0x10, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0xac, 0x6a, 0xac, 0x5b, - 0x00, 0x65, 0xa6, 0x5b, + 0xac, 0x6a, 0xc8, 0x5b, + 0x00, 0x65, 0xc2, 0x5b, 0x38, 0x6a, 0xcc, 0x00, - 0xa3, 0x6a, 0xb0, 0x5b, + 0xa3, 0x6a, 0xcc, 0x5b, 0xff, 0x38, 0x12, 0x69, 0x80, 0x02, 0x04, 0x00, 0xe7, 0x35, 0x6a, 0x08, @@ -139,334 +139,348 @@ 0xff, 0x6a, 0x10, 0x00, 0xff, 0x6a, 0x12, 0x00, 0xff, 0x6a, 0x14, 0x00, - 0x01, 0x38, 0x18, 0x61, + 0x22, 0x38, 0xc8, 0x28, + 0x01, 0x38, 0x1c, 0x61, + 0x02, 0x64, 0xc8, 0x00, + 0x01, 0x38, 0x1c, 0x61, 0xbf, 0x35, 0x6a, 0x08, - 0x02, 0x6a, 0xf8, 0x01, - 0xff, 0x69, 0xca, 0x08, + 0xff, 0x64, 0xf8, 0x09, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x0b, 0x1c, 0x69, - 0x04, 0x0b, 0x28, 0x69, - 0x10, 0x0c, 0x1e, 0x79, - 0x04, 0x0b, 0x28, 0x69, - 0xff, 0x6a, 0xca, 0x08, - 0x00, 0x35, 0xee, 0x5a, - 0x80, 0x02, 0x7c, 0x69, - 0xff, 0x65, 0x6c, 0x79, + 0x80, 0x02, 0xa4, 0x69, + 0x10, 0x0c, 0x7a, 0x69, + 0x80, 0x94, 0x22, 0x79, + 0x00, 0x35, 0x0a, 0x5b, + 0x80, 0x02, 0xa4, 0x69, + 0xff, 0x65, 0x94, 0x79, + 0x01, 0x38, 0x70, 0x71, 0xff, 0x38, 0x70, 0x18, - 0xff, 0x38, 0x6c, 0x79, - 0x80, 0xea, 0x48, 0x61, + 0xff, 0x38, 0x94, 0x79, + 0x80, 0xea, 0x4a, 0x61, 0xef, 0x38, 0xc8, 0x18, 0x80, 0x6a, 0xc8, 0x00, - 0x00, 0x65, 0x3a, 0x49, + 0x00, 0x65, 0x3c, 0x49, 0x33, 0x38, 0xc8, 0x28, 0xff, 0x64, 0xd0, 0x09, 0x04, 0x39, 0xc0, 0x31, 0x09, 0x6a, 0xd6, 0x01, - 0x80, 0xeb, 0x40, 0x79, + 0x80, 0xeb, 0x42, 0x79, 0xf7, 0xeb, 0xd6, 0x09, - 0x08, 0xeb, 0x44, 0x69, + 0x08, 0xeb, 0x46, 0x69, 0x01, 0x6a, 0xd6, 0x01, 0x08, 0xe9, 0x10, 0x31, 0x03, 0x8c, 0x10, 0x30, + 0xff, 0x38, 0x70, 0x18, 0x88, 0x6a, 0xcc, 0x00, - 0x39, 0x6a, 0xb2, 0x5b, + 0x39, 0x6a, 0xce, 0x5b, 0x08, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x0d, 0x93, 0x26, 0x01, - 0x00, 0x65, 0x30, 0x5c, - 0x88, 0x6a, 0x20, 0x5c, - 0x00, 0x65, 0xa6, 0x5b, + 0x00, 0x65, 0x78, 0x5c, + 0x88, 0x6a, 0xcc, 0x00, + 0x00, 0x65, 0x6a, 0x5c, + 0x00, 0x65, 0xc2, 0x5b, 0xff, 0x6a, 0xc8, 0x08, 0x08, 0x39, 0x72, 0x18, 0x00, 0x3a, 0x74, 0x20, - 0x01, 0x0c, 0x64, 0x79, + 0x00, 0x65, 0x02, 0x41, + 0x01, 0x0c, 0x6c, 0x79, 0x10, 0x0c, 0x02, 0x79, - 0xff, 0x35, 0x26, 0x09, - 0x04, 0x0b, 0x6a, 0x69, - 0x00, 0x65, 0x84, 0x59, + 0x10, 0x0c, 0x7a, 0x69, + 0x01, 0xfc, 0x70, 0x79, + 0xff, 0x6a, 0x70, 0x08, + 0x01, 0x0c, 0x76, 0x79, + 0x10, 0x0c, 0x02, 0x79, + 0x00, 0x65, 0xae, 0x59, + 0x01, 0xfc, 0x94, 0x69, + 0x40, 0x0d, 0x84, 0x69, + 0xb1, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x94, 0x41, + 0x2e, 0xfc, 0xa2, 0x28, + 0x3f, 0x38, 0xc8, 0x08, + 0x00, 0x51, 0x94, 0x71, + 0xff, 0x6a, 0xc8, 0x08, + 0xf8, 0x39, 0x72, 0x18, + 0xff, 0x3a, 0x74, 0x20, + 0x01, 0x38, 0x70, 0x18, + 0x00, 0x65, 0x86, 0x41, 0x03, 0x08, 0x52, 0x31, 0xff, 0x38, 0x50, 0x09, + 0x12, 0x01, 0x02, 0x00, 0xff, 0x08, 0x52, 0x09, 0xff, 0x09, 0x54, 0x09, 0xff, 0x0a, 0x56, 0x09, 0xff, 0x38, 0x50, 0x09, 0x00, 0x65, 0xaa, 0x40, - 0x00, 0x65, 0x84, 0x59, + 0x10, 0x0c, 0xa4, 0x79, + 0x00, 0x65, 0xae, 0x59, 0x7f, 0x02, 0x04, 0x08, 0xe1, 0x6a, 0x22, 0x01, 0x00, 0x65, 0xaa, 0x40, - 0x04, 0x93, 0x9a, 0x69, + 0x04, 0x93, 0xc2, 0x69, 0xdf, 0x93, 0x26, 0x09, - 0x20, 0x93, 0x88, 0x69, + 0x20, 0x93, 0xb2, 0x69, 0x02, 0x93, 0x26, 0x01, - 0x01, 0x94, 0x8a, 0x79, - 0x01, 0x94, 0x8a, 0x79, - 0x01, 0x94, 0x8a, 0x79, - 0x01, 0x94, 0x8a, 0x79, - 0x01, 0x94, 0x8a, 0x79, - 0x01, 0x94, 0x8a, 0x79, - 0x10, 0x94, 0x98, 0x69, - 0x7f, 0x05, 0xa0, 0x69, - 0x02, 0x03, 0xa0, 0x79, - 0x11, 0x0c, 0x9c, 0x79, + 0x01, 0x94, 0xb6, 0x79, + 0x01, 0x94, 0xb6, 0x79, + 0x01, 0x94, 0xb6, 0x79, + 0x01, 0x94, 0xb6, 0x79, + 0x01, 0x94, 0xb6, 0x79, + 0x10, 0x94, 0xc0, 0x69, 0xd7, 0x93, 0x26, 0x09, - 0x28, 0x93, 0xa2, 0x69, - 0x03, 0x08, 0x52, 0x31, - 0xff, 0x38, 0x50, 0x09, - 0x12, 0x01, 0x02, 0x00, + 0x28, 0x93, 0xc4, 0x69, 0xff, 0x6a, 0xd4, 0x0c, - 0x00, 0x65, 0x0e, 0x5b, + 0x00, 0x65, 0x2a, 0x5b, 0x05, 0xb4, 0x10, 0x31, 0x02, 0x6a, 0x1a, 0x31, 0x03, 0x8c, 0x10, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0xb4, 0x6a, 0xb0, 0x5b, + 0xb4, 0x6a, 0xcc, 0x5b, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, - 0x00, 0x65, 0xa6, 0x5b, - 0x3d, 0x6a, 0xee, 0x5a, + 0x00, 0x65, 0xc2, 0x5b, + 0x3d, 0x6a, 0x0a, 0x5b, 0xac, 0x6a, 0x26, 0x01, - 0x04, 0x0b, 0xc2, 0x69, - 0x04, 0x0b, 0xc8, 0x69, - 0x10, 0x0c, 0xc4, 0x79, - 0x02, 0x03, 0xcc, 0x79, - 0x11, 0x0c, 0xc8, 0x79, + 0x04, 0x0b, 0xde, 0x69, + 0x04, 0x0b, 0xe4, 0x69, + 0x10, 0x0c, 0xe0, 0x79, + 0x02, 0x03, 0xe8, 0x79, + 0x11, 0x0c, 0xe4, 0x79, 0xd7, 0x93, 0x26, 0x09, - 0x28, 0x93, 0xce, 0x69, + 0x28, 0x93, 0xea, 0x69, 0x12, 0x01, 0x02, 0x00, 0x00, 0x65, 0xaa, 0x40, - 0x00, 0x65, 0x0e, 0x5b, + 0x00, 0x65, 0x2a, 0x5b, 0xff, 0x06, 0x44, 0x09, 0x00, 0x65, 0xaa, 0x40, 0x10, 0x3d, 0x06, 0x00, 0xff, 0x34, 0xca, 0x08, - 0x80, 0x65, 0x00, 0x62, + 0x80, 0x65, 0x1c, 0x62, 0x0f, 0xa1, 0xca, 0x08, 0x07, 0xa1, 0xca, 0x08, 0x40, 0xa0, 0xc8, 0x08, 0x00, 0x65, 0xca, 0x00, 0x80, 0x65, 0xca, 0x00, - 0x80, 0xa0, 0xf0, 0x79, + 0x80, 0xa0, 0x0c, 0x7a, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x02, 0x42, - 0x20, 0xa0, 0x08, 0x7a, + 0x00, 0x65, 0x1e, 0x42, + 0x20, 0xa0, 0x24, 0x7a, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x9e, 0x5b, - 0xa0, 0x3d, 0x10, 0x62, + 0x00, 0x65, 0xba, 0x5b, + 0xa0, 0x3d, 0x2c, 0x62, 0x23, 0xa0, 0x0c, 0x08, - 0x00, 0x65, 0x9e, 0x5b, - 0xa0, 0x3d, 0x10, 0x62, - 0x00, 0xb9, 0x08, 0x42, - 0xff, 0x65, 0x08, 0x62, + 0x00, 0x65, 0xba, 0x5b, + 0xa0, 0x3d, 0x2c, 0x62, + 0x00, 0xb9, 0x24, 0x42, + 0xff, 0x65, 0x24, 0x62, 0xa1, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, - 0x10, 0x51, 0x10, 0x72, + 0x10, 0x51, 0x2c, 0x72, 0x40, 0x6a, 0x18, 0x00, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x9e, 0x5b, - 0xa0, 0x3d, 0xda, 0x71, + 0x00, 0x65, 0xba, 0x5b, + 0xa0, 0x3d, 0xf6, 0x71, 0x40, 0x6a, 0x18, 0x00, 0xff, 0x34, 0xa6, 0x08, - 0x80, 0x34, 0x18, 0x62, + 0x80, 0x34, 0x34, 0x62, 0x7f, 0xa0, 0x40, 0x09, 0x08, 0x6a, 0x68, 0x00, 0x00, 0x65, 0xaa, 0x40, - 0x64, 0x6a, 0xe4, 0x5a, - 0x80, 0x64, 0x8e, 0x6a, - 0x04, 0x64, 0x70, 0x72, - 0x02, 0x64, 0x76, 0x72, - 0x00, 0x6a, 0x38, 0x72, - 0x03, 0x64, 0x8a, 0x72, - 0x01, 0x64, 0x6c, 0x72, - 0x07, 0x64, 0xcc, 0x72, - 0x08, 0x64, 0x34, 0x72, - 0x23, 0x64, 0xd0, 0x72, + 0x64, 0x6a, 0x00, 0x5b, + 0x80, 0x64, 0xaa, 0x6a, + 0x04, 0x64, 0x8c, 0x72, + 0x02, 0x64, 0x92, 0x72, + 0x00, 0x6a, 0x54, 0x72, + 0x03, 0x64, 0xa6, 0x72, + 0x01, 0x64, 0x88, 0x72, + 0x07, 0x64, 0xe8, 0x72, + 0x08, 0x64, 0x50, 0x72, + 0x23, 0x64, 0xec, 0x72, 0x11, 0x6a, 0x22, 0x01, - 0x07, 0x6a, 0xd6, 0x5a, + 0x07, 0x6a, 0xf2, 0x5a, 0xff, 0x06, 0xd4, 0x08, 0x00, 0x65, 0xaa, 0x40, - 0xff, 0xa8, 0x3c, 0x6a, - 0xff, 0xa2, 0x54, 0x7a, + 0xff, 0xa8, 0x58, 0x6a, + 0xff, 0xa2, 0x70, 0x7a, 0x01, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0xca, 0x5b, - 0xff, 0xa2, 0x54, 0x7a, + 0x00, 0xb9, 0xe6, 0x5b, + 0xff, 0xa2, 0x70, 0x7a, 0x71, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, - 0x40, 0x51, 0x54, 0x62, + 0x40, 0x51, 0x70, 0x62, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0xca, 0x5b, + 0x00, 0xb9, 0xe6, 0x5b, 0xff, 0x3e, 0x74, 0x09, 0xff, 0x90, 0x7c, 0x08, 0x00, 0x65, 0x4e, 0x58, 0x00, 0x65, 0xbc, 0x40, - 0x20, 0xa0, 0x5c, 0x6a, + 0x20, 0xa0, 0x78, 0x6a, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0x6a, 0x74, 0x5b, - 0xff, 0x6a, 0x8a, 0x5b, + 0x00, 0x6a, 0x90, 0x5b, + 0xff, 0x6a, 0xa6, 0x5b, 0xff, 0xf8, 0xc8, 0x08, 0xff, 0x4f, 0xc8, 0x08, - 0x01, 0x6a, 0x74, 0x5b, - 0x00, 0xb9, 0x8a, 0x5b, + 0x01, 0x6a, 0x90, 0x5b, + 0x00, 0xb9, 0xa6, 0x5b, 0x01, 0x4f, 0x9e, 0x18, 0x02, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x38, 0x5c, + 0x00, 0x65, 0x80, 0x5c, 0x00, 0x65, 0xbc, 0x40, 0x41, 0x6a, 0x22, 0x01, 0x00, 0x65, 0xaa, 0x40, 0x04, 0xa0, 0x40, 0x01, - 0x00, 0x65, 0x50, 0x5c, + 0x00, 0x65, 0x98, 0x5c, 0x00, 0x65, 0xbc, 0x40, - 0x10, 0x36, 0x34, 0x7a, + 0x10, 0x36, 0x50, 0x7a, 0x05, 0x38, 0x46, 0x31, 0x04, 0x14, 0x58, 0x31, 0x03, 0xa9, 0x60, 0x31, 0xa3, 0x6a, 0xcc, 0x00, - 0x38, 0x6a, 0xb0, 0x5b, + 0x38, 0x6a, 0xcc, 0x5b, 0xac, 0x6a, 0xcc, 0x00, - 0x14, 0x6a, 0xb2, 0x5b, - 0xa9, 0x6a, 0xb4, 0x5b, - 0x00, 0x65, 0x34, 0x42, + 0x14, 0x6a, 0xce, 0x5b, + 0xa9, 0x6a, 0xd0, 0x5b, + 0x00, 0x65, 0x50, 0x42, 0xef, 0x36, 0x6c, 0x08, - 0x00, 0x65, 0x34, 0x42, + 0x00, 0x65, 0x50, 0x42, 0x0f, 0x64, 0xc8, 0x08, 0x07, 0x64, 0xc8, 0x08, 0x00, 0x37, 0x6e, 0x00, 0xff, 0x6a, 0xa4, 0x00, - 0x00, 0x65, 0x44, 0x5b, - 0xff, 0x51, 0xa0, 0x72, - 0x20, 0x36, 0xaa, 0x7a, - 0x00, 0x90, 0x32, 0x5b, - 0x00, 0x65, 0xac, 0x42, + 0x00, 0x65, 0x60, 0x5b, + 0xff, 0x51, 0xbc, 0x72, + 0x20, 0x36, 0xc6, 0x7a, + 0x00, 0x90, 0x4e, 0x5b, + 0x00, 0x65, 0xc8, 0x42, 0xff, 0x06, 0xd4, 0x08, - 0x00, 0x65, 0x9e, 0x5b, - 0xe0, 0x3d, 0xc6, 0x62, - 0x20, 0x12, 0xc6, 0x62, - 0x51, 0x6a, 0xda, 0x5a, - 0x00, 0x65, 0x2c, 0x5b, + 0x00, 0x65, 0xba, 0x5b, + 0xe0, 0x3d, 0xe2, 0x62, + 0x20, 0x12, 0xe2, 0x62, + 0x51, 0x6a, 0xf6, 0x5a, + 0x00, 0x65, 0x48, 0x5b, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0xa1, 0xbe, 0x62, - 0x04, 0xa0, 0xbe, 0x7a, + 0x00, 0xa1, 0xda, 0x62, + 0x04, 0xa0, 0xda, 0x7a, 0xfb, 0xa0, 0x40, 0x09, 0x80, 0x36, 0x6c, 0x00, - 0x80, 0xa0, 0x34, 0x7a, + 0x80, 0xa0, 0x50, 0x7a, 0x7f, 0xa0, 0x40, 0x09, - 0xff, 0x6a, 0xd6, 0x5a, - 0x00, 0x65, 0x34, 0x42, - 0x04, 0xa0, 0xc4, 0x7a, - 0x00, 0x65, 0x50, 0x5c, - 0x00, 0x65, 0xc6, 0x42, - 0x00, 0x65, 0x38, 0x5c, + 0xff, 0x6a, 0xf2, 0x5a, + 0x00, 0x65, 0x50, 0x42, + 0x04, 0xa0, 0xe0, 0x7a, + 0x00, 0x65, 0x98, 0x5c, + 0x00, 0x65, 0xe2, 0x42, + 0x00, 0x65, 0x80, 0x5c, 0x31, 0x6a, 0x22, 0x01, - 0x0c, 0x6a, 0xd6, 0x5a, - 0x00, 0x65, 0x34, 0x42, + 0x0c, 0x6a, 0xf2, 0x5a, + 0x00, 0x65, 0x50, 0x42, 0x61, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x34, 0x42, - 0x51, 0x6a, 0xda, 0x5a, + 0x00, 0x65, 0x50, 0x42, + 0x51, 0x6a, 0xf6, 0x5a, 0x51, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x34, 0x42, + 0x00, 0x65, 0x50, 0x42, 0x10, 0x3d, 0x06, 0x00, 0xff, 0x65, 0x68, 0x0c, 0xff, 0x06, 0xd4, 0x08, - 0x01, 0x0c, 0xdc, 0x7a, - 0x04, 0x0c, 0xde, 0x6a, + 0x01, 0x0c, 0xf8, 0x7a, + 0x04, 0x0c, 0xfa, 0x6a, 0xe0, 0x03, 0x7a, 0x08, - 0xe0, 0x3d, 0xea, 0x62, + 0xe0, 0x3d, 0x06, 0x63, 0xff, 0x65, 0xcc, 0x08, 0xff, 0x12, 0xda, 0x0c, 0xff, 0x06, 0xd4, 0x0c, 0xd1, 0x6a, 0x22, 0x01, 0x00, 0x65, 0xaa, 0x40, 0xff, 0x65, 0x26, 0x09, - 0x01, 0x0b, 0xfe, 0x6a, - 0x10, 0x0c, 0xf0, 0x7a, - 0x04, 0x0b, 0xf8, 0x6a, + 0x01, 0x0b, 0x1a, 0x6b, + 0x10, 0x0c, 0x0c, 0x7b, + 0x04, 0x0b, 0x14, 0x6b, 0xff, 0x6a, 0xca, 0x08, - 0x04, 0x93, 0xfc, 0x6a, - 0x01, 0x94, 0xfa, 0x7a, - 0x10, 0x94, 0xfc, 0x6a, - 0x80, 0x3d, 0x02, 0x73, - 0x0f, 0x04, 0x06, 0x6b, - 0x02, 0x03, 0x06, 0x7b, - 0x11, 0x0c, 0x02, 0x7b, + 0x04, 0x93, 0x18, 0x6b, + 0x01, 0x94, 0x16, 0x7b, + 0x10, 0x94, 0x18, 0x6b, + 0x80, 0x3d, 0x1e, 0x73, + 0x0f, 0x04, 0x22, 0x6b, + 0x02, 0x03, 0x22, 0x7b, + 0x11, 0x0c, 0x1e, 0x7b, 0xc7, 0x93, 0x26, 0x09, 0xff, 0x99, 0xd4, 0x08, - 0x38, 0x93, 0x08, 0x6b, + 0x38, 0x93, 0x24, 0x6b, 0xff, 0x6a, 0xd4, 0x0c, - 0x80, 0x36, 0x0c, 0x6b, + 0x80, 0x36, 0x28, 0x6b, 0x21, 0x6a, 0x22, 0x05, 0xff, 0x65, 0x20, 0x09, - 0xff, 0x51, 0x1a, 0x63, + 0xff, 0x51, 0x36, 0x63, 0xff, 0x37, 0xc8, 0x08, - 0xa1, 0x6a, 0x26, 0x43, + 0xa1, 0x6a, 0x42, 0x43, 0xff, 0x51, 0xc8, 0x08, - 0xb9, 0x6a, 0x26, 0x43, + 0xb9, 0x6a, 0x42, 0x43, 0xff, 0x90, 0xa4, 0x08, - 0xff, 0xba, 0x2a, 0x73, + 0xff, 0xba, 0x46, 0x73, 0xff, 0xba, 0x20, 0x09, 0xff, 0x65, 0xca, 0x18, - 0x00, 0x6c, 0x1e, 0x63, + 0x00, 0x6c, 0x3a, 0x63, 0xff, 0x90, 0xca, 0x0c, 0xff, 0x6a, 0xca, 0x04, - 0x20, 0x36, 0x3e, 0x7b, - 0x00, 0x90, 0x12, 0x5b, - 0xff, 0x65, 0x3e, 0x73, - 0xff, 0x52, 0x3c, 0x73, + 0x20, 0x36, 0x5a, 0x7b, + 0x00, 0x90, 0x2e, 0x5b, + 0xff, 0x65, 0x5a, 0x73, + 0xff, 0x52, 0x58, 0x73, 0xff, 0xba, 0xcc, 0x08, 0xff, 0x52, 0x20, 0x09, 0xff, 0x66, 0x74, 0x09, 0xff, 0x65, 0x20, 0x0d, 0xff, 0xba, 0x7e, 0x0c, - 0x00, 0x6a, 0x3e, 0x5c, + 0x00, 0x6a, 0x86, 0x5c, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0x51, 0xca, 0x43, - 0xff, 0x3f, 0x98, 0x73, + 0x00, 0x51, 0xe6, 0x43, + 0xff, 0x3f, 0xb4, 0x73, 0xff, 0x6a, 0xa2, 0x00, - 0x00, 0x3f, 0x12, 0x5b, - 0xff, 0x65, 0x98, 0x73, + 0x00, 0x3f, 0x2e, 0x5b, + 0xff, 0x65, 0xb4, 0x73, 0x20, 0x36, 0x6c, 0x00, - 0x20, 0xa0, 0x52, 0x6b, + 0x20, 0xa0, 0x6e, 0x6b, 0xff, 0xb9, 0xa2, 0x0c, 0xff, 0x6a, 0xa2, 0x04, 0xff, 0x65, 0xa4, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0xbe, 0x5b, + 0x45, 0x6a, 0xda, 0x5b, 0x01, 0x6a, 0xd0, 0x01, 0x09, 0x6a, 0xd6, 0x01, - 0x80, 0xeb, 0x5e, 0x7b, + 0x80, 0xeb, 0x7a, 0x7b, 0x01, 0x6a, 0xd6, 0x01, 0x01, 0xe9, 0xa4, 0x34, 0x88, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0xbe, 0x5b, + 0x45, 0x6a, 0xda, 0x5b, 0x01, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x0d, 0x6a, 0x26, 0x01, - 0x00, 0x65, 0x30, 0x5c, + 0x00, 0x65, 0x78, 0x5c, 0xff, 0x99, 0xa4, 0x0c, 0xff, 0x65, 0xa4, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0xbe, 0x5b, + 0x45, 0x6a, 0xda, 0x5b, 0x01, 0x6a, 0xd0, 0x01, 0x01, 0x6a, 0xdc, 0x05, 0x88, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0xbe, 0x5b, + 0x45, 0x6a, 0xda, 0x5b, 0x01, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x01, 0x6a, 0x26, 0x05, 0x01, 0x65, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0x8e, 0x7b, + 0x80, 0xee, 0xaa, 0x7b, 0xff, 0x6a, 0xdc, 0x0d, 0xff, 0x65, 0x32, 0x09, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0x30, 0x44, + 0x00, 0x65, 0x78, 0x44, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0x6a, 0x54, 0x5b, + 0x00, 0x6a, 0x70, 0x5b, 0xff, 0x52, 0xa2, 0x0c, - 0x01, 0x0c, 0x9e, 0x7b, - 0x04, 0x0c, 0x9e, 0x6b, + 0x01, 0x0c, 0xba, 0x7b, + 0x04, 0x0c, 0xba, 0x6b, 0xe0, 0x03, 0x06, 0x08, 0xe0, 0x03, 0x7a, 0x0c, 0xff, 0x8c, 0x10, 0x08, @@ -489,29 +503,34 @@ 0x00, 0x6c, 0xda, 0x24, 0xff, 0x65, 0xc8, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x41, 0x6a, 0xba, 0x5b, + 0x41, 0x6a, 0xd6, 0x5b, 0xff, 0x90, 0xe2, 0x09, 0x20, 0x6a, 0xd0, 0x01, - 0x04, 0x35, 0xdc, 0x7b, + 0x04, 0x35, 0xf8, 0x7b, 0x1d, 0x6a, 0xdc, 0x01, - 0xdc, 0xee, 0xd8, 0x63, - 0x00, 0x65, 0xe8, 0x43, + 0xdc, 0xee, 0xf4, 0x63, + 0x00, 0x65, 0x0e, 0x44, 0x01, 0x6a, 0xdc, 0x01, 0x20, 0xa0, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0xe2, 0x7b, + 0x80, 0xee, 0xfe, 0x7b, + 0x11, 0x6a, 0xdc, 0x01, + 0x50, 0xee, 0x02, 0x64, + 0x20, 0x6a, 0xd0, 0x01, + 0x09, 0x6a, 0xdc, 0x01, + 0x88, 0xee, 0x08, 0x64, 0x19, 0x6a, 0xdc, 0x01, - 0xd8, 0xee, 0xe6, 0x63, + 0xd8, 0xee, 0x0c, 0x64, 0xff, 0x6a, 0xdc, 0x09, - 0x18, 0xee, 0xea, 0x6b, + 0x18, 0xee, 0x10, 0x6c, 0xff, 0x6a, 0xd4, 0x0c, 0x88, 0x6a, 0xcc, 0x00, - 0x41, 0x6a, 0xba, 0x5b, + 0x41, 0x6a, 0xd6, 0x5b, 0x20, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x35, 0x14, 0x6c, + 0x04, 0x35, 0x3c, 0x6c, 0xa0, 0x6a, 0xca, 0x00, 0x20, 0x65, 0xc8, 0x18, 0xff, 0x6c, 0x32, 0x09, @@ -522,15 +541,32 @@ 0xff, 0x6c, 0x32, 0x09, 0xff, 0x6c, 0x32, 0x09, 0xff, 0x6c, 0x32, 0x09, - 0x00, 0x65, 0x00, 0x64, + 0x00, 0x65, 0x26, 0x64, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0x30, 0x5c, - 0x04, 0x35, 0x0c, 0x7b, - 0xa0, 0x6a, 0x20, 0x5c, - 0x00, 0x65, 0x22, 0x5c, - 0x00, 0x65, 0x22, 0x5c, - 0x00, 0x65, 0x22, 0x44, - 0xff, 0x65, 0xcc, 0x08, + 0x00, 0x65, 0x78, 0x44, + 0xa0, 0x6a, 0xcc, 0x00, + 0xe8, 0x6a, 0xc8, 0x00, + 0x01, 0x94, 0x40, 0x6c, + 0x10, 0x94, 0x42, 0x6c, + 0x08, 0x94, 0x54, 0x6c, + 0x08, 0x94, 0x54, 0x6c, + 0x08, 0x94, 0x54, 0x6c, + 0x00, 0x65, 0x68, 0x5c, + 0x08, 0x64, 0xc8, 0x18, + 0x00, 0x8c, 0xca, 0x18, + 0x00, 0x65, 0x4a, 0x4c, + 0x00, 0x65, 0x40, 0x44, + 0xf7, 0x93, 0x26, 0x09, + 0x08, 0x93, 0x56, 0x6c, + 0x00, 0x65, 0x68, 0x5c, + 0x08, 0x64, 0xc8, 0x18, + 0x08, 0x64, 0x58, 0x64, + 0xff, 0x6a, 0xd4, 0x0c, + 0x00, 0x65, 0x78, 0x5c, + 0x00, 0x65, 0x68, 0x5c, + 0x00, 0x65, 0x68, 0x5c, + 0x00, 0x65, 0x68, 0x5c, + 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, @@ -538,19 +574,19 @@ 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x0c, - 0x08, 0x94, 0x30, 0x7c, + 0x08, 0x94, 0x78, 0x7c, 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x34, 0x6c, + 0x08, 0x93, 0x7c, 0x6c, 0xff, 0x6a, 0xd4, 0x0c, 0xff, 0x40, 0x74, 0x09, 0xff, 0x90, 0x80, 0x08, 0xff, 0x6a, 0x72, 0x05, - 0xff, 0x40, 0x4c, 0x64, - 0xff, 0x3f, 0x44, 0x64, + 0xff, 0x40, 0x94, 0x64, + 0xff, 0x3f, 0x8c, 0x64, 0xff, 0x6a, 0xca, 0x04, 0xff, 0x3f, 0x20, 0x09, 0x01, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0xca, 0x5b, + 0x00, 0xb9, 0xe6, 0x5b, 0xff, 0xba, 0x7e, 0x0c, 0xff, 0x40, 0x20, 0x09, 0xff, 0xba, 0x80, 0x0c, @@ -558,12 +594,36 @@ 0xff, 0x90, 0x7e, 0x0c, }; +static int aic7xxx_patch15_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch15_func(struct aic7xxx_host *p) +{ + return ((p->bugs & AHC_BUG_SCBCHAN_UPLOAD) != 0); +} + +static int aic7xxx_patch14_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch14_func(struct aic7xxx_host *p) +{ + return ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0); +} + +static int aic7xxx_patch13_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch13_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_WIDE) != 0); +} + static int aic7xxx_patch12_func(struct aic7xxx_host *p); static int aic7xxx_patch12_func(struct aic7xxx_host *p) { - return ((p->features & AHC_WIDE) != 0); + return ((p->bugs & AHC_BUG_AUTOFLUSH) != 0); } static int aic7xxx_patch11_func(struct aic7xxx_host *p); @@ -692,54 +752,66 @@ { aic7xxx_patch7_func, 113, 1, 2 }, { aic7xxx_patch0_func, 114, 1, 1 }, { aic7xxx_patch1_func, 118, 1, 1 }, - { aic7xxx_patch1_func, 121, 3, 2 }, + { aic7xxx_patch1_func, 121, 3, 3 }, + { aic7xxx_patch11_func, 123, 1, 1 }, { aic7xxx_patch0_func, 124, 5, 1 }, - { aic7xxx_patch1_func, 132, 2, 3 }, { aic7xxx_patch7_func, 132, 1, 1 }, - { aic7xxx_patch0_func, 134, 3, 1 }, - { aic7xxx_patch11_func, 138, 1, 2 }, - { aic7xxx_patch0_func, 139, 1, 1 }, - { aic7xxx_patch7_func, 140, 7, 2 }, - { aic7xxx_patch0_func, 147, 1, 1 }, - { aic7xxx_patch1_func, 152, 14, 3 }, - { aic7xxx_patch11_func, 165, 1, 1 }, - { aic7xxx_patch0_func, 166, 9, 1 }, - { aic7xxx_patch7_func, 180, 2, 1 }, - { aic7xxx_patch7_func, 182, 1, 1 }, - { aic7xxx_patch11_func, 183, 6, 3 }, - { aic7xxx_patch1_func, 183, 2, 2 }, - { aic7xxx_patch0_func, 185, 4, 1 }, - { aic7xxx_patch7_func, 190, 1, 1 }, - { aic7xxx_patch7_func, 194, 20, 1 }, - { aic7xxx_patch1_func, 215, 3, 3 }, - { aic7xxx_patch11_func, 217, 1, 1 }, - { aic7xxx_patch0_func, 218, 5, 1 }, - { aic7xxx_patch11_func, 223, 1, 2 }, - { aic7xxx_patch0_func, 224, 9, 1 }, - { aic7xxx_patch12_func, 240, 1, 2 }, - { aic7xxx_patch0_func, 241, 1, 1 }, - { aic7xxx_patch4_func, 302, 1, 2 }, - { aic7xxx_patch0_func, 303, 1, 1 }, - { aic7xxx_patch2_func, 306, 1, 1 }, - { aic7xxx_patch1_func, 316, 3, 2 }, - { aic7xxx_patch0_func, 319, 5, 1 }, - { aic7xxx_patch12_func, 327, 1, 2 }, - { aic7xxx_patch0_func, 328, 1, 1 }, - { aic7xxx_patch5_func, 333, 1, 1 }, - { aic7xxx_patch11_func, 375, 15, 1 }, - { aic7xxx_patch1_func, 427, 7, 2 }, - { aic7xxx_patch0_func, 434, 8, 1 }, - { aic7xxx_patch1_func, 443, 4, 2 }, - { aic7xxx_patch0_func, 447, 6, 1 }, - { aic7xxx_patch1_func, 453, 4, 2 }, - { aic7xxx_patch0_func, 457, 3, 1 }, - { aic7xxx_patch10_func, 467, 10, 1 }, - { aic7xxx_patch1_func, 486, 17, 4 }, - { aic7xxx_patch9_func, 494, 4, 2 }, - { aic7xxx_patch0_func, 498, 2, 1 }, - { aic7xxx_patch0_func, 503, 33, 1 }, - { aic7xxx_patch10_func, 536, 4, 1 }, - { aic7xxx_patch5_func, 540, 2, 1 }, - { aic7xxx_patch5_func, 543, 9, 1 }, + { aic7xxx_patch9_func, 133, 1, 1 }, + { aic7xxx_patch10_func, 134, 3, 1 }, + { aic7xxx_patch7_func, 137, 3, 2 }, + { aic7xxx_patch0_func, 140, 2, 1 }, + { aic7xxx_patch7_func, 142, 5, 2 }, + { aic7xxx_patch0_func, 147, 3, 1 }, + { aic7xxx_patch7_func, 150, 1, 2 }, + { aic7xxx_patch0_func, 151, 2, 1 }, + { aic7xxx_patch1_func, 153, 15, 4 }, + { aic7xxx_patch11_func, 166, 1, 2 }, + { aic7xxx_patch0_func, 167, 1, 1 }, + { aic7xxx_patch0_func, 168, 10, 1 }, + { aic7xxx_patch7_func, 181, 1, 2 }, + { aic7xxx_patch0_func, 182, 2, 1 }, + { aic7xxx_patch7_func, 184, 18, 1 }, + { aic7xxx_patch1_func, 202, 3, 3 }, + { aic7xxx_patch7_func, 204, 1, 1 }, + { aic7xxx_patch0_func, 205, 4, 1 }, + { aic7xxx_patch7_func, 210, 2, 1 }, + { aic7xxx_patch7_func, 215, 13, 3 }, + { aic7xxx_patch12_func, 218, 1, 1 }, + { aic7xxx_patch12_func, 219, 4, 1 }, + { aic7xxx_patch1_func, 229, 3, 3 }, + { aic7xxx_patch11_func, 231, 1, 1 }, + { aic7xxx_patch0_func, 232, 5, 1 }, + { aic7xxx_patch11_func, 237, 1, 2 }, + { aic7xxx_patch0_func, 238, 9, 1 }, + { aic7xxx_patch13_func, 254, 1, 2 }, + { aic7xxx_patch0_func, 255, 1, 1 }, + { aic7xxx_patch4_func, 316, 1, 2 }, + { aic7xxx_patch0_func, 317, 1, 1 }, + { aic7xxx_patch2_func, 320, 1, 1 }, + { aic7xxx_patch1_func, 330, 3, 2 }, + { aic7xxx_patch0_func, 333, 5, 1 }, + { aic7xxx_patch13_func, 341, 1, 2 }, + { aic7xxx_patch0_func, 342, 1, 1 }, + { aic7xxx_patch5_func, 347, 1, 1 }, + { aic7xxx_patch11_func, 389, 15, 2 }, + { aic7xxx_patch14_func, 402, 1, 1 }, + { aic7xxx_patch1_func, 441, 7, 2 }, + { aic7xxx_patch0_func, 448, 8, 1 }, + { aic7xxx_patch1_func, 457, 4, 2 }, + { aic7xxx_patch0_func, 461, 6, 1 }, + { aic7xxx_patch1_func, 467, 4, 2 }, + { aic7xxx_patch0_func, 471, 3, 1 }, + { aic7xxx_patch10_func, 481, 10, 1 }, + { aic7xxx_patch1_func, 500, 22, 5 }, + { aic7xxx_patch11_func, 508, 4, 1 }, + { aic7xxx_patch7_func, 512, 7, 3 }, + { aic7xxx_patch15_func, 512, 5, 2 }, + { aic7xxx_patch0_func, 517, 2, 1 }, + { aic7xxx_patch10_func, 522, 50, 3 }, + { aic7xxx_patch14_func, 543, 17, 2 }, + { aic7xxx_patch0_func, 560, 4, 1 }, + { aic7xxx_patch10_func, 572, 4, 1 }, + { aic7xxx_patch5_func, 576, 2, 1 }, + { aic7xxx_patch5_func, 579, 9, 1 }, }; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/aic7xxx_old.c linux.ac/drivers/scsi/aic7xxx_old.c --- linux.vanilla/drivers/scsi/aic7xxx_old.c Mon Apr 30 15:13:24 2001 +++ linux.ac/drivers/scsi/aic7xxx_old.c Tue May 22 12:26:13 2001 @@ -256,7 +256,7 @@ #include #include -#include /* for kmalloc() */ +#include /* for kmalloc() */ #include /* for CONFIG_PCI */ @@ -266,7 +266,7 @@ */ #define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a)) -#define AIC7XXX_C_VERSION "5.2.1" +#define AIC7XXX_C_VERSION "5.2.4" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -311,8 +311,8 @@ * You can try raising me if tagged queueing is enabled, or lowering * me if you only have 4 SCBs. */ -#ifdef CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE -#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE +#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE +#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE #else #define AIC7XXX_CMDS_PER_DEVICE 8 #endif @@ -323,7 +323,7 @@ * NOTE: Do NOT enable this when running on kernels version 1.2.x and below. * NOTE: This does affect performance since it has to maintain statistics. */ -#ifdef CONFIG_AIC7XXX_OLD_PROC_STATS +#ifdef CONFIG_AIC7XXX_PROC_STATS #define AIC7XXX_PROC_STATS #endif @@ -347,7 +347,7 @@ * Make a define that will tell the driver not to use tagged queueing * by default. */ -#ifdef CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT +#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT #define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\ 0, 0, 0, 0, 0, 0, 0, 0} #else @@ -706,13 +706,14 @@ /*28*/ unsigned int pad; /* * Unused by the kernel, but we require * the padding so that the array of - * hardware SCBs is alligned on 32 byte + * hardware SCBs is aligned on 32 byte * boundaries so the sequencer can index */ }; typedef enum { SCB_FREE = 0x0000, + SCB_DTR_SCB = 0x0001, SCB_WAITINGQ = 0x0002, SCB_ACTIVE = 0x0004, SCB_SENSE = 0x0008, @@ -830,6 +831,17 @@ unsigned int dma_len; /* DMA length */ }; +typedef enum { + AHC_BUG_NONE = 0x0000, + AHC_BUG_TMODE_WIDEODD = 0x0001, + AHC_BUG_AUTOFLUSH = 0x0002, + AHC_BUG_CACHETHEN = 0x0004, + AHC_BUG_CACHETHEN_DIS = 0x0008, + AHC_BUG_PCI_2_1_RETRY = 0x0010, + AHC_BUG_PCI_MWI = 0x0020, + AHC_BUG_SCBCHAN_UPLOAD = 0x0040, +} ahc_bugs; + struct aic7xxx_scb { struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */ Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */ @@ -942,7 +954,6 @@ unsigned long isr_count; /* Interrupt count */ unsigned long spurious_int; scb_data_type *scb_data; - volatile unsigned short needdv; volatile unsigned short needppr; volatile unsigned short needsdtr; volatile unsigned short needwdtr; @@ -973,10 +984,9 @@ #define BUS_DEVICE_RESET_PENDING 0x02 #define DEVICE_RESET_DELAY 0x04 #define DEVICE_PRINT_DTR 0x08 -#define DEVICE_PARITY_ERROR 0x10 -#define DEVICE_WAS_BUSY 0x20 -#define DEVICE_SCSI_3 0x40 -#define DEVICE_SCANNED 0x80 +#define DEVICE_WAS_BUSY 0x10 +#define DEVICE_SCSI_3 0x20 +#define DEVICE_DTR_SCANNED 0x40 volatile unsigned char dev_flags[MAX_TARGETS]; volatile unsigned char dev_active_cmds[MAX_TARGETS]; volatile unsigned char dev_temp_queue_depth[MAX_TARGETS]; @@ -989,10 +999,6 @@ spinlock_t spin_lock; volatile unsigned char cpu_lock_count[NR_CPUS]; - Scsi_Cmnd *dev_dtr_cmnd[MAX_TARGETS]; - - unsigned int dev_checksum[MAX_TARGETS]; - unsigned char dev_last_queue_full[MAX_TARGETS]; unsigned char dev_last_queue_full_count[MAX_TARGETS]; unsigned char dev_max_queue_depth[MAX_TARGETS]; @@ -1040,6 +1046,7 @@ int host_no; /* SCSI host number */ unsigned long mbase; /* I/O memory address */ ahc_chip chip; /* chip type */ + ahc_bugs bugs; dma_addr_t fifo_dma; /* DMA handle for fifo arrays */ /* @@ -2821,138 +2828,98 @@ { p->flags &= ~AHC_ABORT_PENDING; } - if (scb->flags & SCB_RESET) + if (scb->flags & (SCB_RESET|SCB_ABORT)) { - cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff); + cmd->result |= (DID_RESET << 16); } - else if (scb->flags & SCB_ABORT) - { - cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff); - } - else if (!(p->dev_flags[tindex] & DEVICE_SCANNED)) + + if (!(p->dev_flags[tindex] & DEVICE_PRESENT)) { if ( (cmd->cmnd[0] == INQUIRY) && (cmd->result == DID_OK) ) { - char *buffer; - + p->dev_flags[tindex] |= DEVICE_PRESENT; - if(cmd->use_sg) - { - struct scatterlist *sg; - - sg = (struct scatterlist *)cmd->request_buffer; - buffer = (char *)sg[0].address; - } - else - { - buffer = (char *)cmd->request_buffer; - } #define WIDE_INQUIRY_BITS 0x60 #define SYNC_INQUIRY_BITS 0x10 #define SCSI_VERSION_BITS 0x07 #define SCSI_DT_BIT 0x04 - if ( (buffer[7] & WIDE_INQUIRY_BITS) && - (p->features & AHC_WIDE) ) - { - p->needwdtr |= (1<needwdtr_copy |= (1<transinfo[tindex].goal_width = p->transinfo[tindex].user_width; - } - else - { - p->needwdtr &= ~(1<needwdtr_copy &= ~(1<target, cmd->channel, cmd->lun, - MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE | - AHC_TRANS_GOAL | - AHC_TRANS_CUR) ); - unpause_sequencer(p, FALSE); - } - if ( (buffer[7] & SYNC_INQUIRY_BITS) && - p->transinfo[tindex].user_offset ) - { - p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period; - p->transinfo[tindex].goal_options = p->transinfo[tindex].user_options; - if (p->features & AHC_ULTRA2) - p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; - else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT) - p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; + if(!(p->dev_flags[tindex] & DEVICE_DTR_SCANNED)) { + char *buffer; + + if(cmd->use_sg) + { + struct scatterlist *sg; + + sg = (struct scatterlist *)cmd->request_buffer; + buffer = (char *)sg[0].address; + } + else + { + buffer = (char *)cmd->request_buffer; + } + + + if ( (buffer[7] & WIDE_INQUIRY_BITS) && + (p->features & AHC_WIDE) ) + { + p->needwdtr |= (1<needwdtr_copy |= (1<transinfo[tindex].goal_width = p->transinfo[tindex].user_width; + } else - p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; - if ( (((buffer[2] & SCSI_VERSION_BITS) == 3) || - (buffer[56] & SCSI_DT_BIT) || - (p->dev_flags[tindex] & DEVICE_SCSI_3) ) && - (p->transinfo[tindex].user_period <= 9) && - (p->transinfo[tindex].user_options) ) { - p->needppr |= (1<needppr_copy |= (1<needsdtr &= ~(1<needsdtr_copy &= ~(1<needwdtr &= ~(1<needwdtr_copy &= ~(1<dev_flags[tindex] |= DEVICE_SCSI_3; + pause_sequencer(p); + aic7xxx_set_width(p, cmd->target, cmd->channel, cmd->lun, + MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE | + AHC_TRANS_GOAL | + AHC_TRANS_CUR) ); + unpause_sequencer(p, FALSE); } - else + if ( (buffer[7] & SYNC_INQUIRY_BITS) && + p->transinfo[tindex].user_offset ) { - p->needsdtr |= (1<needsdtr_copy |= (1<transinfo[tindex].goal_period = - MAX(10, p->transinfo[tindex].goal_period); - p->transinfo[tindex].goal_options = 0; + p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_options = p->transinfo[tindex].user_options; + if (p->features & AHC_ULTRA2) + p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; + else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT) + p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; + else + p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; + if ( (((buffer[2] & SCSI_VERSION_BITS) >= 3) || + (buffer[56] & SCSI_DT_BIT) || + (p->dev_flags[tindex] & DEVICE_SCSI_3) ) && + (p->transinfo[tindex].user_period <= 9) && + (p->transinfo[tindex].user_options) ) + { + p->needppr |= (1<needppr_copy |= (1<needsdtr &= ~(1<needsdtr_copy &= ~(1<needwdtr &= ~(1<needwdtr_copy &= ~(1<dev_flags[tindex] |= DEVICE_SCSI_3; + } + else + { + p->needsdtr |= (1<needsdtr_copy |= (1<transinfo[tindex].goal_period = + MAX(10, p->transinfo[tindex].goal_period); + p->transinfo[tindex].goal_options = 0; + } } - } - else - { - p->needsdtr &= ~(1<needsdtr_copy &= ~(1<transinfo[tindex].goal_period = 255; - p->transinfo[tindex].goal_offset = 0; - p->transinfo[tindex].goal_options = 0; - } - /* - * This is needed to work around a sequencer bug for now. Regardless - * of the controller in use, if we have a Quantum drive, we need to - * limit the speed to 80MByte/sec. As soon as I get a fixed version - * of the sequencer, this code will get yanked. - */ - if(!strncmp(buffer + 8, "QUANTUM", 7) && - p->transinfo[tindex].goal_options ) - { - p->transinfo[tindex].goal_period = - MAX(p->transinfo[tindex].goal_period, 10); - p->transinfo[tindex].goal_options = 0; - p->needppr &= ~(1<needppr_copy &= ~(1<needsdtr |= (1<needsdtr_copy |= (1<needwdtr |= (1<needwdtr_copy |= (1<dev_dtr_cmnd[tindex] == cmd) { - unsigned int checksum = 0; - int *ibuffer; - int i=0; - - ibuffer = (int *)buffer; - for( i = 0; i < (cmd->request_bufflen >> 2); i++) + else { - checksum += ibuffer[i]; + p->needsdtr &= ~(1<needsdtr_copy &= ~(1<transinfo[tindex].goal_period = 255; + p->transinfo[tindex].goal_offset = 0; + p->transinfo[tindex].goal_options = 0; } - p->dev_checksum[tindex] = checksum; - p->dev_flags[tindex] |= DEVICE_SCANNED; + p->dev_flags[tindex] |= DEVICE_DTR_SCANNED; p->dev_flags[tindex] |= DEVICE_PRINT_DTR; } #undef WIDE_INQUIRY_BITS @@ -2961,7 +2928,8 @@ #undef SCSI_DT_BIT } } - else if ((scb->flags & SCB_MSGOUT_BITS) != 0) + + if ((scb->flags & SCB_MSGOUT_BITS) != 0) { unsigned short mask; int message_error = FALSE; @@ -2981,7 +2949,6 @@ if (scb->flags & SCB_MSGOUT_WDTR) { - p->dtr_pending &= ~mask; if (message_error) { if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && @@ -3000,7 +2967,6 @@ } if (scb->flags & SCB_MSGOUT_SDTR) { - p->dtr_pending &= ~mask; if (message_error) { if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && @@ -3020,7 +2986,6 @@ } if (scb->flags & SCB_MSGOUT_PPR) { - p->dtr_pending &= ~mask; if(message_error) { if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && @@ -3045,6 +3010,7 @@ } } } + queue_depth = p->dev_temp_queue_depth[tindex]; if (queue_depth >= p->dev_active_cmds[tindex]) { @@ -3078,9 +3044,18 @@ } } } - if ( !(scb->tag_action) && (p->tagenable & (1<tag_action)) + { + aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, + /* unbusy */ TRUE); + if (p->tagenable & (1<dev_temp_queue_depth[tindex] = p->dev_max_queue_depth[tindex]; + } + } + if(scb->flags & SCB_DTR_SCB) { - p->dev_temp_queue_depth[tindex] = p->dev_max_queue_depth[tindex]; + p->dtr_pending &= ~(1 << tindex); } p->dev_active_cmds[tindex]--; p->activescbs--; @@ -3189,6 +3164,14 @@ printk(INFO_LEAD "Aborting scb %d\n", p->host_no, CTL_OF_SCB(scb), scb->hscb->tag); found++; + /* + * Clear any residual information since the normal aic7xxx_done() path + * doesn't touch the residuals. + */ + scb->hscb->residual_SG_segment_count = 0; + scb->hscb->residual_data_count[0] = 0; + scb->hscb->residual_data_count[1] = 0; + scb->hscb->residual_data_count[2] = 0; aic7xxx_done(p, scb); } } @@ -3401,8 +3384,22 @@ active_scb = aic_inb(p, SCBPTR); if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS)) + { printk(INFO_LEAD "Reset device, active_scb %d\n", p->host_no, channel, target, lun, active_scb); + printk(INFO_LEAD "Current scb_tag %d, SEQADDR 0x%x, LASTPHASE " + "0x%x\n", + p->host_no, channel, target, lun, aic_inb(p, SCB_TAG), + aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8), + aic_inb(p, LASTPHASE)); + printk(INFO_LEAD "SG_CACHEPTR 0x%x, SG_COUNT %d, SCSISIGI 0x%x\n", + p->host_no, channel, target, lun, + (p->features & AHC_ULTRA2) ? aic_inb(p, SG_CACHEPTR) : 0, + aic_inb(p, SG_COUNT), aic_inb(p, SCSISIGI)); + printk(INFO_LEAD "SSTAT0 0x%x, SSTAT1 0x%x, SSTAT2 0x%x\n", + p->host_no, channel, target, lun, aic_inb(p, SSTAT0), + aic_inb(p, SSTAT1), aic_inb(p, SSTAT2)); + } /* * Deal with the busy target and linked next issues. */ @@ -3446,11 +3443,11 @@ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS)) printk(INFO_LEAD "Cleaning up status information " "and delayed_scbs.\n", p->host_no, channel, i, lun); - p->dev_flags[i] &= ~(BUS_DEVICE_RESET_PENDING | DEVICE_PARITY_ERROR); + p->dev_flags[i] &= ~BUS_DEVICE_RESET_PENDING; if ( tag == SCB_LIST_NULL ) { p->dev_flags[i] |= DEVICE_PRINT_DTR | DEVICE_RESET_DELAY; - p->dev_expires[i] = jiffies + (4 * HZ); + p->dev_expires[i] = jiffies + (1 * HZ); p->dev_timer_active |= (0x01 << i); p->dev_last_queue_full_count[i] = 0; p->dev_last_queue_full[i] = 0; @@ -3495,7 +3492,7 @@ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE; } } - if ( j > (p->scb_data->maxscbs + 1) ) + if ( j > (p->scb_data->numscbs + 1) ) { if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET)) printk(WARN_LEAD "Yikes!! There's a loop in the " @@ -3554,7 +3551,7 @@ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE; } } - if ( j > (p->scb_data->maxscbs + 1) ) + if ( j > (p->scb_data->numscbs + 1) ) { if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET)) printk(WARN_LEAD "Yikes!! There's a loop in the " @@ -4300,11 +4297,25 @@ if (actual < cmd->underflow) { if (aic7xxx_verbose & VERBOSE_MINOR_ERROR) + { printk(INFO_LEAD "Underflow - Wanted %u, %s %u, residual SG " "count %d.\n", p->host_no, CTL_OF_SCB(scb), cmd->underflow, (cmd->request.cmd == WRITE) ? "wrote" : "read", actual, hscb->residual_SG_segment_count); + printk(INFO_LEAD "status 0x%x.\n", p->host_no, CTL_OF_SCB(scb), + hscb->target_status); + } + /* + * In 2.4, only send back the residual information, don't flag this + * as an error. Before 2.4 we had to flag this as an error because + * the mid layer didn't check residual data counts to see if the + * command needs retried. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + cmd->resid = scb->sg_length - actual; +#else aic7xxx_error(cmd) = DID_RETRY_COMMAND; +#endif aic7xxx_status(cmd) = hscb->target_status; } } @@ -4623,7 +4634,6 @@ */ p->needwdtr &= ~target_mask; p->needwdtr_copy &= ~target_mask; - p->dtr_pending &= ~target_mask; scb->flags &= ~SCB_MSGOUT_BITS; aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR)); @@ -4643,8 +4653,7 @@ */ p->needsdtr &= ~target_mask; p->needsdtr_copy &= ~target_mask; - p->dtr_pending &= ~target_mask; - scb->flags &= ~SCB_MSGOUT_SDTR; + scb->flags &= ~SCB_MSGOUT_BITS; aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0, (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL)); if(aic7xxx_verbose & VERBOSE_NEGOTIATION2) @@ -4778,6 +4787,8 @@ aic7xxx_error(cmd) = DID_OK; break; } /* first time sense, no errors */ + printk(INFO_LEAD "CHECK_CONDITION on REQUEST_SENSE, returning " + "an error.\n", p->host_no, CTL_OF_SCB(scb)); aic7xxx_error(cmd) = DID_ERROR; scb->flags &= ~SCB_SENSE; break; @@ -5136,12 +5147,21 @@ printk(KERN_WARNING " %s seen Data Phase. Length=%d, NumSGs=%d.\n", (aic_inb(p, SEQ_FLAGS) & DPHASE) ? "Have" : "Haven't", scb->sg_length, scb->sg_count); - for (i = 0; i < scb->sg_count; i++) + printk(KERN_WARNING " Raw SCSI Command: 0x"); + for (i = 0; i < scb->hscb->SCSI_cmd_length; i++) + { + printk("%02x ", scb->cmd->cmnd[i]); + } + printk("\n"); + if(aic7xxx_verbose > 0xffff) { - printk(KERN_WARNING " sg[%d] - Addr 0x%x : Length %d\n", + for (i = 0; i < scb->sg_count; i++) + { + printk(KERN_WARNING " sg[%d] - Addr 0x%x : Length %d\n", i, le32_to_cpu(scb->sg_list[i].address), le32_to_cpu(scb->sg_list[i].length) ); + } } aic7xxx_error(scb->cmd) = DID_ERROR; } @@ -5156,7 +5176,7 @@ unsigned char resid_sgcnt, index; unsigned char scb_index = aic_inb(p, SCB_TAG); unsigned int cur_addr, resid_dcnt; - unsigned int native_addr, native_length; + unsigned int native_addr, native_length, sg_addr; int i; if(scb_index > p->scb_data->numscbs) @@ -5176,6 +5196,9 @@ scb->flags, (unsigned long)scb->cmd); break; } + if(aic7xxx_verbose & VERBOSE_MINOR_ERROR) + printk(INFO_LEAD "Got WIDE_RESIDUE message, patching up data " + "pointer.\n", p->host_no, CTL_OF_SCB(scb)); /* * We have a valid scb to use on this WIDE_RESIDUE message, so @@ -5188,132 +5211,87 @@ */ cur_addr = aic_inb(p, SHADDR) | (aic_inb(p, SHADDR + 1) << 8) | (aic_inb(p, SHADDR + 2) << 16) | (aic_inb(p, SHADDR + 3) << 24); + sg_addr = aic_inb(p, SG_COUNT + 1) | (aic_inb(p, SG_COUNT + 2) << 8) | + (aic_inb(p, SG_COUNT + 3) << 16) | (aic_inb(p, SG_COUNT + 4) << 24); resid_sgcnt = aic_inb(p, SCB_RESID_SGCNT); resid_dcnt = aic_inb(p, SCB_RESID_DCNT) | (aic_inb(p, SCB_RESID_DCNT + 1) << 8) | (aic_inb(p, SCB_RESID_DCNT + 2) << 16); - index = scb->sg_count - (resid_sgcnt + 1); + index = scb->sg_count - ((resid_sgcnt) ? resid_sgcnt : 1); native_addr = le32_to_cpu(scb->sg_list[index].address); native_length = le32_to_cpu(scb->sg_list[index].length); /* - * Make sure this is a valid sg_seg for the given pointer + * If resid_dcnt == native_length, then we just loaded this SG + * segment and we need to back it up one... */ - if(cur_addr < native_addr || - cur_addr > (native_addr + native_length + 1)) - { - printk(WARN_LEAD "invalid cur_addr:0x%x during WIDE_RESIDUE\n", - p->host_no, CTL_OF_SCB(scb), cur_addr); - if(index > 0) - printk(WARN_LEAD " sg_address[-1]:0x%x sg_length[-1]:%d\n", - p->host_no, CTL_OF_SCB(scb), - le32_to_cpu(scb->sg_list[index - 1].address), - le32_to_cpu(scb->sg_list[index - 1].length)); - printk(WARN_LEAD " sg_address:0x%x sg_length:%d\n", - p->host_no, CTL_OF_SCB(scb), - native_addr, native_length); - if(resid_sgcnt > 1) - printk(WARN_LEAD " sg_address[1]:0x%x sg_length[1]:%d\n", - p->host_no, CTL_OF_SCB(scb), - le32_to_cpu(scb->sg_list[index + 1].address), - le32_to_cpu(scb->sg_list[index + 1].length)); - printk(WARN_LEAD " cur_address:0x%x resid_dcnt:0x%06x\n", - p->host_no, CTL_OF_SCB(scb), - cur_addr, resid_dcnt); - break; - } - - if( (resid_sgcnt == 0) && - ((resid_dcnt == 0) || (resid_dcnt == 0xffffff))) - { - /* - * We are at the end of the transfer and this is about a byte - * we ignored already (because the sequencer knew this was - * the last segment and set the adapter to ignore any wide - * residue bytes that might come through, which is only done - * on the last scatter gather segment of transfers). - */ - break; - } - else if(cur_addr == native_addr) + if(resid_dcnt == native_length) { - /* - * If our current address matches the sg_seg->address then we - * have to back up the sg array to the previous segment and set - * it up to have only one byte of transfer left to go. - */ if(index == 0) { - printk(WARN_LEAD "bogus WIDE_RESIDUE message, no data has been " - "transferred.\n", p->host_no, CTL_OF_SCB(scb)); + /* + * Oops, this isn't right, we can't back up to before the + * beginning. This must be a bogus message, ignore it. + */ break; } - resid_sgcnt++; - index--; - cur_addr = le32_to_cpu(scb->sg_list[index].address) + - le32_to_cpu(scb->sg_list[index].length) - 1; - native_addr = aic_inb(p, SG_NEXT) | (aic_inb(p, SG_NEXT + 1) << 8) - | (aic_inb(p, SG_NEXT + 2) << 16) | (aic_inb(p, SG_NEXT + 3) << 24); - native_addr -= SG_SIZEOF; - aic_outb(p, resid_sgcnt, SG_COUNT); - aic_outb(p, resid_sgcnt, SCB_RESID_SGCNT); - aic_outb(p, native_addr & 0xff, SG_NEXT); - aic_outb(p, (native_addr >> 8) & 0xff, SG_NEXT + 1); - aic_outb(p, (native_addr >> 16) & 0xff, SG_NEXT + 2); - aic_outb(p, (native_addr >> 24) & 0xff, SG_NEXT + 3); - aic_outb(p, 1, SCB_RESID_DCNT); - aic_outb(p, 0, SCB_RESID_DCNT + 1); - aic_outb(p, 0, SCB_RESID_DCNT + 2); - aic_outb(p, 1, HCNT); - aic_outb(p, 0, HCNT + 1); - aic_outb(p, 0, HCNT + 2); - aic_outb(p, cur_addr & 0xff, HADDR); - aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1); - aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2); - aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3); + resid_dcnt = 1; + resid_sgcnt += 1; + native_addr = le32_to_cpu(scb->sg_list[index - 1].address); + native_length = le32_to_cpu(scb->sg_list[index - 1].length); + cur_addr = native_addr + (native_length - 1); + sg_addr -= sizeof(struct hw_scatterlist); } else { /* - * Back the data pointer up by one and add one to the remaining - * byte count. Then store that in the HCNT and HADDR registers. + * resid_dcnt != native_length, so we are in the middle of a SG + * element. Back it up one byte and leave the rest alone. */ - cur_addr--; - resid_dcnt++; - aic_outb(p, resid_dcnt & 0xff, SCB_RESID_DCNT); - aic_outb(p, (resid_dcnt >> 8) & 0xff, SCB_RESID_DCNT + 1); - aic_outb(p, (resid_dcnt >> 16) & 0xff, SCB_RESID_DCNT + 2); - aic_outb(p, resid_dcnt & 0xff, HCNT); - aic_outb(p, (resid_dcnt >> 8) & 0xff, HCNT + 1); - aic_outb(p, (resid_dcnt >> 16) & 0xff, HCNT + 2); - aic_outb(p, cur_addr & 0xff, HADDR); - aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1); - aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2); - aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3); + resid_dcnt += 1; + cur_addr -= 1; } + + /* + * Output the new addresses and counts to the right places on the + * card. + */ + aic_outb(p, resid_sgcnt, SG_COUNT); + aic_outb(p, resid_sgcnt, SCB_RESID_SGCNT); + aic_outb(p, sg_addr & 0xff, SG_COUNT + 1); + aic_outb(p, (sg_addr >> 8) & 0xff, SG_COUNT + 2); + aic_outb(p, (sg_addr >> 16) & 0xff, SG_COUNT + 3); + aic_outb(p, (sg_addr >> 24) & 0xff, SG_COUNT + 4); + aic_outb(p, resid_dcnt & 0xff, SCB_RESID_DCNT); + aic_outb(p, (resid_dcnt >> 8) & 0xff, SCB_RESID_DCNT + 1); + aic_outb(p, (resid_dcnt >> 16) & 0xff, SCB_RESID_DCNT + 2); + /* - * The sequencer actually wants to find the new address and byte - * count in the SHCNT and SHADDR register sets. These registers - * are a shadow of the regular HCNT and HADDR registers. On the - * Ultra2 controllers, these registers are read only and the way - * we have to set their values is to put the values we want into - * the HCNT and HADDR registers and then output PRELOADEN into - * the DFCNTRL register which causes the card to latch the current - * values in the HADDR and HCNT registers and drop it through to - * the shadow registers. On older cards we copy them directly - * across by hand. + * The sequencer actually wants to find the new address + * in the SHADDR register set. On the Ultra2 and later controllers + * this register set is readonly. In order to get the right number + * into the register, you actually have to enter it in HADDR and then + * use the PRELOADEN bit of DFCNTRL to drop it through from the + * HADDR register to the SHADDR register. On non-Ultra2 controllers, + * we simply write it direct. */ if(p->features & AHC_ULTRA2) { - aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); - i=0; + /* + * We might as well be accurate and drop both the resid_dcnt and + * cur_addr into HCNT and HADDR and have both of them drop + * through to the shadow layer together. + */ + aic_outb(p, resid_dcnt & 0xff, HCNT); + aic_outb(p, (resid_dcnt >> 8) & 0xff, HCNT + 1); + aic_outb(p, (resid_dcnt >> 16) & 0xff, HCNT + 2); + aic_outb(p, cur_addr & 0xff, HADDR); + aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1); + aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2); + aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3); + aic_outb(p, aic_inb(p, DMAPARAMS) | PRELOADEN, DFCNTRL); udelay(1); - while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000)) - { - udelay(1); - } aic_outb(p, aic_inb(p, DMAPARAMS) & ~(SCSIEN|HDMAEN), DFCNTRL); i=0; - udelay(1); while(((aic_inb(p, DFCNTRL) & (SCSIEN|HDMAEN)) != 0) && (i++ < 1000)) { udelay(1); @@ -5321,9 +5299,6 @@ } else { - aic_outb(p, resid_dcnt & 0xff, STCNT); - aic_outb(p, (resid_dcnt >> 8) & 0xff, STCNT + 1); - aic_outb(p, (resid_dcnt >> 16) & 0xff, STCNT + 2); aic_outb(p, cur_addr & 0xff, SHADDR); aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1); aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2); @@ -5332,15 +5307,82 @@ } break; - -#if AIC7XXX_NOT_YET - case TRACEPOINT: + case SEQ_SG_FIXUP: + { + unsigned char scb_index, tmp; + int sg_addr, sg_length; + + scb_index = aic_inb(p, SCB_TAG); + + if(scb_index > p->scb_data->numscbs) { - printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no, - channel, target, lun); + printk(WARN_LEAD "invalid scb_index during SEQ_SG_FIXUP.\n", + p->host_no, -1, -1, -1); + printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 " + "0x%x\n", p->host_no, -1, -1, -1, + aic_inb(p, SCSISIGI), + aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8), + aic_inb(p, SSTAT0), aic_inb(p, SSTAT1)); + printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n", + p->host_no, -1, -1, -1, aic_inb(p, SG_CACHEPTR), + aic_inb(p, SSTAT2), aic_inb(p, STCNT + 2) << 16 | + aic_inb(p, STCNT + 1) << 8 | aic_inb(p, STCNT)); + /* + * XXX: Add error handling here + */ + break; } - break; + scb = p->scb_data->scb_array[scb_index]; + if(!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) + { + printk(WARN_LEAD "invalid scb during SEQ_SG_FIXUP flags:0x%x " + "scb->cmd:0x%x\n", p->host_no, CTL_OF_SCB(scb), + scb->flags, (unsigned int)scb->cmd); + printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 " + "0x%x\n", p->host_no, CTL_OF_SCB(scb), + aic_inb(p, SCSISIGI), + aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8), + aic_inb(p, SSTAT0), aic_inb(p, SSTAT1)); + printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n", + p->host_no, CTL_OF_SCB(scb), aic_inb(p, SG_CACHEPTR), + aic_inb(p, SSTAT2), aic_inb(p, STCNT + 2) << 16 | + aic_inb(p, STCNT + 1) << 8 | aic_inb(p, STCNT)); + break; + } + if(aic7xxx_verbose & VERBOSE_MINOR_ERROR) + printk(INFO_LEAD "Fixing up SG address for sequencer.\n", p->host_no, + CTL_OF_SCB(scb)); + /* + * Advance the SG pointer to the next element in the list + */ + tmp = aic_inb(p, SG_NEXT); + tmp += SG_SIZEOF; + aic_outb(p, tmp, SG_NEXT); + if( tmp < SG_SIZEOF ) + aic_outb(p, aic_inb(p, SG_NEXT + 1) + 1, SG_NEXT + 1); + tmp = aic_inb(p, SG_COUNT) - 1; + aic_outb(p, tmp, SG_COUNT); + sg_addr = le32_to_cpu(scb->sg_list[scb->sg_count - tmp].address); + sg_length = le32_to_cpu(scb->sg_list[scb->sg_count - tmp].length); + /* + * Now stuff the element we just advanced past down onto the + * card so it can be stored in the residual area. + */ + aic_outb(p, sg_addr & 0xff, HADDR); + aic_outb(p, (sg_addr >> 8) & 0xff, HADDR + 1); + aic_outb(p, (sg_addr >> 16) & 0xff, HADDR + 2); + aic_outb(p, (sg_addr >> 24) & 0xff, HADDR + 3); + aic_outb(p, sg_length & 0xff, HCNT); + aic_outb(p, (sg_length >> 8) & 0xff, HCNT + 1); + aic_outb(p, (sg_length >> 16) & 0xff, HCNT + 2); + aic_outb(p, (tmp << 2) | ((tmp == 1) ? LAST_SEG : 0), SG_CACHEPTR); + aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); + while(aic_inb(p, SSTAT0) & SDONE) udelay(1); + while(aic_inb(p, DFCNTRL) & (HDMAEN|SCSIEN)) aic_outb(p, 0, DFCNTRL); + } + break; +#if AIC7XXX_NOT_YET case TRACEPOINT2: { printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no, @@ -5385,6 +5427,10 @@ unsigned char target_scsirate, tindex; unsigned short target_mask; unsigned char target, channel, lun; + unsigned char bus_width, new_bus_width; + unsigned char trans_options, new_trans_options; + unsigned int period, new_period, offset, new_offset, maxsync; + struct aic7xxx_syncrate *syncrate; target = scb->cmd->target; channel = scb->cmd->channel; @@ -5408,6 +5454,35 @@ } /* + * Even if we are an Ultra3 card, don't allow Ultra3 sync rates when + * using the SDTR messages. We need the PPR messages to enable the + * higher speeds that include things like Dual Edge clocking. + */ + if (p->features & AHC_ULTRA2) + { + if ( (aic_inb(p, SBLKCTL) & ENAB40) && + !(aic_inb(p, SSTAT2) & EXP_ACTIVE) ) + { + if (p->features & AHC_ULTRA3) + maxsync = AHC_SYNCRATE_ULTRA3; + else + maxsync = AHC_SYNCRATE_ULTRA2; + } + else + { + maxsync = AHC_SYNCRATE_ULTRA; + } + } + else if (p->features & AHC_ULTRA) + { + maxsync = AHC_SYNCRATE_ULTRA; + } + else + { + maxsync = AHC_SYNCRATE_FAST; + } + + /* * Just accept the length byte outright and perform * more checking once we know the message type. */ @@ -5418,9 +5493,6 @@ { case MSG_EXT_SDTR: { - unsigned int period, offset; - unsigned char maxsync, saved_offset, options; - struct aic7xxx_syncrate *syncrate; if (p->msg_buf[1] != MSG_EXT_SDTR_LEN) { @@ -5433,35 +5505,18 @@ break; } - period = p->msg_buf[3]; - saved_offset = offset = p->msg_buf[4]; - options = 0; + period = new_period = p->msg_buf[3]; + offset = new_offset = p->msg_buf[4]; + trans_options = new_trans_options = 0; + bus_width = new_bus_width = target_scsirate & WIDEXFER; /* - * Even if we are an Ultra3 card, don't allow Ultra3 sync rates when - * using the SDTR messages. We need the PPR messages to enable the - * higher speeds that include things like Dual Edge clocking. + * If our current max syncrate is in the Ultra3 range, bump it back + * down to Ultra2 since we can't negotiate DT transfers using SDTR */ - if (p->features & AHC_ULTRA2) - { - if ( (aic_inb(p, SBLKCTL) & ENAB40) && - !(aic_inb(p, SSTAT2) & EXP_ACTIVE) ) - { - maxsync = AHC_SYNCRATE_ULTRA2; - } - else - { - maxsync = AHC_SYNCRATE_ULTRA; - } - } - else if (p->features & AHC_ULTRA) - { - maxsync = AHC_SYNCRATE_ULTRA; - } - else - { - maxsync = AHC_SYNCRATE_FAST; - } + if(maxsync == AHC_SYNCRATE_ULTRA3) + maxsync = AHC_SYNCRATE_ULTRA2; + /* * We might have a device that is starting negotiation with us * before we can start up negotiation with it....be prepared to @@ -5471,88 +5526,116 @@ if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) != (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) ) { - if (!(p->dev_flags[tindex] & DEVICE_SCANNED) && - !(p->needsdtr_copy & target_mask) && - (p->transinfo[tindex].user_offset) ) + if (!(p->dev_flags[tindex] & DEVICE_DTR_SCANNED)) { /* - * Not only is the device starting this up, but it also hasn't - * been scanned yet, so this would likely be our TUR or our - * INQUIRY command at scan time, so we need to use the - * settings from the SEEPROM if they existed. Of course, even - * if we didn't find a SEEPROM, we stuffed default values into - * the user settings anyway, so use those in all cases. + * We shouldn't get here unless this is a narrow drive, wide + * devices should trigger this same section of code in the WDTR + * handler first instead. */ - p->transinfo[tindex].goal_period = - p->transinfo[tindex].user_period; - if(p->features & AHC_ULTRA2) - { - p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; - } - else if (p->transinfo[tindex].cur_width) + p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_8_BIT; + p->transinfo[tindex].goal_options = 0; + if(p->transinfo[tindex].user_offset) { - p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; + p->needsdtr_copy |= target_mask; + p->transinfo[tindex].goal_period = + MAX(10,p->transinfo[tindex].user_period); + if(p->features & AHC_ULTRA2) + { + p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; + } + else + { + p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; + } } else { - p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; + p->needsdtr_copy &= ~target_mask; + p->transinfo[tindex].goal_period = 255; + p->transinfo[tindex].goal_offset = 0; } - p->needsdtr_copy |= target_mask; + p->dev_flags[tindex] |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR; + } + else if ((p->needsdtr_copy & target_mask) == 0) + { + /* + * This is a preemptive message from the target, we've already + * scanned this target and set our options for it, and we + * don't need a WDTR with this target (for whatever reason), + * so reject this incoming WDTR + */ + reject = TRUE; + break; } + + /* The device is sending this message first and we have to reply */ + reply = TRUE; + if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) { printk(INFO_LEAD "Received pre-emptive SDTR message from " "target.\n", p->host_no, CTL_OF_SCB(scb)); } - if ( !p->transinfo[tindex].goal_offset ) - period = 255; - if ( p->transinfo[tindex].goal_period > period ) - period = p->transinfo[tindex].goal_period; + /* + * Validate the values the device passed to us against our SEEPROM + * settings. We don't have to do this if we aren't replying since + * the device isn't allowed to send values greater than the ones + * we first sent to it. + */ + new_period = MAX(period, p->transinfo[tindex].goal_period); + new_offset = MIN(offset, p->transinfo[tindex].goal_offset); } - - syncrate = aic7xxx_find_syncrate(p, &period, maxsync, &options); - aic7xxx_validate_offset(p, syncrate, &offset, - target_scsirate & WIDEXFER); - aic7xxx_set_syncrate(p, syncrate, target, channel, period, - offset, options, AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + + /* + * Use our new_period, new_offset, bus_width, and card options + * to determine the actual syncrate settings + */ + syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync, + &trans_options); + aic7xxx_validate_offset(p, syncrate, &new_offset, bus_width); /* - * Did we drop to async? Or are we sending a reply? If we are, - * then we have to make sure that the reply value reflects the proper - * settings so we need to set the goal values according to what - * we need to send. + * Did we drop to async? If so, send a reply regardless of whether + * or not we initiated this negotiation. */ - if ( (offset != saved_offset) || - ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) != - (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) ) ) + if ((new_offset == 0) && (new_offset != offset)) { - aic7xxx_set_syncrate(p, syncrate, target, channel, period, offset, - options, AHC_TRANS_GOAL|AHC_TRANS_QUITE); + p->needsdtr_copy &= ~target_mask; + reply = TRUE; } /* - * Did we start this, if not, or if we went to low and had to + * Did we start this, if not, or if we went too low and had to * go async, then send an SDTR back to the target */ - p->needsdtr &= ~target_mask; - p->dtr_pending &= ~target_mask; - if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) != - (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) || - (offset != saved_offset) ) + if(reply) { - reply = TRUE; - p->dtr_pending |= target_mask; + /* when sending a reply, make sure that the goal settings are + * updated along with current and active since the code that + * will actually build the message for the sequencer uses the + * goal settings as its guidelines. + */ + aic7xxx_set_syncrate(p, syncrate, target, channel, new_period, + new_offset, trans_options, + AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR); scb->flags &= ~SCB_MSGOUT_BITS; scb->flags |= SCB_MSGOUT_SDTR; aic_outb(p, HOST_MSG, MSG_OUT); aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); } + else + { + aic7xxx_set_syncrate(p, syncrate, target, channel, new_period, + new_offset, trans_options, + AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + p->needsdtr &= ~target_mask; + } done = TRUE; break; } case MSG_EXT_WDTR: { - unsigned char bus_width; if (p->msg_buf[1] != MSG_EXT_WDTR_LEN) { @@ -5565,7 +5648,8 @@ break; } - bus_width = p->msg_buf[3]; + bus_width = new_bus_width = p->msg_buf[3]; + if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR)) == (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR) ) { @@ -5584,7 +5668,7 @@ } /* We fall through on purpose */ case MSG_EXT_WDTR_BUS_8_BIT: { - bus_width = MSG_EXT_WDTR_BUS_8_BIT; + p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_8_BIT; p->needwdtr_copy &= ~target_mask; break; } @@ -5593,29 +5677,40 @@ break; } } - p->dtr_pending &= ~target_mask; p->needwdtr &= ~target_mask; + aic7xxx_set_width(p, target, channel, lun, new_bus_width, + AHC_TRANS_ACTIVE|AHC_TRANS_CUR); } else { - if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) ) + if ( !(p->dev_flags[tindex] & DEVICE_DTR_SCANNED) ) { /* * Well, we now know the WDTR and SYNC caps of this device since * it contacted us first, mark it as such and copy the user stuff * over to the goal stuff. */ - p->transinfo[tindex].goal_period = - p->transinfo[tindex].user_period; + if( (p->features & AHC_WIDE) && p->transinfo[tindex].user_width ) + { + p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_16_BIT; + p->needwdtr_copy |= target_mask; + } + + /* + * Devices that support DT transfers don't start WDTR requests + */ + p->transinfo[tindex].goal_options = 0; + if(p->transinfo[tindex].user_offset) { + p->needsdtr_copy |= target_mask; + p->transinfo[tindex].goal_period = + MAX(10,p->transinfo[tindex].user_period); if(p->features & AHC_ULTRA2) { p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; } - else if( p->transinfo[tindex].user_width && - (bus_width == MSG_EXT_WDTR_BUS_16_BIT) && - p->features & AHC_WIDE ) + else if( p->transinfo[tindex].goal_width ) { p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; } @@ -5623,48 +5718,76 @@ { p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; } + } else { + p->needsdtr_copy &= ~target_mask; + p->transinfo[tindex].goal_period = 255; + p->transinfo[tindex].goal_offset = 0; } - p->transinfo[tindex].goal_width = - p->transinfo[tindex].user_width; - p->needwdtr_copy |= target_mask; - p->needsdtr_copy |= target_mask; + + p->dev_flags[tindex] |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR; } - if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) + else if ((p->needwdtr_copy & target_mask) == 0) { - printk(INFO_LEAD "Received pre-emptive WDTR message from " - "target.\n", p->host_no, CTL_OF_SCB(scb)); - } - switch(bus_width) + /* + * This is a preemptive message from the target, we've already + * scanned this target and set our options for it, and we + * don't need a WDTR with this target (for whatever reason), + * so reject this incoming WDTR + */ + reject = TRUE; + break; + } + + /* The device is sending this message first and we have to reply */ + reply = TRUE; + + if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) { - default: + printk(INFO_LEAD "Received pre-emptive WDTR message from " + "target.\n", p->host_no, CTL_OF_SCB(scb)); + } + switch(bus_width) + { + case MSG_EXT_WDTR_BUS_16_BIT: { if ( (p->features & AHC_WIDE) && (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT) ) { - bus_width = MSG_EXT_WDTR_BUS_16_BIT; + new_bus_width = MSG_EXT_WDTR_BUS_16_BIT; break; } } /* Fall through if we aren't a wide card */ + default: case MSG_EXT_WDTR_BUS_8_BIT: { p->needwdtr_copy &= ~target_mask; - bus_width = MSG_EXT_WDTR_BUS_8_BIT; - aic7xxx_set_width(p, target, channel, lun, bus_width, - AHC_TRANS_GOAL|AHC_TRANS_QUITE); + new_bus_width = MSG_EXT_WDTR_BUS_8_BIT; break; } } - reply = TRUE; scb->flags &= ~SCB_MSGOUT_BITS; scb->flags |= SCB_MSGOUT_WDTR; p->needwdtr &= ~target_mask; - p->dtr_pending |= target_mask; + if((p->dtr_pending & target_mask) == 0) + { + /* there is no other command with SCB_DTR_SCB already set that will + * trigger the release of the dtr_pending bit. Both set the bit + * and set scb->flags |= SCB_DTR_SCB + */ + p->dtr_pending |= target_mask; + scb->flags |= SCB_DTR_SCB; + } aic_outb(p, HOST_MSG, MSG_OUT); aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); + /* when sending a reply, make sure that the goal settings are + * updated along with current and active since the code that + * will actually build the message for the sequencer uses the + * goal settings as its guidelines. + */ + aic7xxx_set_width(p, target, channel, lun, new_bus_width, + AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR); } - aic7xxx_set_width(p, target, channel, lun, bus_width, - AHC_TRANS_ACTIVE|AHC_TRANS_CUR); /* * By virtue of the SCSI spec, a WDTR message negates any existing @@ -5681,10 +5804,6 @@ } case MSG_EXT_PPR: { - unsigned char bus_width, trans_options, new_trans_options; - unsigned int period, offset; - unsigned char maxsync, saved_offset; - struct aic7xxx_syncrate *syncrate; if (p->msg_buf[1] != MSG_EXT_PPR_LEN) { @@ -5697,9 +5816,9 @@ break; } - period = p->msg_buf[3]; - offset = saved_offset = p->msg_buf[5]; - bus_width = p->msg_buf[6]; + period = new_period = p->msg_buf[3]; + offset = new_offset = p->msg_buf[5]; + bus_width = new_bus_width = p->msg_buf[6]; trans_options = new_trans_options = p->msg_buf[7] & 0xf; if(aic7xxx_verbose & VERBOSE_NEGOTIATION2) @@ -5709,22 +5828,6 @@ trans_options); } - if ( (aic_inb(p, SBLKCTL) & ENAB40) && - !(aic_inb(p, SSTAT2) & EXP_ACTIVE) ) - { - if(p->features & AHC_ULTRA3) - { - maxsync = AHC_SYNCRATE_ULTRA3; - } - else - { - maxsync = AHC_SYNCRATE_ULTRA2; - } - } - else - { - maxsync = AHC_SYNCRATE_ULTRA; - } /* * We might have a device that is starting negotiation with us * before we can start up negotiation with it....be prepared to @@ -5733,13 +5836,22 @@ */ if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) != (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR) ) - { - reply = TRUE; - scb->flags &= ~SCB_MSGOUT_BITS; - scb->flags |= SCB_MSGOUT_PPR; - p->dev_flags[tindex] |= DEVICE_SCSI_3; - if (!(p->dev_flags[tindex] & DEVICE_SCANNED)) - { + { + /* Have we scanned the device yet? */ + if (!(p->dev_flags[tindex] & DEVICE_DTR_SCANNED)) + { + /* The device is electing to use PPR messages, so we will too until + * we know better */ + p->needppr |= target_mask; + p->needppr_copy |= target_mask; + p->needsdtr &= ~target_mask; + p->needsdtr_copy &= ~target_mask; + p->needwdtr &= ~target_mask; + p->needwdtr_copy &= ~target_mask; + + /* We know the device is SCSI-3 compliant due to PPR */ + p->dev_flags[tindex] |= DEVICE_SCSI_3; + /* * Not only is the device starting this up, but it also hasn't * been scanned yet, so this would likely be our TUR or our @@ -5748,15 +5860,19 @@ * if we didn't find a SEEPROM, we stuffed default values into * the user settings anyway, so use those in all cases. */ - p->transinfo[tindex].goal_period = - p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_width = + p->transinfo[tindex].user_width; if(p->transinfo[tindex].user_offset) { + p->transinfo[tindex].goal_period = + p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_options = + p->transinfo[tindex].user_options; if(p->features & AHC_ULTRA2) { p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; } - else if( p->transinfo[tindex].user_width && + else if( p->transinfo[tindex].goal_width && (bus_width == MSG_EXT_WDTR_BUS_16_BIT) && p->features & AHC_WIDE ) { @@ -5767,117 +5883,142 @@ p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; } } - p->transinfo[tindex].goal_width = - p->transinfo[tindex].user_width; - p->transinfo[tindex].goal_options = - p->transinfo[tindex].user_options; + else + { + p->transinfo[tindex].goal_period = 255; + p->transinfo[tindex].goal_offset = 0; + p->transinfo[tindex].goal_options = 0; + } + p->dev_flags[tindex] |= DEVICE_DTR_SCANNED | DEVICE_PRINT_DTR; } + else if ((p->needppr_copy & target_mask) == 0) + { + /* + * This is a preemptive message from the target, we've already + * scanned this target and set our options for it, and we + * don't need a PPR with this target (for whatever reason), + * so reject this incoming PPR + */ + reject = TRUE; + break; + } + + /* The device is sending this message first and we have to reply */ + reply = TRUE; + if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) { printk(INFO_LEAD "Received pre-emptive PPR message from " "target.\n", p->host_no, CTL_OF_SCB(scb)); } - if ( !p->transinfo[tindex].goal_offset ) - period = 255; - if ( p->transinfo[tindex].goal_period > period ) - period = p->transinfo[tindex].goal_period; - if ( p->transinfo[tindex].goal_options == 0 ) - new_trans_options = 0; - switch(bus_width) + + } + + switch(bus_width) + { + case MSG_EXT_WDTR_BUS_16_BIT: { - default: + if ( (p->transinfo[tindex].goal_width == + MSG_EXT_WDTR_BUS_16_BIT) && p->features & AHC_WIDE) { - if ( (p->features & AHC_WIDE) && - (p->transinfo[tindex].goal_width == - MSG_EXT_WDTR_BUS_16_BIT) ) - { - bus_width = MSG_EXT_WDTR_BUS_16_BIT; - break; - } - } /* Fall through if we aren't a wide card */ - case MSG_EXT_WDTR_BUS_8_BIT: - { - p->needwdtr_copy &= ~target_mask; - bus_width = MSG_EXT_WDTR_BUS_8_BIT; - aic7xxx_set_width(p, target, channel, lun, bus_width, - AHC_TRANS_GOAL|AHC_TRANS_QUITE); break; } } - if ( (p->transinfo[tindex].goal_period > 9) || - (p->transinfo[tindex].goal_options == 0) ) + default: { - scb->flags &= ~SCB_MSGOUT_BITS; - reject = TRUE; - reply = FALSE; - p->needppr &= ~(1 << tindex); - p->needppr_copy &= ~(1 << tindex); - if ( p->transinfo[tindex].goal_offset ) + if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && + ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) || + (aic7xxx_verbose > 0xffff)) ) { - p->needsdtr |= (1 << tindex); - p->needsdtr_copy |= (1 << tindex); - } - if ( p->transinfo[tindex].goal_width ) - { - p->needwdtr |= (1 << tindex); - p->needwdtr_copy |= (1 << tindex); + reply = TRUE; + printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n", + p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width)); } + } /* We fall through on purpose */ + case MSG_EXT_WDTR_BUS_8_BIT: + { + /* + * According to the spec, if we aren't wide, we also can't be + * Dual Edge so clear the options byte + */ + new_trans_options = 0; + new_bus_width = MSG_EXT_WDTR_BUS_8_BIT; + break; } } + + if(reply) + { + /* when sending a reply, make sure that the goal settings are + * updated along with current and active since the code that + * will actually build the message for the sequencer uses the + * goal settings as its guidelines. + */ + aic7xxx_set_width(p, target, channel, lun, new_bus_width, + AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync, + &new_trans_options); + aic7xxx_validate_offset(p, syncrate, &new_offset, new_bus_width); + aic7xxx_set_syncrate(p, syncrate, target, channel, new_period, + new_offset, new_trans_options, + AHC_TRANS_GOAL|AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + } else { - switch(bus_width) + aic7xxx_set_width(p, target, channel, lun, new_bus_width, + AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + syncrate = aic7xxx_find_syncrate(p, &new_period, maxsync, + &new_trans_options); + aic7xxx_validate_offset(p, syncrate, &new_offset, new_bus_width); + aic7xxx_set_syncrate(p, syncrate, target, channel, new_period, + new_offset, new_trans_options, + AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + } + + /* + * As it turns out, if we don't *have* to have PPR messages, then + * configure ourselves not to use them since that makes some + * external drive chassis work (those chassis can't parse PPR + * messages and they mangle the SCSI bus until you send a WDTR + * and SDTR that they can understand). + */ + if(new_trans_options == 0) + { + p->needppr &= ~target_mask; + p->needppr_copy &= ~target_mask; + if(new_offset) { - default: - { - reject = TRUE; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && - ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) || - (aic7xxx_verbose > 0xffff)) ) - { - printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n", - p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width)); - } - } /* We fall through on purpose */ - case MSG_EXT_WDTR_BUS_8_BIT: - { - /* - * According to the spec, if we aren't wide, we also can't be - * Dual Edge so clear the options byte - */ - new_trans_options = 0; - bus_width = MSG_EXT_WDTR_BUS_8_BIT; - break; - } - case MSG_EXT_WDTR_BUS_16_BIT: - { - break; - } + p->needsdtr |= target_mask; + p->needsdtr_copy |= target_mask; + } + if (new_bus_width) + { + p->needwdtr |= target_mask; + p->needwdtr_copy |= target_mask; } } - if ( !reject ) + if((new_offset == 0) && (offset != 0)) { - aic7xxx_set_width(p, target, channel, lun, bus_width, - AHC_TRANS_ACTIVE|AHC_TRANS_CUR); - syncrate = aic7xxx_find_syncrate(p, &period, maxsync, - &new_trans_options); - aic7xxx_validate_offset(p, syncrate, &offset, bus_width); - aic7xxx_set_syncrate(p, syncrate, target, channel, period, - offset, new_trans_options, - AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + /* + * Oops, the syncrate went to low for this card and we fell off + * to async (should never happen with a device that uses PPR + * messages, but have to be complete) + */ + reply = TRUE; } - p->dtr_pending &= ~target_mask; - p->needppr &= ~target_mask; if(reply) { - p->dtr_pending |= target_mask; scb->flags &= ~SCB_MSGOUT_BITS; scb->flags |= SCB_MSGOUT_PPR; aic_outb(p, HOST_MSG, MSG_OUT); aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); } + else + { + p->needppr &= ~target_mask; + } done = TRUE; break; } @@ -6115,16 +6256,14 @@ printerror = 0; } } - if ( (scb != NULL) && - (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)]) ) + if ( (scb != NULL) && (scb->flags & SCB_DTR_SCB) ) { /* - * This might be a SCSI-3 device that is dropping the bus due to - * errors and signalling that we should reduce the transfer speed. - * All we have to do is complete this command (since it's a negotiation - * command already) and the checksum routine should flag an error and - * reduce the speed setting and renegotiate. We call the reset routing - * just to clean out the hardware from this scb. + * Hmmm...error during a negotiation command. Either we have a + * borken bus, or the device doesn't like our negotiation message. + * Since we check the INQUIRY data of a device before sending it + * negotiation messages, assume the bus is borken for whatever + * reason. Complete the command. */ printerror = 0; aic7xxx_reset_device(p, target, channel, ALL_LUNS, scb->hscb->tag); @@ -6256,19 +6395,6 @@ cmd->result = 0; scb = NULL; } - else if (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)]) - { - /* - * Turn off the needsdtr, needwdtr, and needppr bits since this device - * doesn't seem to exist. - */ - p->needppr &= ~(0x01 << TARGET_INDEX(scb->cmd)); - p->needppr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd)); - p->needsdtr &= ~(0x01 << TARGET_INDEX(scb->cmd)); - p->needsdtr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd)); - p->needwdtr &= ~(0x01 << TARGET_INDEX(scb->cmd)); - p->needwdtr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd)); - } } /* * Keep the sequencer from trying to restart any selections @@ -6391,7 +6517,6 @@ } } else if( (lastphase == P_MESGOUT) && - (cmd == p->dev_dtr_cmnd[tindex]) && (scb->flags & SCB_MSGOUT_PPR) ) { /* @@ -6410,7 +6535,6 @@ aic7xxx_set_syncrate(p, NULL, scb->cmd->target, scb->cmd->channel, 0, 0, 0, AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE); p->transinfo[tindex].goal_options = 0; - p->dtr_pending &= ~(1 << tindex); scb->flags &= ~SCB_MSGOUT_BITS; if(aic7xxx_verbose & VERBOSE_NEGOTIATION2) { @@ -6433,87 +6557,6 @@ } scb = NULL; } - else if(p->dev_flags[tindex] & DEVICE_PARITY_ERROR) - { - struct aic7xxx_syncrate *syncrate; - unsigned int period = p->transinfo[tindex].cur_period; - unsigned char options = p->transinfo[tindex].cur_options; - /* - * oops, we had a failure, lower the transfer rate and try again. It's - * worth noting here that it might be wise to also check for typical - * wide setting on narrow cable type problems and try disabling wide - * instead of slowing down if those exist. That's hard to do with simple - * checksums though. - */ - printk(WARN_LEAD "Parity error during %s phase.\n", - p->host_no, CTL_OF_SCB(scb), phase); - if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL) - { - syncrate++; - if( (syncrate->rate[0] != NULL) && - (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) ) - { - p->transinfo[tindex].goal_period = syncrate->period; - if( p->transinfo[tindex].goal_period > 9 ) - { - p->transinfo[tindex].goal_options = 0; - p->needppr &= ~(1<needsdtr |= (1<needppr_copy &= ~(1<needsdtr_copy |= (1<transinfo[tindex].goal_width) - { - p->needwdtr |= (1<needwdtr_copy |= (1<transinfo[tindex].goal_width) - { - p->transinfo[tindex].goal_width = 0; - p->needwdtr &= ~(1<needwdtr_copy &= ~(1<transinfo[tindex].goal_offset = - p->transinfo[tindex].user_offset; - p->transinfo[tindex].goal_period = - p->transinfo[tindex].user_period; - p->transinfo[tindex].goal_options = - p->transinfo[tindex].user_options; - if( p->transinfo[tindex].goal_period <= 9 ) - { - p->needppr |= (1<needsdtr &= ~(1<needppr_copy |= (1<needsdtr_copy &= ~(1<needppr &= ~(1<needsdtr |= (1<needppr_copy &= ~(1<needsdtr_copy |= (1<transinfo[tindex].goal_offset = 0; - p->transinfo[tindex].goal_period = 255; - p->transinfo[tindex].goal_options = 0; - p->transinfo[tindex].goal_width = 0; - p->needppr &= ~(1<needsdtr &= ~(1<needwdtr &= ~(1<needppr_copy &= ~(1<needsdtr_copy &= ~(1<needwdtr_copy &= ~(1<dev_flags[tindex] &= ~DEVICE_PARITY_ERROR; - } - else - { - p->dev_flags[tindex] |= DEVICE_PARITY_ERROR; - } /* * We've set the hardware to assert ATN if we get a parity @@ -6782,34 +6825,11 @@ else if (scb->flags & SCB_SENSE) { char *buffer = &scb->cmd->sense_buffer[0]; - if (scb->cmd == p->dev_dtr_cmnd[tindex]) - { - struct aic7xxx_scb *old_scb; - /* - * We have valid sense data, send it back immediately. - */ - old_scb = p->scb_data->scb_array[scb->cmd->next->tag]; - *old_scb->cmd->sense_buffer = *scb->cmd->sense_buffer; - old_scb->hscb->target_status = scb->hscb->target_status; - old_scb->cmd->result = scb->hscb->target_status; - old_scb->cmd->result |= (DID_ERROR << 16); - aic7xxx_status(old_scb->cmd) = scb->hscb->target_status; - scbq_remove(&p->waiting_scbs, old_scb); - scbq_remove(&p->delayed_scbs[tindex], old_scb); - scb->cmd->next = NULL; - aic7xxx_done(p, scb); - aic7xxx_done(p, old_scb); - continue; - } - else if (buffer[12] == 0x47 || buffer[12] == 0x54) + + if (buffer[12] == 0x47 || buffer[12] == 0x54) { /* - * SCSI errors, run domain validation and re-run negotiation - */ - p->needdv |= (1<needppr |= (p->needppr_copy & (1<needsdtr |= (p->needsdtr_copy & (1<hscb->target_status = 0; scb->cmd->result = 0; + scb->hscb->residual_SG_segment_count = 0; + scb->hscb->residual_data_count[0] = 0; + scb->hscb->residual_data_count[1] = 0; + scb->hscb->residual_data_count[2] = 0; aic7xxx_error(scb->cmd) = DID_OK; + aic7xxx_status(scb->cmd) = 0; + /* + * The QUEUE_FULL/BUSY handler in aic7xxx_seqint takes care of putting + * this command on a timer and allowing us to retry it. Here, we + * just 0 out a few values so that they don't carry through to when + * the command finally does complete. + */ break; default: cmd = scb->cmd; @@ -6987,18 +7018,14 @@ if(!p) return; spin_lock_irqsave(&io_request_lock, cpu_flags); - if(test_and_set_bit(AHC_IN_ISR_BIT, (void *)&p->flags)) - { - spin_unlock_irqrestore(&io_request_lock, cpu_flags); - return; - } + p->flags |= AHC_IN_ISR; do { aic7xxx_isr(irq, dev_id, regs); } while ( (aic_inb(p, INTSTAT) & INT_PEND) ); aic7xxx_done_cmds_complete(p); aic7xxx_run_waiting_queues(p); - clear_bit(AHC_IN_ISR_BIT, (void *)&p->flags); + p->flags &= ~AHC_IN_ISR; spin_unlock_irqrestore(&io_request_lock, cpu_flags); } @@ -8779,7 +8806,7 @@ /* * In the future, we may call this function as a last resort for - * error handling. Let's be nice and not do any unecessary delays. + * error handling. Let's be nice and not do any unnecessary delays. */ wait = 1000; /* 1 msec (1000 * 1 msec) */ while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK)) @@ -8923,22 +8950,6 @@ kfree(p->scb_data); } - /* - * Free any alloced Scsi_Cmnd structures that might be around for - * negotiation purposes.... - */ - for (i = 0; i < MAX_TARGETS; i++) - { - if(p->dev_dtr_cmnd[i]) - { - if(p->dev_dtr_cmnd[i]->request_buffer) - { - kfree(p->dev_dtr_cmnd[i]->request_buffer); - } - kfree(p->dev_dtr_cmnd[i]); - } - } - pci_free_consistent(p->pdev, 3*256, (void *)p->untagged_scbs, p->fifo_dma); } @@ -9325,7 +9336,7 @@ } aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB); aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1); - p->needppr = p->needppr_copy = p->needdv = 0; + p->needppr = p->needppr_copy = 0; p->needwdtr = p->needwdtr_copy; p->needsdtr = p->needsdtr_copy; p->dtr_pending = 0; @@ -9383,6 +9394,81 @@ /*+F************************************************************************* * Function: + * aic7xxx_configure_bugs + * + * Description: + * Take the card passed in and set the appropriate bug flags based upon + * the card model. Also make any changes needed to device registers or + * PCI registers while we are here. + *-F*************************************************************************/ +static void +aic7xxx_configure_bugs(struct aic7xxx_host *p) +{ + unsigned short tmp_word; + + switch(p->chip & AHC_CHIPID_MASK) + { + case AHC_AIC7860: + p->bugs |= AHC_BUG_PCI_2_1_RETRY; + /* fall through */ + case AHC_AIC7850: + case AHC_AIC7870: + p->bugs |= AHC_BUG_TMODE_WIDEODD | AHC_BUG_CACHETHEN | AHC_BUG_PCI_MWI; + break; + case AHC_AIC7880: + p->bugs |= AHC_BUG_TMODE_WIDEODD | AHC_BUG_PCI_2_1_RETRY | + AHC_BUG_CACHETHEN | AHC_BUG_PCI_MWI; + break; + case AHC_AIC7890: + p->bugs |= AHC_BUG_AUTOFLUSH | AHC_BUG_CACHETHEN; + break; + case AHC_AIC7892: + p->bugs |= AHC_BUG_SCBCHAN_UPLOAD; + break; + case AHC_AIC7895: + p->bugs |= AHC_BUG_TMODE_WIDEODD | AHC_BUG_PCI_2_1_RETRY | + AHC_BUG_CACHETHEN | AHC_BUG_PCI_MWI; + break; + case AHC_AIC7896: + p->bugs |= AHC_BUG_CACHETHEN_DIS; + break; + case AHC_AIC7899: + p->bugs |= AHC_BUG_SCBCHAN_UPLOAD; + break; + default: + /* Nothing to do */ + break; + } + + /* + * Now handle the bugs that require PCI register or card register tweaks + */ + pci_read_config_word(p->pdev, PCI_COMMAND, &tmp_word); + if(p->bugs & AHC_BUG_PCI_MWI) + { + tmp_word &= ~PCI_COMMAND_INVALIDATE; + } + else + { + tmp_word |= PCI_COMMAND_INVALIDATE; + } + pci_write_config_word(p->pdev, PCI_COMMAND, tmp_word); + + if(p->bugs & AHC_BUG_CACHETHEN) + { + aic_outb(p, aic_inb(p, DSCOMMAND0) & ~CACHETHEN, DSCOMMAND0); + } + else if (p->bugs & AHC_BUG_CACHETHEN_DIS) + { + aic_outb(p, aic_inb(p, DSCOMMAND0) | CACHETHEN, DSCOMMAND0); + } + + return; +} + + +/*+F************************************************************************* + * Function: * aic7xxx_detect * * Description: @@ -10076,6 +10162,14 @@ aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS); } + /* + * Call our function to fixup any bugs that exist on this chipset. + * This may muck with PCI settings and other device settings, so + * make sure it's after all the other PCI and device register + * tweaks so it can back out bad settings on specific broken cards. + */ + aic7xxx_configure_bugs(temp_p); + if ( list_p == NULL ) { list_p = current_p = temp_p; @@ -10100,7 +10194,7 @@ } /* while(pdev=....) */ } /* for PCI_DEVICES */ } /* PCI BIOS present */ -#endif CONFIG_PCI +#endif /* CONFIG_PCI */ #if defined(__i386__) || defined(__alpha__) /* @@ -10319,6 +10413,11 @@ } /* + * All the 7770 based chipsets have this bug + */ + temp_p->bugs |= AHC_BUG_TMODE_WIDEODD; + + /* * Set the FIFO threshold and the bus off time. */ hostconf = aic_inb(temp_p, HOSTCONF); @@ -10537,308 +10636,16 @@ return (found); } -static void aic7xxx_build_negotiation_cmnd(struct aic7xxx_host *p, - Scsi_Cmnd *old_cmd, int tindex); - +#ifdef AIC7XXX_VERBOSE_DEBUGGING /*+F************************************************************************* * Function: - * aic7xxx_allocate_negotiation_command + * aic7xxx_print_scb * * Description: - * allocate the actual command struct and fill in the gaps... + * Dump the byte codes for an about to be sent SCB. *-F*************************************************************************/ -static Scsi_Cmnd * -aic7xxx_allocate_negotiation_command(struct aic7xxx_host *p, - Scsi_Cmnd *old_cmd, int tindex) -{ - Scsi_Cmnd *cmd; - char *buffer; - - if (!(p->dev_dtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) ) - { - return(NULL); - } - if (!(buffer = kmalloc(256, GFP_ATOMIC))) - { - kfree(p->dev_dtr_cmnd[tindex]); - p->dev_dtr_cmnd[tindex] = NULL; - return(NULL); - } - cmd = p->dev_dtr_cmnd[tindex]; - memset(cmd, 0, sizeof(Scsi_Cmnd)); - memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd)); - memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd)); - memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd)); - cmd->lun = 0; - cmd->request_bufflen = 255; - cmd->request_buffer = buffer; - cmd->sc_data_direction = SCSI_DATA_READ; - cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0; - cmd->bufflen = 0; - cmd->buffer = NULL; - cmd->underflow = 0; - cmd->cmd_len = 6; - cmd->cmnd[0] = cmd->data_cmnd[0] = INQUIRY; - cmd->cmnd[1] = cmd->data_cmnd[1] = 0; - cmd->cmnd[2] = cmd->data_cmnd[2] = 0; - cmd->cmnd[3] = cmd->data_cmnd[3] = 0; - cmd->cmnd[4] = cmd->data_cmnd[4] = 255; /* match what scsi.c does here */ - cmd->cmnd[5] = cmd->data_cmnd[5] = 0; - return(cmd); -} - -/*+F************************************************************************* - * Function: - * aic7xxx_negotiation_complete - * - * Description: - * Handle completion events for our Negotiation commands. Clear out the - * struct and get it ready for its next use. - *-F*************************************************************************/ -static void -aic7xxx_negotiation_complete(Scsi_Cmnd *cmd) -{ - unsigned int checksum; - int i; - int *ibuffer; - struct aic7xxx_host *p = (struct aic7xxx_host *)cmd->host->hostdata; - int tindex = TARGET_INDEX(cmd); - struct aic7xxx_syncrate *syncrate; - - /* - * perform our minimalistic domain validation - */ - if(p->dev_flags[tindex] & DEVICE_SCANNED) - { - ibuffer = (int *)cmd->request_buffer; - checksum = 0; - for(i = 0; i < (cmd->request_bufflen >> 2); i++) - { - checksum += ibuffer[i]; - } - if( (checksum != p->dev_checksum[tindex]) && - (p->transinfo[tindex].cur_offset != 0) ) - { - unsigned int period = p->transinfo[tindex].cur_period; - unsigned char options = p->transinfo[tindex].cur_options; - - if (p->needdv & (1<host_no, CTL_OF_CMD(cmd)); - } - if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL) - { - syncrate++; - if( (syncrate->rate[0] != NULL) && - (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) ) - { - p->transinfo[tindex].goal_period = syncrate->period; - if( p->transinfo[tindex].goal_period > 9 ) - { - p->transinfo[tindex].goal_options = 0; - p->needppr &= ~(1<needsdtr |= (1<needppr_copy &= ~(1<needsdtr_copy |= (1<transinfo[tindex].goal_width) - { - p->needwdtr |= (1<needwdtr_copy |= (1<transinfo[tindex].goal_width) - { - p->transinfo[tindex].goal_width = 0; - p->needwdtr &= ~(1<needwdtr_copy &= ~(1<transinfo[tindex].goal_offset = - p->transinfo[tindex].user_offset; - p->transinfo[tindex].goal_period = - p->transinfo[tindex].user_period; - p->transinfo[tindex].goal_options = - p->transinfo[tindex].user_options; - if( p->transinfo[tindex].goal_period <= 9 ) - { - p->needppr |= (1<needsdtr &= ~(1<needppr_copy |= (1<needsdtr_copy &= ~(1<needppr &= ~(1<needsdtr |= (1<needppr_copy &= ~(1<needsdtr_copy |= (1<transinfo[tindex].goal_offset = 0; - p->transinfo[tindex].goal_period = 255; - p->transinfo[tindex].goal_options = 0; - p->transinfo[tindex].goal_width = 0; - p->needppr &= ~(1<needsdtr &= ~(1<needwdtr &= ~(1<needppr_copy &= ~(1<needsdtr_copy &= ~(1<needwdtr_copy &= ~(1<needdv &= ~(1<host_no, CTL_OF_CMD(cmd)); - } - /* - * Update the checksum in case the INQUIRY data has changed, maybe - * in relation to a change in the mode pages, or whatever. - */ - p->dev_checksum[tindex] = checksum; - /* - * Signal that we are trying out the domain validation - */ - p->needdv |= (1<needppr |= (p->needppr_copy & (1<needsdtr |= (p->needsdtr_copy & (1<needwdtr |= (p->needwdtr_copy & (1<needdv & (1<host_no, CTL_OF_CMD(cmd)); - } - /* - * We successfully did our checksum, so don't leave the needdv flag set - * in case we might have set it last time through. - */ - p->needdv &= ~(1<dtr_pending &= ~(0x01 << tindex); - /* - * This looks recursive in the extreme, but if this was a WDTR negotiation - * and we didn't follow up with SDTR yet, then this will get it started. - * For all other cases, this should work out to be a no-op, unless we are - * doing domain validation and happen to need a new negotiation command. - * - * In case we don't want this to go any further, the cmdcmplt interrupt - * handler will NULL out the cmd->next entry so that the real SCSI command - * can be sent back to the mid layer code with SENSE data intact. We'll - * finish things up when the cmd gets sent back down to us, so no worries. - */ - if(cmd->next) - { - aic7xxx_build_negotiation_cmnd(p, cmd->next, tindex); - } - return; -} - -/*+F************************************************************************* - * Function: - * aic7xxx_build_negotiation_command - * - * Description: - * Build a Scsi_Cmnd structure to perform negotiation with or else send - * a pre-built command specifically for this purpose. - *-F*************************************************************************/ -static void -aic7xxx_build_negotiation_cmnd(struct aic7xxx_host *p, Scsi_Cmnd *old_cmd, - int tindex) -{ - - if ( !(p->dtr_pending & (1<needppr & (1<needwdtr & (1<needsdtr & (1<dev_dtr_cmnd[tindex] == NULL) && - (aic7xxx_allocate_negotiation_command(p, old_cmd, tindex) == NULL) ) - { - return; - } - /* - * Before sending this thing out, we also make the cmd->next pointer - * point to the real command so we can stuff any possible SENSE data - * into the real command instead of this fake command. This has to be - * done each time the command is built, not just the first time, hence - * it's outside of the above if()... - */ - p->dev_dtr_cmnd[tindex]->next = old_cmd; - /* - * Clear the buffer so checksums come out right.... - */ - memset(p->dev_dtr_cmnd[tindex]->request_buffer, 0, - p->dev_dtr_cmnd[tindex]->request_bufflen); - /* - * Remove any commands for this particular device that might be on the - * waiting_scbs queue or qinfifo so that this command goes out first. - * This is vital for our implementation of domain validation. - */ - pause_sequencer(p); - aic7xxx_search_qinfifo(p, old_cmd->target, old_cmd->channel, ALL_LUNS, - SCB_LIST_NULL, 0, TRUE, &p->delayed_scbs[tindex]); - unpause_sequencer(p, FALSE); - { - struct aic7xxx_scb *scb, *next; - - scb = p->waiting_scbs.head; - while(scb != NULL) - { - if( aic7xxx_match_scb(p, scb, old_cmd->target, old_cmd->channel, - ALL_LUNS, SCB_LIST_NULL) ) - { - next = scb->q_next; - scbq_remove(&p->waiting_scbs, scb); - scbq_insert_tail(&p->delayed_scbs[tindex], scb); - scb = next; - } - else - { - scb = scb->q_next; - } - } - } - aic7xxx_queue(p->dev_dtr_cmnd[tindex], - aic7xxx_negotiation_complete); - } -} - -#ifdef AIC7XXX_VERBOSE_DEBUGGING -/*+F************************************************************************* - * Function: - * aic7xxx_print_scb - * - * Description: - * Dump the byte codes for an about to be sent SCB. - *-F*************************************************************************/ -static void -aic7xxx_print_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) +static void +aic7xxx_print_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { int i; unsigned char *x; @@ -10877,7 +10684,7 @@ */ hscb->control = 0; scb->tag_action = 0; - cmd->tag = hscb->tag; + if (p->discenable & mask) { hscb->control |= DISCENB; @@ -10906,34 +10713,29 @@ } } } - if ( cmd == p->dev_dtr_cmnd[tindex] ) + if ( !(p->dtr_pending & mask) && + ( (p->needppr & mask) || + (p->needwdtr & mask) || + (p->needsdtr & mask) ) && + (p->dev_flags[tindex] & DEVICE_DTR_SCANNED) ) { p->dtr_pending |= mask; scb->tag_action = 0; - if (p->dev_flags[tindex] & DEVICE_SCANNED) + hscb->control &= DISCENB; + hscb->control |= MK_MESSAGE; + if(p->needppr & mask) { - hscb->control &= DISCENB; - hscb->control |= MK_MESSAGE; - if(p->needppr & mask) - { - scb->flags |= SCB_MSGOUT_PPR; - } - else if(p->needwdtr & mask) - { - scb->flags |= SCB_MSGOUT_WDTR; - } - else if(p->needsdtr & mask) - { - scb->flags |= SCB_MSGOUT_SDTR; - } + scb->flags |= SCB_MSGOUT_PPR; } - } - if ( !(p->dtr_pending & mask) && - ( (p->needppr & mask) || - (p->needwdtr & mask) || - (p->needsdtr & mask) ) ) - { - aic7xxx_build_negotiation_cmnd(p, cmd, tindex); + else if(p->needwdtr & mask) + { + scb->flags |= SCB_MSGOUT_WDTR; + } + else if(p->needsdtr & mask) + { + scb->flags |= SCB_MSGOUT_SDTR; + } + scb->flags |= SCB_DTR_SCB; } hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) | ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07); @@ -11079,50 +10881,58 @@ aic7xxx_allocate_scb(p); DRIVER_UNLOCK scb = scbq_remove_head(&p->scb_data->free_scbs); - } - if (scb == NULL) - { - printk(WARN_LEAD "Couldn't get a free SCB.\n", p->host_no, - CTL_OF_CMD(cmd)); - cmd->result = (DID_BUS_BUSY << 16); + if(scb == NULL) + printk(WARN_LEAD "Couldn't get a free SCB.\n", p->host_no, + CTL_OF_CMD(cmd)); + } + while (scb == NULL) + { + /* + * Well, all SCBs are currently active on the bus. So, we spin here + * running the interrupt handler until one completes and becomes free. + * We can do this safely because we either A) hold the driver lock (in + * 2.0 kernels) or we have the io_request_lock held (in 2.2 and later + * kernels) and so either way, we won't take any other interrupts and + * the queue path will block until we release it. Also, we would worry + * about running the completion queues, but obviously there are plenty + * of commands outstanding to trigger a later interrupt that will do + * that for us, so skip it here. + */ DRIVER_LOCK - aic7xxx_queue_cmd_complete(p, cmd); + aic7xxx_isr(p->irq, p, NULL); DRIVER_UNLOCK - return 0; + scb = scbq_remove_head(&p->scb_data->free_scbs); } - else - { - scb->cmd = cmd; - aic7xxx_position(cmd) = scb->hscb->tag; + scb->cmd = cmd; + aic7xxx_position(cmd) = scb->hscb->tag; - /* - * Construct the SCB beforehand, so the sequencer is - * paused a minimal amount of time. - */ - aic7xxx_buildscb(p, cmd, scb); + /* + * Make sure the Scsi_Cmnd pointer is saved, the struct it points to + * is set up properly, and the parity error flag is reset, then send + * the SCB to the sequencer and watch the fun begin. + */ + cmd->scsi_done = fn; + cmd->result = DID_OK; + memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); + aic7xxx_error(cmd) = DID_OK; + aic7xxx_status(cmd) = 0; + cmd->host_scribble = NULL; - /* - * Make sure the Scsi_Cmnd pointer is saved, the struct it points to - * is set up properly, and the parity error flag is reset, then send - * the SCB to the sequencer and watch the fun begin. - */ - cmd->scsi_done = fn; - cmd->result = DID_OK; - memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); - aic7xxx_error(cmd) = DID_OK; - aic7xxx_status(cmd) = 0; - cmd->host_scribble = NULL; + /* + * Construct the SCB beforehand, so the sequencer is + * paused a minimal amount of time. + */ + aic7xxx_buildscb(p, cmd, scb); - scb->flags |= SCB_ACTIVE | SCB_WAITINGQ; + scb->flags |= SCB_ACTIVE | SCB_WAITINGQ; - DRIVER_LOCK - scbq_insert_tail(&p->waiting_scbs, scb); - if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0) - { - aic7xxx_run_waiting_queues(p); - } - DRIVER_UNLOCK + DRIVER_LOCK + scbq_insert_tail(&p->waiting_scbs, scb); + if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0) + { + aic7xxx_run_waiting_queues(p); } + DRIVER_UNLOCK return (0); } @@ -11188,6 +10998,12 @@ aic_inb(p, SCSISIGI), aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8), aic_inb(p, SSTAT0), aic_inb(p, SSTAT1)); + printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n", p->host_no, + CTL_OF_SCB(scb), + (p->features & AHC_ULTRA2) ? aic_inb(p, SG_CACHEPTR) : 0, + aic_inb(p, SSTAT2), + aic_inb(p, STCNT + 2) << 16 | aic_inb(p, STCNT + 1) << 8 | + aic_inb(p, STCNT)); } channel = cmd->channel; @@ -11358,7 +11174,6 @@ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) unsigned long cpu_flags = 0; #endif - Scsi_Cmnd *cmd_next, *cmd_prev; p = (struct aic7xxx_host *) cmd->host->hostdata; scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]); @@ -11367,7 +11182,7 @@ * I added a new config option to the driver: "panic_on_abort" that will * cause the driver to panic and the machine to stop on the first abort * or reset call into the driver. At that point, it prints out a lot of - * usefull information for me which I can then use to try and debug the + * useful information for me which I can then use to try and debug the * problem. Simply enable the boot time prompt in order to activate this * code. */ @@ -11388,13 +11203,11 @@ { aic7xxx_isr(p->irq, p, (void *)NULL); pause_sequencer(p); - aic7xxx_done_cmds_complete(p); } + aic7xxx_done_cmds_complete(p); - if ((scb == NULL) || (cmd->serial_number != cmd->serial_number_at_timeout)) - /* Totally bogus cmd since it points beyond our */ - { /* valid SCB range or doesn't even match it's own*/ - /* timeout serial number. */ + if (scb == NULL) + { if (aic7xxx_verbose & VERBOSE_ABORT_MID) printk(INFO_LEAD "Abort called with bogus Scsi_Cmnd " "pointer.\n", p->host_no, CTL_OF_CMD(cmd)); @@ -11413,28 +11226,6 @@ /* finish successfully, or to indicate that we */ /* don't have this cmd any more and the mid level */ /* code needs to find it. */ - cmd_next = p->completeq.head; - cmd_prev = NULL; - while (cmd_next != NULL) - { - if (cmd_next == cmd) - { - if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) - printk(INFO_LEAD "Abort called for command " - "on completeq, completing.\n", p->host_no, CTL_OF_CMD(cmd)); - if ( cmd_prev == NULL ) - p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble; - else - cmd_prev->host_scribble = cmd_next->host_scribble; - cmd_next->scsi_done(cmd_next); - unpause_sequencer(p, FALSE); - DRIVER_UNLOCK - return(SCSI_ABORT_NOT_RUNNING); /* It's already back as a successful - * completion */ - } - cmd_prev = cmd_next; - cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble; - } if (aic7xxx_verbose & VERBOSE_ABORT_MID) printk(INFO_LEAD "Abort called for already completed" " command.\n", p->host_no, CTL_OF_CMD(cmd)); @@ -11492,8 +11283,20 @@ found = 0; p->flags |= AHC_IN_ABORT; if (aic7xxx_verbose & VERBOSE_ABORT) - printk(INFO_LEAD "Aborting scb %d, flags 0x%x\n", - p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags); + { + printk(INFO_LEAD "Aborting scb %d, flags 0x%x, SEQADDR 0x%x, LASTPHASE " + "0x%x\n", + p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags, + aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8), + aic_inb(p, LASTPHASE)); + printk(INFO_LEAD "SG_CACHEPTR 0x%x, SG_COUNT %d, SCSISIGI 0x%x\n", + p->host_no, CTL_OF_SCB(scb), (p->features & AHC_ULTRA2) ? + aic_inb(p, SG_CACHEPTR) : 0, aic_inb(p, SG_COUNT), + aic_inb(p, SCSISIGI)); + printk(INFO_LEAD "SSTAT0 0x%x, SSTAT1 0x%x, SSTAT2 0x%x\n", + p->host_no, CTL_OF_SCB(scb), aic_inb(p, SSTAT0), + aic_inb(p, SSTAT1), aic_inb(p, SSTAT2)); + } /* * First, let's check to see if the currently running command is our target @@ -11521,6 +11324,16 @@ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) printk(INFO_LEAD "SCB is currently active. " "Waiting on completion.\n", p->host_no, CTL_OF_SCB(scb)); + printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 " + "0x%x\n", p->host_no, CTL_OF_SCB(scb), + aic_inb(p, SCSISIGI), + aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8), + aic_inb(p, SSTAT0), aic_inb(p, SSTAT1)); + printk(INFO_LEAD "SG_CACHEPTR 0x%x, SSTAT2 0x%x, STCNT 0x%x\n", + p->host_no, CTL_OF_SCB(scb), + (p->features & AHC_ULTRA2) ? aic_inb(p, SG_CACHEPTR) : 0, + aic_inb(p, SSTAT2), aic_inb(p, STCNT + 2) << 16 | + aic_inb(p, STCNT + 1) << 8 | aic_inb(p, STCNT)); unpause_sequencer(p, FALSE); p->flags &= ~AHC_IN_ABORT; scb->flags |= SCB_RECOVERY_SCB; /* Note the fact that we've been */ @@ -11536,35 +11349,7 @@ if ((found == 0) && (scb->flags & SCB_WAITINGQ)) { int tindex = TARGET_INDEX(cmd); - unsigned short mask; - - mask = (1 << tindex); - if (p->dtr_pending & mask) - { - if (p->dev_dtr_cmnd[tindex]->next != cmd) - found = 1; - else - found = 0; - } - else - { - found = 1; - } - if (found == 0) - { - /* - * OK..this means the command we are currently getting an abort - * for has an outstanding negotiation command in front of it. - * We don't really have a way to tie back into the negotiation - * commands, so we just send this back as pending, then it - * will get reset in 2 seconds. - */ - unpause_sequencer(p, TRUE); - scb->flags |= SCB_ABORT; - DRIVER_UNLOCK - return(SCSI_ABORT_PENDING); - } if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) printk(INFO_LEAD "SCB found on waiting list and " "aborted.\n", p->host_no, CTL_OF_SCB(scb)); @@ -11721,10 +11506,8 @@ #define DEVICE_RESET 0x01 #define BUS_RESET 0x02 #define HOST_RESET 0x04 -#define FAIL 0x08 -#define RESET_DELAY 0x10 +#define RESET_DELAY 0x08 int action; - Scsi_Cmnd *cmd_prev, *cmd_next; if ( cmd == NULL ) @@ -11742,7 +11525,7 @@ * I added a new config option to the driver: "panic_on_abort" that will * cause the driver to panic and the machine to stop on the first abort * or reset call into the driver. At that point, it prints out a lot of - * usefull information for me which I can then use to try and debug the + * useful information for me which I can then use to try and debug the * problem. Simply enable the boot time prompt in order to activate this * code. */ @@ -11752,86 +11535,32 @@ DRIVER_LOCK pause_sequencer(p); - while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR)) - { - aic7xxx_isr(p->irq, p, (void *)NULL ); - pause_sequencer(p); - aic7xxx_done_cmds_complete(p); - } - if (scb == NULL) + if(flags & SCSI_RESET_SYNCHRONOUS) { if (aic7xxx_verbose & VERBOSE_RESET_MID) - printk(INFO_LEAD "Reset called with bogus Scsi_Cmnd" - "->SCB mapping, improvising.\n", p->host_no, CTL_OF_CMD(cmd)); - if ( flags & SCSI_RESET_SUGGEST_HOST_RESET ) - { - action = HOST_RESET; - } - else - { - action = BUS_RESET; - } + printk(INFO_LEAD "Reset called for a SYNCHRONOUS reset, flags 0x%x, " + "cmd->result 0x%x.\n", p->host_no, CTL_OF_CMD(cmd), flags, + cmd->result); + scb = NULL; + action = HOST_RESET; } - else if (scb->cmd != cmd) + else if ((scb == NULL) || (scb->cmd != cmd)) { if (aic7xxx_verbose & VERBOSE_RESET_MID) - printk(INFO_LEAD "Reset called with recycled SCB " - "for cmd.\n", p->host_no, CTL_OF_CMD(cmd)); - cmd_prev = NULL; - cmd_next = p->completeq.head; - while ( cmd_next != NULL ) - { - if (cmd_next == cmd) - { - if (aic7xxx_verbose & VERBOSE_RESET_RETURN) - printk(INFO_LEAD "Reset, found cmd on completeq" - ", completing.\n", p->host_no, CTL_OF_CMD(cmd)); - unpause_sequencer(p, FALSE); - DRIVER_UNLOCK - return(SCSI_RESET_NOT_RUNNING); - } - cmd_prev = cmd_next; - cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble; - } - if ( !(flags & SCSI_RESET_SYNCHRONOUS) ) - { - if (aic7xxx_verbose & VERBOSE_RESET_RETURN) - printk(INFO_LEAD "Reset, cmd not found," - " failing.\n", p->host_no, CTL_OF_CMD(cmd)); - unpause_sequencer(p, FALSE); - DRIVER_UNLOCK - return(SCSI_RESET_NOT_RUNNING); - } - else - { - if (aic7xxx_verbose & VERBOSE_RESET_MID) - printk(INFO_LEAD "Reset called, no scb, " - "flags 0x%x\n", p->host_no, CTL_OF_CMD(cmd), flags); - scb = NULL; - action = HOST_RESET; - } + printk(INFO_LEAD "Reset called with bogus Scsi_Cmnd" + "->SCB mapping, failing.\n", p->host_no, CTL_OF_CMD(cmd)); + aic7xxx_done_cmds_complete(p); + aic7xxx_run_waiting_queues(p); + unpause_sequencer(p, FALSE); + DRIVER_UNLOCK + return(SCSI_RESET_NOT_RUNNING); } else { if (aic7xxx_verbose & VERBOSE_RESET_MID) printk(INFO_LEAD "Reset called, scb %d, flags " "0x%x\n", p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags); - if ( aic7xxx_scb_on_qoutfifo(p, scb) ) - { - if(aic7xxx_verbose & VERBOSE_RESET_RETURN) - printk(INFO_LEAD "SCB on qoutfifo, completing.\n", p->host_no, - CTL_OF_SCB(scb)); - if ((aic_inb(p,INTSTAT) & CMDCMPLT) == 0) - printk(INFO_LEAD "missed CMDCMPLT interrupt!\n", p->host_no, - CTL_OF_SCB(scb)); - aic7xxx_handle_command_completion_intr(p); - aic7xxx_done_cmds_complete(p); - aic7xxx_run_waiting_queues(p); - unpause_sequencer(p, FALSE); - DRIVER_UNLOCK - return(SCSI_RESET_SUCCESS); - } if ( flags & SCSI_RESET_SUGGEST_HOST_RESET ) { action = HOST_RESET; @@ -11845,6 +11574,26 @@ action = DEVICE_RESET; } } + + while((aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR)) + { + aic7xxx_isr(p->irq, p, (void *)NULL ); + pause_sequencer(p); + } + aic7xxx_done_cmds_complete(p); + + if(scb && (scb->cmd == NULL)) + { + /* + * We just completed the command when we ran the isr stuff, so we no + * longer have it. + */ + aic7xxx_run_waiting_queues(p); + unpause_sequencer(p, FALSE); + DRIVER_UNLOCK + return(SCSI_RESET_SUCCESS); + } + if ( (action & DEVICE_RESET) && (p->dev_flags[tindex] & BUS_DEVICE_RESET_PENDING) ) { @@ -11904,14 +11653,13 @@ switch (action) { case RESET_DELAY: + aic7xxx_run_waiting_queues(p); unpause_sequencer(p, FALSE); DRIVER_UNLOCK - return(SCSI_RESET_PENDING); - break; - case FAIL: - unpause_sequencer(p, FALSE); - DRIVER_UNLOCK - return(SCSI_RESET_ERROR); + if(scb == NULL) + return(SCSI_RESET_PUNT); + else + return(SCSI_RESET_PENDING); break; case DEVICE_RESET: p->flags |= AHC_IN_RESET; @@ -11929,7 +11677,7 @@ case HOST_RESET: default: p->flags |= AHC_IN_RESET | AHC_RESET_DELAY; - p->dev_expires[p->scsi_id] = jiffies + (3 * HZ); + p->dev_expires[p->scsi_id] = jiffies + (1 * HZ); p->dev_timer_active |= (0x01 << p->scsi_id); if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) || time_after_eq(p->dev_timer.expires, p->dev_expires[p->scsi_id]) ) @@ -11956,21 +11704,14 @@ p->msg_index = 0; p->msg_len = 0; } - aic7xxx_run_done_queue(p, TRUE); - /* - * If this a SCSI_RESET_SYNCHRONOUS then the command we were given is - * in need of being re-started, so send it on through to aic7xxx_queue - * and let it set until the delay is over. This keeps it from dying - * entirely and avoids getting a bogus dead command back through the - * mid-level code due to too many retries. - */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,132) - if ( flags & SCSI_RESET_SYNCHRONOUS ) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) + if(flags & SCSI_RESET_SYNCHRONOUS) { - cmd->result = DID_BUS_BUSY << 16; + cmd->result = DID_RESET << 16; cmd->done(cmd); } #endif + aic7xxx_run_done_queue(p, TRUE); p->flags &= ~AHC_IN_RESET; /* * We can't rely on run_waiting_queues to unpause the sequencer for @@ -11981,7 +11722,10 @@ aic7xxx_run_waiting_queues(p); unpause_sequencer(p, FALSE); DRIVER_UNLOCK - return(result); + if(scb == NULL) + return(SCSI_RESET_SUCCESS|SCSI_RESET_HOST_RESET); + else + return(result); break; } } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/dec_esp.c linux.ac/drivers/scsi/dec_esp.c --- linux.vanilla/drivers/scsi/dec_esp.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/scsi/dec_esp.c Tue Apr 3 17:55:03 2001 @@ -87,7 +87,7 @@ unsigned char scsi_pmaz_dma_buff_used[ESP_NCMD]; unsigned char scsi_cur_buff = 1; /* Leave space for command buffer */ __u32 esp_virt_buffer; -int scsi_current_length = 0; +int scsi_current_length; volatile unsigned char cmd_buffer[16]; volatile unsigned char pmaz_cmd_buffer[16]; @@ -181,10 +181,13 @@ esp->esp_command_dvma = (__u32) KSEG1ADDR((volatile unsigned char *) cmd_buffer); esp->irq = SCSI_INT; - request_irq(esp->irq, esp_intr, SA_INTERRUPT, "NCR 53C94 SCSI", - NULL); - request_irq(SCSI_DMA_INT, scsi_dma_int, SA_INTERRUPT, "JUNKIO SCSI DMA", - NULL); + if (request_irq(esp->irq, esp_intr, SA_INTERRUPT, + "NCR 53C94 SCSI", NULL)) + goto err_dealloc; + if (request_irq(SCSI_DMA_INT, scsi_dma_int, SA_INTERRUPT, + "JUNKIO SCSI DMA", NULL)) + goto err_free_irq; + esp->scsi_id = 7; @@ -257,7 +260,12 @@ esp->dma_mmu_release_scsi_sgl = 0; esp->dma_advance_sg = 0; - request_irq(esp->irq, esp_intr, SA_INTERRUPT, "PMAZ_AA", NULL); + if (request_irq(esp->irq, esp_intr, SA_INTERRUPT, + "PMAZ_AA", NULL)) { + esp_deallocate(esp); + release_tc_card(slot); + continue; + } esp->scsi_id = 7; esp->diff = 0; esp_initialize(esp); @@ -267,10 +275,16 @@ if(nesps) { printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, esps_in_use); - esps_running = esps_in_use; - return esps_in_use; - } else - return 0; + esps_running = esps_in_use; + return esps_in_use; + } + return 0; + + err_free_irq: + free_irq(esp->irq, esp_intr); + err_dealloc: + esp_deallocate(esp); + return 0; } /************************************************************* DMA Functions */ @@ -524,4 +538,4 @@ (char *) KSEG0ADDR((sp->request_buffer)); } -#endif \ No newline at end of file +#endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/dmx3191d.c linux.ac/drivers/scsi/dmx3191d.c --- linux.vanilla/drivers/scsi/dmx3191d.c Mon Apr 30 15:13:24 2001 +++ linux.ac/drivers/scsi/dmx3191d.c Tue May 22 10:22:23 2001 @@ -59,7 +59,7 @@ struct pci_dev *pdev = NULL; if (!pci_present()) { - dmx3191d_printk("PCI support not enabled\n"); + printk(KERN_WARNING "dmx3191: PCI support not enabled\n"); return 0; } @@ -75,7 +75,7 @@ port = pci_resource_start (pdev, 0); if (!request_region(port, DMX3191D_REGION, DMX3191D_DRIVER_NAME)) { - dmx3191d_printk("region 0x%lx-0x%lx already reserved\n", + printk(KERN_ERR "dmx3191: region 0x%lx-0x%lx already reserved\n", port, port + DMX3191D_REGION); continue; } @@ -93,7 +93,7 @@ if (request_irq(pdev->irq, dmx3191d_do_intr, SA_SHIRQ, DMX3191D_DRIVER_NAME, instance)) { - dmx3191d_printk("irq %d not available\n", pdev->irq); + printk(KERN_WARNING "dmx3191: IRQ %d not available - switching to polled mode.\n", pdev->irq); /* Steam powered scsi controllers run without an IRQ anyway */ instance->irq = IRQ_NONE; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/dmx3191d.h linux.ac/drivers/scsi/dmx3191d.h --- linux.vanilla/drivers/scsi/dmx3191d.h Mon Sep 18 22:12:01 2000 +++ linux.ac/drivers/scsi/dmx3191d.h Tue May 22 10:21:15 2001 @@ -20,8 +20,6 @@ #define PCI_DEVICE_ID_DOMEX_DMX3191D 0x0001 #endif -#define dmx3191d_printk( args... ) printk(__FILE__": " ##args) - #ifndef ASM int dmx3191d_abort(Scsi_Cmnd *); int dmx3191d_detect(Scsi_Host_Template *); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/hosts.c linux.ac/drivers/scsi/hosts.c --- linux.vanilla/drivers/scsi/hosts.c Mon Oct 30 22:44:29 2000 +++ linux.ac/drivers/scsi/hosts.c Tue Apr 3 17:55:03 2001 @@ -232,6 +232,7 @@ retval->use_clustering = tpnt->use_clustering; retval->select_queue_depths = tpnt->select_queue_depths; + retval->max_sectors = tpnt->max_sectors; if(!scsi_hostlist) scsi_hostlist = retval; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/hosts.h linux.ac/drivers/scsi/hosts.h --- linux.vanilla/drivers/scsi/hosts.h Mon Apr 30 15:13:24 2001 +++ linux.ac/drivers/scsi/hosts.h Sat May 26 17:36:18 2001 @@ -243,6 +243,11 @@ short unsigned int sg_tablesize; /* + * if the host adapter has limitations beside segment count + */ + short unsigned int max_sectors; + + /* * True if this host adapter can make good use of linked commands. * This will allow more than one command to be queued to a given * unit on a given host. Set this to the maximum number of command @@ -380,6 +385,7 @@ int can_queue; short cmd_per_lun; short unsigned int sg_tablesize; + short unsigned int max_sectors; unsigned in_recovery:1; unsigned unchecked_isa_dma:1; @@ -523,7 +529,7 @@ /* These are used by loadable modules */ extern int scsi_register_module(int, void *); -extern void scsi_unregister_module(int, void *); +extern int scsi_unregister_module(int, void *); /* The different types of modules that we can load and unload */ #define MODULE_SCSI_HA 1 diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/i91uscsi.c linux.ac/drivers/scsi/i91uscsi.c --- linux.vanilla/drivers/scsi/i91uscsi.c Tue Apr 3 17:32:20 2001 +++ linux.ac/drivers/scsi/i91uscsi.c Tue Apr 3 17:55:03 2001 @@ -590,7 +590,7 @@ int init_tulip(HCS * pCurHcb, SCB * scbp, int tul_num_scb, BYTE * pbBiosAdr, int seconds) { int i; - WORD *pwFlags; + BYTE *pwFlags; BYTE *pbHeads; SCB *pTmpScb, *pPrevScb = NULL; @@ -674,7 +674,7 @@ ((pCurHcb->HCS_Config & HCC_AUTO_TERM) >> 4) | (TUL_RD(pCurHcb->HCS_Base, TUL_GCTRL1) & 0xFE)); for (i = 0, - pwFlags = (WORD *) & (i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config), + pwFlags = & (i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config), pbHeads = pbBiosAdr + 0x180; i < pCurHcb->HCS_MaxTar; i++, pwFlags++) { diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/ncr53c8xx.c linux.ac/drivers/scsi/ncr53c8xx.c --- linux.vanilla/drivers/scsi/ncr53c8xx.c Mon Apr 30 15:13:24 2001 +++ linux.ac/drivers/scsi/ncr53c8xx.c Thu May 24 00:02:48 2001 @@ -103,7 +103,7 @@ /* ** Name and version of the driver */ -#define SCSI_NCR_DRIVER_NAME "ncr53c8xx-3.4.3-20010212" +#define SCSI_NCR_DRIVER_NAME "ncr53c8xx-3.4.3b-20010512" #define SCSI_NCR_DEBUG_FLAGS (0) @@ -3082,7 +3082,7 @@ }; /* -** Print something which allows to retrieve the controler type, unit, +** Print something which allows to retrieve the controller type, unit, ** target, lun concerned by a kernel message. */ @@ -4302,7 +4302,7 @@ ** **---------------------------------------------------- */ -#if 0 /* This stuff was only usefull for linux-1.2.13 */ +#if 0 /* This stuff was only useful for linux-1.2.13 */ if (lp && !lp->numtags && cmd->device && cmd->device->tagged_queue) { lp->numtags = tp->usrtags; ncr_setup_tags (np, cmd->target, cmd->lun); @@ -5701,7 +5701,7 @@ */ fak = (kpc - 1) / div_10M[div] + 1; -#if 0 /* This optimization does not seem very usefull */ +#if 0 /* This optimization does not seem very useful */ per = (fak * div_10M[div]) / clk; @@ -6647,7 +6647,7 @@ delta=(INB (nc_dfifo) - rest) & 0x7f; /* - ** The data in the dma fifo has not been transfered to + ** The data in the dma fifo has not been transferred to ** the target -> add the amount to the rest ** and clear the data. ** Check the sstat2 register in case of wide transfer. @@ -7153,7 +7153,7 @@ ** Was Sie schon immer ueber transfermode negotiation wissen wollten ... ** ** We try to negotiate sync and wide transfer only after -** a successfull inquire command. We look at byte 7 of the +** a successful inquire command. We look at byte 7 of the ** inquire data to determine the capabilities of the target. ** ** When we try to negotiate, we append the negotiation message @@ -7550,7 +7550,7 @@ /*========================================================== ** ** -** Aquire a control block +** Acquire a control block ** ** **========================================================== diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/osst.c linux.ac/drivers/scsi/osst.c --- linux.vanilla/drivers/scsi/osst.c Thu Jan 4 21:00:55 2001 +++ linux.ac/drivers/scsi/osst.c Tue Apr 3 17:55:03 2001 @@ -318,7 +318,8 @@ } } - cmd[1] |= (SRpnt->sr_device->lun << 5) & 0xe0; + if (SRpnt->sr_device->scsi_level <= SCSI_2) + cmd[1] |= (SRpnt->sr_device->lun << 5) & 0xe0; init_MUTEX_LOCKED(&STp->sem); SRpnt->sr_use_sg = (bytes > (STp->buffer)->sg[0].length) ? (STp->buffer)->use_sg : 0; @@ -679,7 +680,7 @@ result = osst_get_frame_position (STp, aSRpnt); if (result == -EIO) if ((result = osst_write_error_recovery(STp, aSRpnt, 0)) == 0) - return 0; /* successfull recovery leaves drive ready for frame */ + return 0; /* successful recovery leaves drive ready for frame */ if (result < 0) break; if (STp->first_frame_position == curr && ((minlast < 0 && diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/osst.h linux.ac/drivers/scsi/osst.h --- linux.vanilla/drivers/scsi/osst.h Sat Dec 30 19:23:14 2000 +++ linux.ac/drivers/scsi/osst.h Sat May 26 18:12:14 2001 @@ -591,7 +591,7 @@ unsigned char logical_blk_in_buffer; /* flag that the block as per logical_blk_num * has been read into STp->buffer and is valid */ int logical_blk_num; /* logical block number */ - unsigned first_frame_position; /* physical frame to be transfered to/from host */ + unsigned first_frame_position; /* physical frame to be transferred to/from host */ unsigned last_frame_position; /* physical frame to be transferd to/from tape */ int cur_frames; /* current number of frames in internal buffer */ int max_frames; /* max number of frames in internal buffer */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pci2220i.c linux.ac/drivers/scsi/pci2220i.c --- linux.vanilla/drivers/scsi/pci2220i.c Sat May 26 16:53:13 2001 +++ linux.ac/drivers/scsi/pci2220i.c Wed May 2 14:15:46 2001 @@ -2393,7 +2393,7 @@ padapter->regRemap = zr + RTR_LOCAL_REMAP; // 32 bit local space remap padapter->regDesc = zr + RTR_REGIONS; // 32 bit local region descriptor padapter->regRange = zr + RTR_LOCAL_RANGE; // 32 bit local range - padapter->regIrqControl = zr + RTR_INT_CONTROL_STATUS; // 16 bit interupt control and status + padapter->regIrqControl = zr + RTR_INT_CONTROL_STATUS; // 16 bit interrupt control and status padapter->regScratchPad = zr + RTR_MAILBOX; // 16 byte scratchpad I/O base address padapter->regBase = zl; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/Config.in linux.ac/drivers/scsi/pcmcia/Config.in --- linux.vanilla/drivers/scsi/pcmcia/Config.in Tue Apr 3 17:32:21 2001 +++ linux.ac/drivers/scsi/pcmcia/Config.in Tue May 22 18:42:45 2001 @@ -8,12 +8,14 @@ bool 'PCMCIA SCSI adapter support' CONFIG_SCSI_PCMCIA if [ "$CONFIG_SCSI_PCMCIA" = "y" ]; then dep_tristate ' Adaptec AHA152X PCMCIA support' CONFIG_PCMCIA_AHA152X m - dep_tristate ' Qlogic PCMCIA support' CONFIG_PCMCIA_QLOGIC m dep_tristate ' Future Domain PCMCIA support' CONFIG_PCMCIA_FDOMAIN m + dep_tristate ' NinjaSCSI-3 / NinjaSCSI-32Bi (16bit) PCMCIA support' CONFIG_PCMCIA_NINJA_SCSI m + dep_tristate ' Qlogic PCMCIA support' CONFIG_PCMCIA_QLOGIC m fi if [ "$CONFIG_PCMCIA_QLOGIC" = "y" -o "$CONFIG_PCMCIA_AHA152X" = "y" -o \ - "$CONFIG_PCMCIA_FDOMAIN" = "y" ]; then + "$CONFIG_PCMCIA_FDOMAIN" = "y" -o "$CONFIG_PCMCIA_APA1480" = "y" -o \ + "$CONFIG_PCMCIA_NINJA_SCSI" ]; then define_bool CONFIG_PCMCIA_SCSICARD y fi diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/Makefile linux.ac/drivers/scsi/pcmcia/Makefile --- linux.vanilla/drivers/scsi/pcmcia/Makefile Tue Apr 3 17:32:21 2001 +++ linux.ac/drivers/scsi/pcmcia/Makefile Sat May 26 19:32:50 2001 @@ -19,6 +19,7 @@ obj-$(CONFIG_PCMCIA_QLOGIC) += qlogic_cs.o obj-$(CONFIG_PCMCIA_FDOMAIN) += fdomain_cs.o obj-$(CONFIG_PCMCIA_AHA152X) += aha152x_cs.o +obj-$(CONFIG_PCMCIA_NINJA_SCSI) += nsp_cs.o list-multi := qlogic_cs.o fdomain_cs.o aha152x_cs.o aha152x_cs-objs := aha152x_stub.o aha152x.o diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/nsp_cs.c linux.ac/drivers/scsi/pcmcia/nsp_cs.c --- linux.vanilla/drivers/scsi/pcmcia/nsp_cs.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/pcmcia/nsp_cs.c Tue Apr 3 17:55:04 2001 @@ -0,0 +1,1714 @@ +/*====================================================================== + + NinjaSCSI-3 / NinjaSCSI-32Bi PCMCIA SCSI hostadapter card driver + By: YOKOTA Hiroshi + + Ver.2.0 Support 32bit PIO mode + Ver.1.1.2 Fix for scatter list buffer exceeds + Ver.1.1 Support scatter list + Ver.0.1 Initial version + + This software may be used and distributed according to the terms of + the GNU General Public License. + +======================================================================*/ + +/*********************************************************************** + This driver is for these PCcards. + + I-O DATA PCSC-F (Workbit NinjaSCSI-3) + "WBT", "NinjaSCSI-3", "R1.0" + I-O DATA CBSC-II (Workbit NinjaSCSI-32Bi in 16bit mode) + "IO DATA", "CBSC16 ", "1" + +***********************************************************************/ + +/* $Id: nsp_cs.c,v 1.28 2001/02/15 02:56:32 elca Exp $ */ + +#ifdef NSP_KERNEL_2_2 +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include <../drivers/scsi/scsi.h> +#include <../drivers/scsi/hosts.h> +#include <../drivers/scsi/sd.h> + +#include +#include + +#include +#include +#include +#include +#include + +#include "nsp_cs.h" + +MODULE_AUTHOR("YOKOTA Hiroshi "); +MODULE_DESCRIPTION("WorkBit NinjaSCSI-3 / NinjaSCSI-32Bi(16bit) PCMCIA SCSI host adapter module"); +MODULE_SUPPORTED_DEVICE("sd,sr,sg,st"); + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +MODULE_PARM_DESC(pc_debug, "set debug level"); +static char *version = "$Id: nsp_cs.c,v 1.28 2001/02/15 02:56:32 elca Exp $"; +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +#else +#define DEBUG(n, args...) /* */ +#endif + +#include "nsp_io.h" + +/*====================================================================*/ + +typedef struct scsi_info_t { + dev_link_t link; + struct Scsi_Host *host; + int ndev; + dev_node_t node[8]; + int stop; + struct bus_operations *bus; +} scsi_info_t; + +static void nsp_release(u_long arg); +static int nsp_event(event_t event, int priority, + event_callback_args_t *args); +static dev_link_t *nsp_attach(void); +static void nsp_detach(dev_link_t *); +static int nsp_detect(Scsi_Host_Template * ); +static const char * nsp_info(struct Scsi_Host *); +static int nsp_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); +static int nsp_abort(Scsi_Cmnd *); +static int nsp_reset(Scsi_Cmnd *, unsigned int); + + +/*----------------------------------------------------------------*/ + +#if (KERNEL_VERSION(2,4,0) > LINUX_VERSION_CODE) +#define PROC_SCSI_NSP PROC_SCSI_IBMMCA /* bad hack... */ +static struct proc_dir_entry proc_scsi_nsp = { + PROC_SCSI_NSP, 6, "nsp_cs", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; +#endif + +/*====================================================================*/ +/* Parameters that can be set with 'insmod' */ + +static unsigned int irq_mask = 0xffff; +MODULE_PARM(irq_mask, "i"); +MODULE_PARM_DESC(irq_mask, "IRQ mask bits"); + +static int irq_list[4] = { -1 }; +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM_DESC(irq_list, "IRQ number list"); + +/*----------------------------------------------------------------*/ +/* driver state info, local to driver */ +static char nspinfo[100]; /* description */ + +/* /usr/src/linux/drivers/scsi/hosts.h */ +static Scsi_Host_Template driver_template = { +/* next: NULL,*/ +/* proc_dir: NULL,*/ +/* proc_info: NULL,*/ + name: "WorkBit NinjaSCSI-3/32Bi", + detect: nsp_detect, +/* release: NULL,*/ + info: nsp_info, +/* command: NULL,*/ + queuecommand: nsp_queuecommand, +/* eh_strategy_handler: nsp_eh_strategy,*/ +/* eh_abort_handler: nsp_eh_abort,*/ +/* eh_device_reset_handler: nsp_eh_device_reset,*/ + eh_bus_reset_handler: nsp_eh_bus_reset, +/* eh_host_reset_handler: nsp_eh_host_reset,*/ + abort: nsp_abort, + reset: nsp_reset, +/* slave_attach: NULL,*/ +/* bios_param: nsp_biosparam,*/ + can_queue: 1, + this_id: -1, + sg_tablesize: SG_ALL, + cmd_per_lun: 1, +/* present: 0,*/ +/* unchecked_isa_dma: 0,*/ + use_clustering: DISABLE_CLUSTERING, + use_new_eh_code: 0, +/* emulated: 0,*/ +}; + +static dev_link_t *dev_list = NULL; +static dev_info_t dev_info = {"nsp_cs"}; + +static nsp_hw_data nsp_data; + +/***********************************************************/ + +static int nsp_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +{ +#ifdef PCMCIA_DEBUG + //unsigned int host_id = SCpnt->host->this_id; + //unsigned int base = SCpnt->host->io_port; + unsigned char target = SCpnt->target; +#endif + + DEBUG(0, __FUNCTION__ "() SCpnt=0x%p target=%d lun=%d buff=0x%p bufflen=%d use_sg=%d\n", + SCpnt, target, SCpnt->lun, SCpnt->request_buffer, SCpnt->request_bufflen, SCpnt->use_sg); + //DEBUG(0, " before CurrentSC=0x%p\n", nsp_data.CurrentSC); + + if(nsp_data.CurrentSC != NULL) { + printk(KERN_DEBUG " " __FUNCTION__ "() CurrentSC!=NULL this can't be happen\n"); + nsp_data.CurrentSC = NULL; + SCpnt->result = DID_BAD_TARGET << 16; + done(SCpnt); + return FAILED; + } + +#ifdef PCMCIA_DEBUG + show_command(SCpnt); +#endif + + SCpnt->scsi_done = done; + nsp_data.CurrentSC = SCpnt; + RESID = SCpnt->request_bufflen; + + SCpnt->SCp.Status = -1; + SCpnt->SCp.Message = -1; + SCpnt->SCp.have_data_in = IO_UNKNOWN; + SCpnt->SCp.sent_command = 0; + SCpnt->SCp.phase = PH_UNDETERMINED; + + /* setup scratch area + SCp.ptr : buffer pointer + SCp.this_residual : buffer length + SCp.buffer : next buffer + SCp.buffers_residual : left buffers in list + SCp.phase : current state of the command */ + if (SCpnt->use_sg) { + SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->request_buffer; + SCpnt->SCp.ptr = SCpnt->SCp.buffer->address; + SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; + SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; + } else { + SCpnt->SCp.ptr = (char *) SCpnt->request_buffer; + SCpnt->SCp.this_residual = SCpnt->request_bufflen; + SCpnt->SCp.buffer = NULL; + SCpnt->SCp.buffers_residual = 0; + } + + if(nsphw_start_selection(SCpnt) == FALSE) { + DEBUG(0, " selection fail\n"); + nsp_data.CurrentSC = NULL; + SCpnt->result = DID_NO_CONNECT << 16; + done(SCpnt); + return FAILED; + } + + + //DEBUG(0, __FUNCTION__ "() out\n"); + return SUCCESS; +} + +/* + * setup PIO FIFO transfer mode and enable/disable to data out + */ +static void nsp_setup_fifo(unsigned int base, int enabled) +{ + unsigned char transfer_mode; + + //DEBUG(0, __FUNCTION__ "() enabled=%d\n", enabled); + + if (enabled != FALSE) { + transfer_mode = TRANSFER_GO | BRAIND; + } else { + transfer_mode = 0; + } + + transfer_mode |= nsp_data.TransferMode; + + nsp_index_write(base, TRANSFERMODE, transfer_mode); +} + +/* + * Initialize Ninja hardware + */ +static int nsphw_init(void) +{ + unsigned int base = nsp_data.BaseAddress; + int i, j; + sync_data tmp_sync = { SyncNegotiation: SYNC_NOT_YET, + SyncPeriod: 0, + SyncOffset: 0 + }; + + //DEBUG(0, __FUNCTION__ "() in\n"); + + nsp_data.ScsiClockDiv = CLOCK_40M; + nsp_data.CurrentSC = NULL; + nsp_data.FifoCount = 0; + nsp_data.TransferMode = MODE_IO8; + + /* setup sync data */ + for ( i = 0; i < N_TARGET; i++ ) { + for ( j = 0; j < N_LUN; j++ ) { + nsp_data.Sync[i][j] = tmp_sync; + } + } + + /* block all interrupts */ + nsp_write(base, IRQCONTROL, IRQCONTROL_ALLMASK); + + /* setup SCSI interface */ + nsp_write(base, IFSELECT, IF_IFSEL); + + nsp_index_write(base, SCSIIRQMODE, 0); + + nsp_index_write(base, TRANSFERMODE, MODE_IO8); + nsp_index_write(base, CLOCKDIV, nsp_data.ScsiClockDiv); + + nsp_index_write(base, PARITYCTRL, 0); + nsp_index_write(base, POINTERCLR, POINTER_CLEAR | + ACK_COUNTER_CLEAR | + REQ_COUNTER_CLEAR | + HOST_COUNTER_CLEAR); + + /* setup fifo asic */ + nsp_write(base, IFSELECT, IF_REGSEL); + nsp_index_write(base, TERMPWRCTRL, 0); + if ((nsp_index_read(base, OTHERCONTROL) & TPWR_SENSE) == 0) { + printk(KERN_INFO "nsp_cs: terminator power on\n"); + nsp_index_write(base, TERMPWRCTRL, POWER_ON); + } + + nsp_index_write(base, TIMERCOUNT, 0); + nsp_index_write(base, TIMERCOUNT, 0); /* requires 2 times!! */ + + nsp_index_write(base, SYNCREG, 0); + nsp_index_write(base, ACKWIDTH, 0); + + /* enable interrupts and ack them */ + nsp_index_write(base, SCSIIRQMODE, SCSI_PHASE_CHANGE_EI | + RESELECT_EI | + SCSI_RESET_IRQ_EI ); + nsp_write(base, IRQCONTROL, IRQCONTROL_ALLCLEAR); + + nsp_setup_fifo(base, FALSE); + + return TRUE; +} + +/* + * Start selection phase + */ +static unsigned int nsphw_start_selection(Scsi_Cmnd *SCpnt) +{ + unsigned int host_id = SCpnt->host->this_id; + unsigned int base = SCpnt->host->io_port; + unsigned char target = SCpnt->target; + int wait_count; + unsigned char phase, arbit; + + //DEBUG(0, __FUNCTION__ "()in\n"); + + + phase = nsp_index_read(base, SCSIBUSMON); + if(phase != BUSMON_BUS_FREE) { + //DEBUG(0, " bus busy\n"); + return FALSE; + } + + /* start arbitration */ + //DEBUG(0, " start arbit\n"); + SCpnt->SCp.phase = PH_ARBSTART; + nsp_index_write(base, SETARBIT, ARBIT_GO); + + wait_count = jiffies + 10 * HZ; + do { + /* XXX: what a stupid chip! */ + arbit = nsp_index_read(base, ARBITSTATUS); + //DEBUG(0, " arbit=%d, wait_count=%d\n", arbit, wait_count); + udelay(1); /* hold 1.2us */ + } while((arbit & (ARBIT_WIN | ARBIT_FAIL)) == 0 && + time_before(jiffies, wait_count)); + + if((arbit & ARBIT_WIN) == 0) { + //DEBUG(0, " arbit fail\n"); + nsp_index_write(base, SETARBIT, ARBIT_FLAG_CLEAR); + return FALSE; + } + + /* assert select line */ + //DEBUG(0, " assert SEL line\n"); + SCpnt->SCp.phase = PH_SELSTART; + udelay(3); + nsp_index_write(base, SCSIDATALATCH, (1 << host_id) | (1 << target)); + nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_BSY | SCSI_ATN); + udelay(3); + nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_BSY | SCSI_DATAOUT_ENB | SCSI_ATN); + nsp_index_write(base, SETARBIT, ARBIT_FLAG_CLEAR); + udelay(3); + nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_DATAOUT_ENB | SCSI_ATN); + + /* check selection timeout */ + nsp_start_timer(SCpnt, 1000/51); + nsp_data.SelectionTimeOut = 1; + + return TRUE; +} + +struct nsp_sync_table { + unsigned int min_period; + unsigned int max_period; + unsigned int chip_period; + unsigned int ack_width; +}; + +static struct nsp_sync_table nsp_sync_table_40M[] = { + {0x0c,0x0c,0x1,0}, /* 20MB 50ns*/ + {0x19,0x19,0x3,1}, /* 10MB 100ns*/ + {0x1a,0x25,0x5,2}, /* 7.5MB 150ns*/ + {0x26,0x32,0x7,3}, /* 5MB 200ns*/ + {0x0, 0, 0, 0} +}; + +static struct nsp_sync_table nsp_sync_table_20M[] = { + {0x19,0x19,0x1,0}, /* 10MB 100ns*/ + {0x1a,0x25,0x2,0}, /* 7.5MB 150ns*/ + {0x26,0x32,0x3,1}, /* 5MB 200ns*/ + {0x0, 0, 0, 0} +}; + +/* + * setup synchronous data transfer mode + */ +static int nsp_msg(Scsi_Cmnd *SCpnt) +{ + unsigned char target = SCpnt->target; + unsigned char lun = SCpnt->lun; + sync_data *sync = &(nsp_data.Sync[target][lun]); + struct nsp_sync_table *sync_table; + unsigned int period, offset; + int i; + + + DEBUG(0, __FUNCTION__ "()\n"); + +/**!**/ + + period = sync->SyncPeriod; + offset = sync->SyncOffset; + + DEBUG(0, " period=0x%x, offset=0x%x\n", period, offset); + + if (nsp_data.ScsiClockDiv == CLOCK_20M) { + sync_table = &nsp_sync_table_20M[0]; + } else { + sync_table = &nsp_sync_table_40M[0]; + } + + for ( i = 0; sync_table->max_period != 0; i++, sync_table++) { + if ( period >= sync_table->min_period && + period <= sync_table->max_period ) { + break; + } + } + + if (period != 0 && sync_table->max_period == 0) { + /* + * No proper period/offset found + */ + DEBUG(0, " no proper period/offset\n"); + + sync->SyncPeriod = 0; + sync->SyncOffset = 0; + sync->SyncRegister = 0; + sync->AckWidth = 0; + + return FALSE; + } + + sync->SyncRegister = (sync_table->chip_period << SYNCREG_PERIOD_SHIFT) | + (offset & SYNCREG_OFFSET_MASK); + sync->AckWidth = sync_table->ack_width; + + DEBUG(0, " sync_reg=0x%x, ack_width=0x%x\n", sync->SyncRegister, sync->AckWidth); + + return TRUE; +} + + +/* + * start ninja hardware timer + */ +static void nsp_start_timer(Scsi_Cmnd *SCpnt, int time) +{ + unsigned int base = SCpnt->host->io_port; + + //DEBUG(0, __FUNCTION__ "() in SCpnt=0x%p, time=%d\n", SCpnt, time); + nsp_data.TimerCount = time; + nsp_index_write(base, TIMERCOUNT, time); +} + +/* + * wait for bus phase change + */ +static int nsp_negate_signal(Scsi_Cmnd *SCpnt, unsigned char mask, char *str) +{ + unsigned int base = SCpnt->host->io_port; + unsigned char reg; + int count, i = TRUE; + + //DEBUG(0, __FUNCTION__ "()\n"); + + count = jiffies + HZ; + + do { + reg = nsp_index_read(base, SCSIBUSMON); + if (reg == 0xff) { + break; + } + } while ((i = time_before(jiffies, count)) && (reg & mask) != 0); + + if (!i) { + printk(KERN_DEBUG __FUNCTION__ " %s signal off timeut\n", str); + } + + return 0; +} + +/* + * expect Ninja Irq + */ +static int nsp_expect_signal(Scsi_Cmnd *SCpnt, + unsigned char current_phase, + unsigned char mask) +{ + unsigned int base = SCpnt->host->io_port; + int wait_count; + unsigned char phase, i_src; + + //DEBUG(0, __FUNCTION__ "() current_phase=0x%x, mask=0x%x\n", current_phase, mask); + + wait_count = jiffies + HZ; + do { + phase = nsp_index_read(base, SCSIBUSMON); + if (phase == 0xff) { + //DEBUG(0, " ret -1\n"); + return -1; + } + i_src = nsp_read(base, IRQSTATUS); + if (i_src & IRQSTATUS_SCSI) { + //DEBUG(0, " ret 0 found scsi signal\n"); + return 0; + } + if ((phase & mask) != 0 && (phase & BUSMON_PHASE_MASK) == current_phase) { + //DEBUG(0, " ret 1 phase=0x%x\n", phase); + return 1; + } + } while(time_before(jiffies, wait_count)); + + //DEBUG(0, __FUNCTION__ " : " __FUNCTION__ " timeout\n"); + return -1; +} + +/* + * transfer SCSI message + */ +static int nsp_xfer(Scsi_Cmnd *SCpnt, int phase) +{ + unsigned int base = SCpnt->host->io_port; + char *buf = nsp_data.MsgBuffer; + int len = MIN(MSGBUF_SIZE, nsp_data.MsgLen); + int ptr; + int ret; + + //DEBUG(0, __FUNCTION__ "()\n"); +/**!**/ + for (ptr = 0; len > 0; len --, ptr ++) { + + ret = nsp_expect_signal(SCpnt, phase, BUSMON_REQ); + if (ret <= 0) { + DEBUG(0, " xfer quit\n"); + return 0; + } + + /* if last byte, negate ATN */ + if (len == 1) { + nsp_index_write(base, SCSIBUSCTRL, AUTODIRECTION | ACKENB); + } + + /* read & write message */ + if (phase & BUSMON_IO) { + //DEBUG(0, " read msg\n"); + buf[ptr] = nsp_index_read(base, SCSIDATAWITHACK); + } else { + //DEBUG(0, " write msg\n"); + nsp_index_write(base, SCSIDATAWITHACK, buf[ptr]); + } + nsp_negate_signal(SCpnt, BUSMON_ACK, "xfer"); + + } + return len; +} + +/* + * get extra SCSI data from fifo + */ +static int nsp_dataphase_bypass(Scsi_Cmnd *SCpnt) +{ + unsigned int base = SCpnt->host->io_port; + unsigned int count; + + //DEBUG(0, __FUNCTION__ "()\n"); + + if (SCpnt->SCp.have_data_in != IO_IN) { + return 0; + } + + count = nsp_fifo_count(SCpnt); + if (nsp_data.FifoCount == count) { + //DEBUG(0, " not use bypass quirk\n"); + return 0; + } + + /* + * XXX: NSP_QUIRK + * data phase skip only occures in case of SCSI_LOW_READ + */ + SCpnt->SCp.phase = PH_DATA; + nsp_pio_read(SCpnt); + nsp_setup_fifo(base, FALSE); + + DEBUG(0, " use bypass quirk\n"); + return 0; +} + +/* + * accept reselection + */ +static int nsp_reselected(Scsi_Cmnd *SCpnt) +{ + unsigned int base = SCpnt->host->io_port; + unsigned char reg; + + //DEBUG(0, __FUNCTION__ "()\n"); + + nsp_negate_signal(SCpnt, BUSMON_SEL, "reselect"); + + nsp_nexus(SCpnt); + reg = nsp_index_read(base, SCSIBUSCTRL) & ~(SCSI_BSY | SCSI_ATN); + nsp_index_write(base, SCSIBUSCTRL, reg); + nsp_index_write(base, SCSIBUSCTRL, reg | AUTODIRECTION | ACKENB); + + return TRUE; +} + +/* + * count how many data transferd + */ +static int nsp_fifo_count(Scsi_Cmnd *SCpnt) +{ + unsigned int base = SCpnt->host->io_port; + unsigned int count; + unsigned int l, m, h; + + nsp_index_write(base, POINTERCLR, POINTER_CLEAR); + + l = (unsigned int)nsp_read(base, DATAREG); + m = (unsigned int)nsp_read(base, DATAREG); + h = (unsigned int)nsp_read(base, DATAREG); + + count = (h << 16) | (m << 8) | (l << 0); + + //DEBUG(0, __FUNCTION__ "() =0x%x\n", count); + + return count; +} + +/* fifo size */ +#define RFIFO_CRIT 64 +#define WFIFO_CRIT 64 + +/* + * read data in DATA IN phase + */ +static void nsp_pio_read(Scsi_Cmnd *SCpnt) +{ + unsigned int base = SCpnt->host->io_port; + int time_out, i; + int ocount, res; + unsigned char stat, fifo_stat; + + ocount = nsp_data.FifoCount; + + DEBUG(0, __FUNCTION__ "() in SCpnt=0x%p resid=%d ocount=%d ptr=0x%p this_residual=%d buffers=0x%p nbuf=%d\n", SCpnt, RESID, ocount, SCpnt->SCp.ptr, SCpnt->SCp.this_residual, SCpnt->SCp.buffer, SCpnt->SCp.buffers_residual); + + time_out = jiffies + 10 * HZ; + + while ((i = time_before(jiffies,time_out)) && + (SCpnt->SCp.this_residual > 0 || SCpnt->SCp.buffers_residual > 0 ) ) { + + stat = nsp_index_read(base, SCSIBUSMON); + stat &= BUSMON_PHASE_MASK; + + res = nsp_fifo_count(SCpnt) - ocount; + + //DEBUG(0, " ptr=0x%p this=0x%x ocount=0x%x res=0x%x\n", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, ocount, res); + if (res == 0) { /* if some data avilable ? */ + if (stat == BUSPHASE_DATA_IN) { /* phase changed? */ + //DEBUG(0, " wait for data this=%d\n", SCpnt->SCp.this_residual); + continue; + } else { + DEBUG(0, " phase changed stat=0x%x\n", stat); + break; + } + } + + fifo_stat = nsp_read(base, FIFOSTATUS); + if ((fifo_stat & FIFOSTATUS_FULL_EMPTY) == 0 && + stat == BUSPHASE_DATA_IN) { + continue; + } + + res = MIN(res, SCpnt->SCp.this_residual); + + switch (nsp_data.TransferMode) { + case MODE_IO32: + res &= ~(BIT(1)|BIT(0)); /* align 4 */ + nsp_fifo32_read(base, SCpnt->SCp.ptr, res >> 2); + break; + case MODE_IO8: + nsp_fifo8_read (base, SCpnt->SCp.ptr, res ); + break; + default: + DEBUG(0, "unknown read mode\n"); + break; + } + + RESID -= res; + SCpnt->SCp.ptr += res; + SCpnt->SCp.this_residual -= res; + ocount += res; + //DEBUG(0, " ptr=0x%p this_residual=0x%x ocount=0x%x\n", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, ocount); + + /* go to next scatter list if availavle */ + if (SCpnt->SCp.this_residual == 0 && + SCpnt->SCp.buffers_residual != 0 ) { + //DEBUG(0, " scatterlist next timeout=%d\n", time_out); + SCpnt->SCp.buffers_residual--; + SCpnt->SCp.buffer++; + SCpnt->SCp.ptr = SCpnt->SCp.buffer->address; + SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; + } + + time_out = jiffies + 10 * HZ; + } + + nsp_data.FifoCount = ocount; + + if (!i) { + printk(KERN_DEBUG __FUNCTION__ "() pio read timeout resid=%d this_residual=%d buffers_residual=%d\n", RESID, SCpnt->SCp.this_residual, SCpnt->SCp.buffers_residual); + } + DEBUG(0, " read ocount=0x%x\n", ocount); +} + +/* + * write data in DATA OUT phase + */ +static void nsp_pio_write(Scsi_Cmnd *SCpnt) +{ + unsigned int base = SCpnt->host->io_port; + int time_out, i; + unsigned int ocount, res; + unsigned char stat; + + ocount = nsp_data.FifoCount; + + DEBUG(0, __FUNCTION__ "() in fifocount=%d ptr=0x%p this_residual=%d buffers=0x%p nbuf=%d resid=0x%x\n", nsp_data.FifoCount, SCpnt->SCp.ptr, SCpnt->SCp.this_residual, SCpnt->SCp.buffer, SCpnt->SCp.buffers_residual, SCpnt->resid); + + time_out = jiffies + 10 * HZ; + + while ((i = time_before(jiffies, time_out)) && + (SCpnt->SCp.this_residual > 0 || SCpnt->SCp.buffers_residual > 0)) { + stat = nsp_index_read(base, SCSIBUSMON); + stat &= BUSMON_PHASE_MASK; + if (stat != BUSPHASE_DATA_OUT) { + DEBUG(0, " phase changed stat=0x%x\n", stat); + break; + } + + res = ocount - nsp_fifo_count(SCpnt); + if (res > 0) { /* write all data? */ + DEBUG(0, " wait for all data out. ocount=0x%x res=%d\n", ocount, res); + continue; + } + + res = MIN(SCpnt->SCp.this_residual, WFIFO_CRIT); + + //DEBUG(0, " ptr=0x%p this=0x%x res=0x%x\n", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, res); + switch (nsp_data.TransferMode) { + case MODE_IO32: + res &= ~(BIT(1)|BIT(0)); /* align 4 */ + nsp_fifo32_write(base, SCpnt->SCp.ptr, res >> 2); + break; + case MODE_IO8: + nsp_fifo8_write (base, SCpnt->SCp.ptr, res ); + break; + default: + DEBUG(0, "unknown write mode\n"); + break; + } + + RESID -= res; + SCpnt->SCp.ptr += res; + SCpnt->SCp.this_residual -= res; + ocount += res; + + /* go to next scatter list if availavle */ + if (SCpnt->SCp.this_residual == 0 && + SCpnt->SCp.buffers_residual != 0 ) { + //DEBUG(0, " scatterlist next\n"); + SCpnt->SCp.buffers_residual--; + SCpnt->SCp.buffer++; + SCpnt->SCp.ptr = SCpnt->SCp.buffer->address; + SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; + } + + time_out = jiffies + 10 * HZ; + } + + nsp_data.FifoCount = ocount; + + if (!i) { + printk(KERN_DEBUG __FUNCTION__ "() pio write timeout resid=%d\n", RESID); + } + //DEBUG(0, " write ocount=%d\n", ocount); +} + +#undef RFIFO_CRIT +#undef WFIFO_CRIT + +/* + * setup synchronous/asynchronous data transfer mode + */ +static int nsp_nexus(Scsi_Cmnd *SCpnt) +{ + unsigned int base = SCpnt->host->io_port; + unsigned char target = SCpnt->target; + unsigned char lun = SCpnt->lun; + sync_data *sync = &(nsp_data.Sync[target][lun]); + + //DEBUG(0, __FUNCTION__ "() in SCpnt=0x%p\n", SCpnt); + + /* setup synch transfer registers */ + nsp_index_write(base, SYNCREG, sync->SyncRegister); + nsp_index_write(base, ACKWIDTH, sync->AckWidth); + + if (RESID % 4 != 0 || + RESID <= 256 ) { + nsp_data.TransferMode = MODE_IO8; + } else { + nsp_data.TransferMode = MODE_IO32; + } + + /* setup pdma fifo */ + nsp_setup_fifo(base, TRUE); + + /* clear ack counter */ + nsp_data.FifoCount = 0; + nsp_index_write(base, POINTERCLR, POINTER_CLEAR | + ACK_COUNTER_CLEAR | + REQ_COUNTER_CLEAR | + HOST_COUNTER_CLEAR); + + return 0; +} + +/* + * interrupt handler + */ +static void nspintr(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int base; + unsigned char i_src, irq_phase, phase; + Scsi_Cmnd *tmpSC; + int ret, len; + unsigned char data_reg, control_reg; + + + //DEBUG(0, __FUNCTION__ "(%d) CurrentSC=0x%p\n", irq, nsp_data.CurrentSC); + + base = nsp_data.BaseAddress; + //DEBUG(0, " base=0x%x\n", base); + + /* + * interrupt check + */ + nsp_write(base, IRQCONTROL, IRQCONTROL_IRQDISABLE); + i_src = nsp_read(base, IRQSTATUS); + if (i_src == 0xff || (i_src & IRQSTATUS_MASK) == 0) { + nsp_write(base, IRQCONTROL, 0); + //DEBUG(0, " no irq\n"); + return; + } + + //DEBUG(0, " i_src=0x%x\n", i_src); + + /* XXX: IMPORTANT + * Do not read an irq_phase register if no scsi phase interrupt. + * Unless, you should lose a scsi phase interrupt. + */ + phase = nsp_index_read(base, SCSIBUSMON); + if((i_src & IRQSTATUS_SCSI) != 0) { + irq_phase = nsp_index_read(base, IRQPHASESENCE); + } else { + irq_phase = 0; + } + + //DEBUG(0, " irq_phase=0x%x\n", irq_phase); + + /* + * timer interrupt handler (scsi vs timer interrupts) + */ + //DEBUG(0, " timercount=%d\n", nsp_data.TimerCount); + if (nsp_data.TimerCount != 0) { + //DEBUG(0, " stop timer\n"); + nsp_index_write(base, TIMERCOUNT, 0); + nsp_index_write(base, TIMERCOUNT, 0); + nsp_data.TimerCount = 0; + } + + if ((i_src & IRQSTATUS_MASK) == IRQSTATUS_TIMER && + nsp_data.SelectionTimeOut == 0) { + //DEBUG(0, " timer start\n"); + nsp_write(base, IRQCONTROL, IRQCONTROL_TIMER_CLEAR); + return; + } + + nsp_write(base, IRQCONTROL, IRQCONTROL_TIMER_CLEAR | IRQCONTROL_FIFO_CLEAR); + + if (nsp_data.CurrentSC == NULL) { + printk(KERN_DEBUG __FUNCTION__ " CurrentSC==NULL irq_status=0x%x phase=0x%x irq_phase=0x%x this can't be happen\n", i_src, phase, irq_phase); + return; + } else { + tmpSC = nsp_data.CurrentSC; + } + + /* + * parse hardware SCSI irq reasons register + */ + if ((i_src & IRQSTATUS_SCSI) != 0) { + if ((irq_phase & SCSI_RESET_IRQ) != 0) { + printk(KERN_DEBUG " " __FUNCTION__ "() bus reset (power off?)\n"); + + nsp_data.CurrentSC = NULL; + tmpSC->result = DID_RESET << 16; + tmpSC->scsi_done(tmpSC); + return; + } + + if ((irq_phase & RESELECT_IRQ) != 0) { + DEBUG(0, " reselect\n"); + nsp_write(base, IRQCONTROL, IRQCONTROL_RESELECT_CLEAR); + if (nsp_reselected(tmpSC) != FALSE) { + return; + } + } + + if ((irq_phase & (PHASE_CHANGE_IRQ | LATCHED_BUS_FREE)) == 0) { + return; + } + } + + //show_phase(tmpSC); + + switch(tmpSC->SCp.phase) { + case PH_SELSTART: + if ((phase & BUSMON_BSY) == 0) { + //DEBUG(0, " selection count=%d\n", nsp_data.SelectionTimeOut); + if (nsp_data.SelectionTimeOut >= NSP_SELTIMEOUT) { + DEBUG(0, " selection time out\n"); + nsp_data.SelectionTimeOut = 0; + nsp_index_write(base, SCSIBUSCTRL, 0); + + nsp_data.CurrentSC = NULL; + tmpSC->result = DID_NO_CONNECT << 16; + tmpSC->scsi_done(tmpSC); + + return; + } + nsp_data.SelectionTimeOut += 1; + nsp_start_timer(tmpSC, 1000/51); + return; + } + + /* attention assert */ + //DEBUG(0, " attention assert\n"); + nsp_data.SelectionTimeOut = 0; + tmpSC->SCp.phase = PH_SELECTED; + nsp_index_write(base, SCSIBUSCTRL, SCSI_ATN); + udelay(1); + nsp_index_write(base, SCSIBUSCTRL, SCSI_ATN | AUTODIRECTION | ACKENB); + return; + + break; + + case PH_RESELECT: + //DEBUG(0, " phase reselect\n"); + if ((phase & BUSMON_PHASE_MASK) != BUSPHASE_MESSAGE_IN) { + + nsp_data.CurrentSC = NULL; + tmpSC->result = DID_ABORT << 16; + tmpSC->scsi_done(tmpSC); + return; + } + /* fall thru */ + default: + if (( i_src & (IRQSTATUS_SCSI | IRQSTATUS_FIFO)) == 0) { + return; + } + break; + } + + /* + * SCSI sequencer + */ + //DEBUG(0, " start scsi seq\n"); + + /* normal disconnect */ + if ((irq_phase & LATCHED_BUS_FREE) != 0) { + //DEBUG(0, " normal disconnect i_src=0x%x, phase=0x%x, irq_phase=0x%x\n", i_src, phase, irq_phase); + if ((tmpSC->SCp.Message == 0)) { /* all command complete and return status */ + nsp_data.CurrentSC = NULL; + tmpSC->result = (DID_OK << 16) | + (tmpSC->SCp.Message << 8) | + (tmpSC->SCp.Status << 0); + DEBUG(0, " command complete result=0x%x\n", tmpSC->result); + tmpSC->scsi_done(tmpSC); + return; + } + + return; + } + + + /* check unexpected bus free state */ + if (phase == 0) { + printk(KERN_DEBUG " " __FUNCTION__ " unexpected bus free. i_src=0x%x, phase=0x%x, irq_phase=0x%x\n", i_src, phase, irq_phase); + + nsp_data.CurrentSC = NULL; + tmpSC->result = DID_ERROR << 16; + tmpSC->scsi_done(tmpSC); + return; + } + + switch (phase & BUSMON_PHASE_MASK) { + case BUSPHASE_COMMAND: + DEBUG(0, " BUSPHASE_COMMAND\n"); + if ((phase & BUSMON_REQ) == 0) { + DEBUG(0, " REQ == 0\n"); + return; + } + + tmpSC->SCp.phase = PH_COMMAND; + + nsp_nexus(tmpSC); + + /* write scsi command */ + nsp_index_write(base, COMMANDCTRL, CLEAR_COMMAND_POINTER); + for (len = 0; len < COMMAND_SIZE(tmpSC->cmnd[0]); len++) { + nsp_index_write(base, COMMANDDATA, tmpSC->cmnd[len]); + } + nsp_index_write(base, COMMANDCTRL, CLEAR_COMMAND_POINTER | AUTO_COMMAND_GO); + break; + + case BUSPHASE_DATA_OUT: + DEBUG(0, " BUSPHASE_DATA_OUT\n"); + + tmpSC->SCp.phase = PH_DATA; + tmpSC->SCp.have_data_in = IO_OUT; + + nsp_pio_write(tmpSC); + + break; + + case BUSPHASE_DATA_IN: + DEBUG(0, " BUSPHASE_DATA_IN\n"); + + tmpSC->SCp.phase = PH_DATA; + tmpSC->SCp.have_data_in = IO_IN; + + nsp_pio_read(tmpSC); + + break; + + case BUSPHASE_STATUS: + nsp_dataphase_bypass(tmpSC); + DEBUG(0, " BUSPHASE_STATUS\n"); + + tmpSC->SCp.phase = PH_STATUS; + + tmpSC->SCp.Status = nsp_index_read(base, SCSIDATAWITHACK); + //DEBUG(0, " message=0x%x status=0x%x\n", tmpSC->SCp.Message, tmpSC->SCp.Status); + + break; + + case BUSPHASE_MESSAGE_OUT: + DEBUG(0, " BUSPHASE_MESSAGE_OUT\n"); + if ((phase & BUSMON_REQ) == 0) { + goto timer_out; + } + + tmpSC->SCp.phase = PH_MSG_OUT; + + /* + * XXX: NSP QUIRK + * NSP invoke interrupts only in the case of scsi phase changes, + * therefore we should poll the scsi phase here to catch + * the next "msg out" if exists (no scsi phase changes). + */ + ret = 16; + len = 0; + + nsp_msg(tmpSC); + + nsp_data.MsgBuffer[len] = IDENTIFY(TRUE, tmpSC->lun); len++; + nsp_data.MsgLen = len; + + do { + DEBUG(0, " msgout loop\n"); + + if (nsp_xfer(tmpSC, BUSPHASE_MESSAGE_OUT)) { + printk(KERN_DEBUG " " __FUNCTION__ " msgout: xfer short\n"); + } + + /* catch a next signal */ + ret = nsp_expect_signal(tmpSC, BUSPHASE_MESSAGE_OUT, BUSMON_REQ); + } while (ret > 0 && len-- > 0); + + break; + + case BUSPHASE_MESSAGE_IN: + nsp_dataphase_bypass(tmpSC); + DEBUG(0, " BUSPHASE_MESSAGE_IN\n"); + if ((phase & BUSMON_REQ) == 0) { + goto timer_out; + } + + tmpSC->SCp.phase = PH_MSG_IN; + + /* + * XXX: NSP QUIRK + * NSP invoke interrupts only in the case of scsi phase changes, + * therefore we should poll the scsi phase here to catch + * the next "msg in" if exists (no scsi phase changes). + */ + ret = 16; + len = 0; + + do { + //DEBUG(0, " msgin loop\n"); + /* read data */ + data_reg = nsp_index_read(base, SCSIDATAIN); + + /* assert ACK */ + control_reg = nsp_index_read(base, SCSIBUSCTRL); + control_reg |= SCSI_ACK; + nsp_index_write(base, SCSIBUSCTRL, control_reg); + nsp_negate_signal(tmpSC, BUSMON_REQ, "msgin"); + + nsp_data.MsgBuffer[len] = data_reg; len++; + DEBUG(0, " msg=0x%x\n", data_reg); + + /* deassert ACK */ + control_reg = nsp_index_read(base, SCSIBUSCTRL); + control_reg &= ~SCSI_ACK; + nsp_index_write(base, SCSIBUSCTRL, control_reg); + + /* catch a next signal */ + ret = nsp_expect_signal(tmpSC, BUSPHASE_MESSAGE_IN, BUSMON_REQ); + } while (ret > 0 && MSGBUF_SIZE > len); + + nsp_data.MsgLen = len; + tmpSC->SCp.Message = nsp_data.MsgBuffer[len-1]; + + //DEBUG(0, " message=0x%x len=%d\n", tmpSC->SCp.Message, nsp_data.MsgLen); + break; + + case BUSPHASE_SELECT: + default: + DEBUG(0, " BUSPHASE other\n"); + + break; + } + + //DEBUG(0, __FUNCTION__ "() out\n"); + return; + +timer_out: + nsp_start_timer(tmpSC, 1000/102); + return; +} + +#ifdef DBG_SHOWCOMMAND +#include "nsp_debug.c" +#endif /* DBG_SHOWCOMMAND */ + +/*====================================================================*/ +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================== + nsp_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + + The dev_link structure is initialized, but we don't actually + configure the card at this point -- we wait until we receive a + card insertion event. +======================================================================*/ +static dev_link_t *nsp_attach(void) +{ + scsi_info_t *info; + client_reg_t client_reg; + dev_link_t *link; + int ret, i; + + DEBUG(0, __FUNCTION__ "()\n"); + + /* Create new SCSI device */ + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) return NULL; + memset(info, 0, sizeof(*info)); + link = &info->link; + link->priv = info; + + /* Initialize the dev_link_t structure */ + link->release.function = &nsp_release; + link->release.data = (u_long)link; + + /* The io structure describes IO port mapping */ + link->io.NumPorts1 = 0x10; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.IOAddrLines = 10; /* not used */ + + /* Interrupt setup */ + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + if (irq_list[0] == -1) { + link->irq.IRQInfo2 = irq_mask; + } else { + for (i = 0; i < 4; i++) { + link->irq.IRQInfo2 |= 1 << irq_list[i]; + } + } + link->irq.Handler = &nspintr; + + /* General socket configuration */ + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.Present = PRESENT_OPTION; + + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME ; + client_reg.event_handler = &nsp_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + nsp_detach(link); + return NULL; + } + + return link; +} /* nsp_attach */ + + +/*====================================================================== + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. +======================================================================*/ +static void nsp_detach(dev_link_t *link) +{ + dev_link_t **linkp; + + DEBUG(0, __FUNCTION__ "(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) { + if (*linkp == link) { + break; + } + } + if (*linkp == NULL) { + return; + } + + del_timer(&link->release); + if (link->state & DEV_CONFIG) { + nsp_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + /* Break the link with Card Services */ + if (link->handle) { + CardServices(DeregisterClient, link->handle); + } + + /* Unlink device structure, free bits */ + *linkp = link->next; + kfree(link->priv); + +} /* nsp_detach */ + + +/*====================================================================== + nsp_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + ethernet device available to the system. +======================================================================*/ +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed +#define CFG_CHECK(fn, args...) \ +if (CardServices(fn, args) != 0) goto next_entry +/*====================================================================*/ + +static void nsp_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + scsi_info_t *info = link->priv; + tuple_t tuple; + cisparse_t parse; + int i, last_ret, last_fn; + u_char tuple_data[64]; + config_info_t conf; + Scsi_Device *dev; + dev_node_t **tail, *node; + struct Scsi_Host *host; + + DEBUG(0, __FUNCTION__ "() in\n"); + + tuple.DesiredTuple = CISTPL_CONFIG; + tuple.Attributes = 0; + tuple.TupleData = tuple_data; + tuple.TupleDataMax = sizeof(tuple_data); + tuple.TupleOffset = 0; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + driver_template.module = &__this_module; + link->state |= DEV_CONFIG; + + /* Look up the current Vcc */ + CS_CHECK(GetConfigurationInfo, handle, &conf); + link->conf.Vcc = conf.Vcc; + + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + CS_CHECK(GetFirstTuple, handle, &tuple); + while (1) { + CFG_CHECK(GetTupleData, handle, &tuple); + CFG_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigIndex = parse.cftable_entry.index; + link->io.BasePort1 = parse.cftable_entry.io.win[0].base; + i = CardServices(RequestIO, handle, &link->io); + if (i == CS_SUCCESS) { + break; + } + next_entry: + DEBUG(0, __FUNCTION__ " next\n"); + CS_CHECK(GetNextTuple, handle, &tuple); + } + + CS_CHECK(RequestIRQ, handle, &link->irq); + CS_CHECK(RequestConfiguration, handle, &link->conf); + + /* A bad hack... */ + release_region(link->io.BasePort1, link->io.NumPorts1); + + /* Set port and IRQ */ + nsp_data.BaseAddress = link->io.BasePort1; + nsp_data.NumAddress = link->io.NumPorts1; + nsp_data.IrqNumber = link->irq.AssignedIRQ; + + DEBUG(0, __FUNCTION__ " I/O[0x%x+0x%x] IRQ %d\n", + nsp_data.BaseAddress, nsp_data.NumAddress, nsp_data.IrqNumber); + + if(nsphw_init() == FALSE) { + goto cs_failed; + } + + scsi_register_module(MODULE_SCSI_HA, &driver_template); + + DEBUG(0, "GET_SCSI_INFO\n"); + tail = &link->dev; + info->ndev = 0; + for (host = scsi_hostlist; host != NULL; host = host->next) { + if (host->hostt == &driver_template) { + for (dev = host->host_queue; dev != NULL; dev = dev->next) { + u_long arg[2], id; + kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg); + id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) + + ((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000); + node = &info->node[info->ndev]; + node->minor = 0; + switch (dev->type) { + case TYPE_TAPE: + node->major = SCSI_TAPE_MAJOR; + sprintf(node->dev_name, "st#%04lx", id); + break; + case TYPE_DISK: + case TYPE_MOD: + node->major = SCSI_DISK0_MAJOR; + sprintf(node->dev_name, "sd#%04lx", id); + break; + case TYPE_ROM: + case TYPE_WORM: + node->major = SCSI_CDROM_MAJOR; + sprintf(node->dev_name, "sr#%04lx", id); + break; + default: + node->major = SCSI_GENERIC_MAJOR; + sprintf(node->dev_name, "sg#%04lx", id); + break; + } + *tail = node; tail = &node->next; + info->ndev++; + info->host = dev->host; + } + } + } + *tail = NULL; + if (info->ndev == 0) { + printk(KERN_INFO "nsp_cs: no SCSI devices found\n"); + } + + /* Finally, report what we've done */ + printk(KERN_INFO "nsp_cs: index 0x%02x: Vcc %d.%d", + link->conf.ConfigIndex, + link->conf.Vcc/10, link->conf.Vcc%10); + if (link->conf.Vpp1) { + printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10); + } + if (link->conf.Attributes & CONF_ENABLE_IRQ) { + printk(", irq %d", link->irq.AssignedIRQ); + } + if (link->io.NumPorts1) { + printk(", io 0x%04x-0x%04x", link->io.BasePort1, + link->io.BasePort1+link->io.NumPorts1-1); + } + printk("\n"); + + link->state &= ~DEV_CONFIG_PENDING; + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); + nsp_release((u_long)link); + return; + +} /* nsp_config */ +#undef CS_CHECK +#undef CFG_CHECK + +/*====================================================================== + After a card is removed, nsp_release() will unregister the net + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. +======================================================================*/ +static void nsp_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + + DEBUG(0, __FUNCTION__ "(0x%p)\n", link); + + /* + * If the device is currently in use, we won't release until it + * is actually closed. + */ + if (link->open) { + DEBUG(1, "nsp_cs: release postponed, '%s' still open\n", + link->dev->dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + + /* Unlink the device chain */ + scsi_unregister_module(MODULE_SCSI_HA, &driver_template); + link->dev = NULL; + + if (link->win) { + CardServices(ReleaseWindow, link->win); + } + CardServices(ReleaseConfiguration, link->handle); + if (link->io.NumPorts1) { + CardServices(ReleaseIO, link->handle, &link->io); + } + if (link->irq.AssignedIRQ) { + CardServices(ReleaseIRQ, link->handle, &link->irq); + } + link->state &= ~DEV_CONFIG; + + if (link->state & DEV_STALE_LINK) { + nsp_detach(link); + } +} /* nsp_release */ + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the net drivers from trying + to talk to the card any more. + + When a CARD_REMOVAL event is received, we immediately set a flag + to block future accesses to this device. All the functions that + actually access the device should check this flag to make sure + the card is still present. + +======================================================================*/ +static int nsp_event(event_t event, + int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + scsi_info_t *info = link->priv; + + DEBUG(1, __FUNCTION__ "(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + DEBUG(0, " event: remove\n"); + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + ((scsi_info_t *)link->priv)->stop = 1; + mod_timer(&link->release, jiffies + HZ/20); + } + break; + + case CS_EVENT_CARD_INSERTION: + DEBUG(0, " event: insert\n"); + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + info->bus = args->bus; + nsp_config(link); + break; + + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + /* Mark the device as stopped, to block IO until later */ + info->stop = 1; + if (link->state & DEV_CONFIG) { + CardServices(ReleaseConfiguration, link->handle); + } + break; + + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + DEBUG(0, " event: reset\n"); + if (link->state & DEV_CONFIG) { + Scsi_Cmnd tmp; + + CardServices(RequestConfiguration, link->handle, &link->conf); + tmp.host = info->host; + nsp_reset(&tmp, 0); + } + info->stop = 0; + /* restart IO */ + nsphw_init(); + break; + + default: + DEBUG(0, " event: unknown\n"); + break; + } + DEBUG(0, __FUNCTION__ " end\n"); + return 0; +} /* nsp_event */ + +/*----------------------------------------------------------------*/ +/* look for ninja3 card and init if found */ +/*----------------------------------------------------------------*/ +static int nsp_detect(Scsi_Host_Template *sht) +{ + struct Scsi_Host *host; /* registered host structure */ + + DEBUG(0, __FUNCTION__ " this_id=%d\n", sht->this_id); + +#if (KERNEL_VERSION(2,3,99) > LINUX_VERSION_CODE) + sht->proc_dir = &proc_scsi_nsp; /* kernel 2.2 */ +#else + sht->proc_name = "nsp_cs"; /* kernel 2.4 */ +#endif + + sht->this_id = SCSI_INITIATOR_ID; + + request_region(nsp_data.BaseAddress, nsp_data.NumAddress,"nsp_cs"); + host = scsi_register(sht, 0); + host->io_port = nsp_data.BaseAddress; + host->unique_id = nsp_data.BaseAddress; + host->n_io_port = nsp_data.NumAddress; + host->irq = nsp_data.IrqNumber; + host->dma_channel = -1; + + sprintf(nspinfo, +/* Buffer size is 100 bytes */ +/* 0 1 2 3 4 5 6 7 8 9 0*/ +/* 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890*/ + "NinjaSCSI-3/32Bi Driver version 2.0, I/O 0x%04lx-0x%04lx IRQ %2d", + host->io_port, host->io_port + host->n_io_port, + host->irq); + sht->name = nspinfo; + + DEBUG(0, __FUNCTION__ " end\n"); + + return 1; /* detect done. */ +} + + +/*----------------------------------------------------------------*/ +/* return info string */ +/*----------------------------------------------------------------*/ +static const char *nsp_info(struct Scsi_Host *host) +{ + return nspinfo; +} + +static int nsp_reset(Scsi_Cmnd *SCpnt, unsigned int why) +{ + DEBUG(0, __FUNCTION__ "() SCpnt=0x%p why=%d\n", SCpnt, why); + + return nsp_eh_bus_reset(SCpnt); +} + +static int nsp_abort(Scsi_Cmnd *SCpnt) +{ + DEBUG(0, __FUNCTION__ "() SCpnt=0x%p\n", SCpnt); + return FAILED; +} + +/*static int nsp_eh_strategy(struct Scsi_Host *Shost) +{ +} +static int nsp_eh_abort(Scsi_Cmnd * cmd) +{ + DEBUG(0, __FUNCTION__"() SCpnt\n"); +} + +static nsp_eh_device_reset(Scsi_Cmnd *) +{ +} +*/ +static int nsp_eh_bus_reset(Scsi_Cmnd *SCpnt) +{ + unsigned int base = SCpnt->host->io_port; + int i; + + DEBUG(0, __FUNCTION__ "() SCpnt=0x%p\n", SCpnt); + + nsp_write(base, IRQCONTROL, IRQCONTROL_ALLMASK); + + nsp_index_write(base, SCSIBUSCTRL, SCSI_RST); + mdelay(100); /* 100ms */ + nsp_index_write(base, SCSIBUSCTRL, 0); + for(i = 0; i < 5; i++) { + (void) nsp_index_read(base, IRQPHASESENCE); /* dummy read */ + } + + nsp_write(base, IRQCONTROL, IRQCONTROL_ALLCLEAR); + + return 0; +} +/* +static nsp_eh_host_reset(Scsi_Cmnd *) +{ +}*/ + +/*======================================================================* + * module entry point + *====================================================================*/ +static int __init nsp_init(void) +{ + servinfo_t serv; + + DEBUG(0, __FUNCTION__ "() in\n"); + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_DEBUG "nsp_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pcmcia_driver(&dev_info, &nsp_attach, &nsp_detach); + + DEBUG(0, __FUNCTION__ "() out\n"); + return 0; +} + + +static void __exit nsp_cleanup(void) +{ + DEBUG(0, "nsp_cs: unloading\n"); + unregister_pcmcia_driver(&dev_info); + while (dev_list != NULL) { + if (dev_list->state & DEV_CONFIG) { + nsp_release((u_long)dev_list); + } + nsp_detach(dev_list); + } +} + +module_init(nsp_init); +module_exit(nsp_cleanup); + +/* + * + * + */ + +/* end */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/nsp_cs.h linux.ac/drivers/scsi/pcmcia/nsp_cs.h --- linux.vanilla/drivers/scsi/pcmcia/nsp_cs.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/pcmcia/nsp_cs.h Tue Apr 3 17:55:04 2001 @@ -0,0 +1,302 @@ +/*=======================================================/ + Header file for nsp_cs.c + By: YOKOTA Hiroshi + + Ver.1.0 : Cut unused lines. + Ver 0.1 : Initial version. + + This software may be used and distributed according to the terms of + the GNU General Public License. + +=========================================================*/ + +/* $Id: nsp_cs.h,v 1.18 2001/02/09 04:42:19 elca Exp $ */ + +#ifndef __nsp_cs__ +#define __nsp_cs__ + +/* for debugging */ +/* +#define DBG +#define DBG_PRINT +#define DBG_SHOWCOMMAND +#define PCMCIA_DEBUG 9 +*/ + +/* +#define static +#define inline +*/ + +/************************************ + * Some useful macros... + */ +#define Number(arr) ((int) (sizeof(arr) / sizeof(arr[0]))) +#define BIT(x) (1<<(x)) +#define MIN(a,b) ((a) > (b) ? (b) : (a)) + +/* SCSI initiator must be 7 */ +#define SCSI_INITIATOR_ID 7 + +/* base register */ +#define IRQCONTROL 0x00 +# define IRQCONTROL_RESELECT_CLEAR BIT(0) +# define IRQCONTROL_PHASE_CHANGE_CLEAR BIT(1) +# define IRQCONTROL_TIMER_CLEAR BIT(2) +# define IRQCONTROL_FIFO_CLEAR BIT(3) +# define IRQCONTROL_ALLMASK 0xff +# define IRQCONTROL_ALLCLEAR 0x0f +# define IRQCONTROL_IRQDISABLE 0xf0 + +#define IRQSTATUS 0x00 +# define IRQSTATUS_SCSI BIT(0) +# define IRQSTATUS_TIMER BIT(2) +# define IRQSTATUS_FIFO BIT(3) +# define IRQSTATUS_MASK 0x0f + +#define IFSELECT 0x01 +# define IF_IFSEL BIT(0) +# define IF_REGSEL BIT(2) + +#define FIFOSTATUS 0x01 +# define FIFOSTATUS_CHIP_REVISION 0x0f +# define FIFOSTATUS_CHIP_ID 0x70 +# define FIFOSTATUS_FULL_EMPTY 0x80 + +#define INDEXREG 0x02 +#define DATAREG 0x03 +#define FIFODATA 0x04 +#define FIFODATA1 0x05 +#define FIFODATA2 0x06 +#define FIFODATA3 0x07 + +/* indexed register */ +#define EXTBUSCTRL 0x10 + +#define CLOCKDIV 0x11 +# define CLOCK_40M 0x02 +# define CLOCK_20M 0x01 + +#define TERMPWRCTRL 0x13 +# define POWER_ON BIT(0) + +#define SCSIIRQMODE 0x15 +# define SCSI_PHASE_CHANGE_EI BIT(0) +# define RESELECT_EI BIT(4) +# define FIFO_IRQ_EI BIT(5) +# define SCSI_RESET_IRQ_EI BIT(6) + +#define IRQPHASESENCE 0x16 +# define LATCHED_MSG BIT(0) +# define LATCHED_IO BIT(1) +# define LATCHED_CD BIT(2) +# define LATCHED_BUS_FREE BIT(3) +# define PHASE_CHANGE_IRQ BIT(4) +# define RESELECT_IRQ BIT(5) +# define FIFO_IRQ BIT(6) +# define SCSI_RESET_IRQ BIT(7) + +#define TIMERCOUNT 0x17 + +#define SCSIBUSCTRL 0x18 +# define SCSI_SEL BIT(0) +# define SCSI_RST BIT(1) +# define SCSI_DATAOUT_ENB BIT(2) +# define SCSI_ATN BIT(3) +# define SCSI_ACK BIT(4) +# define SCSI_BSY BIT(5) +# define AUTODIRECTION BIT(6) +# define ACKENB BIT(7) + +#define SCSIBUSMON 0x19 + +#define SETARBIT 0x1A +# define ARBIT_GO BIT(0) +# define ARBIT_FLAG_CLEAR BIT(1) + +#define ARBITSTATUS 0x1A +/*# define ARBIT_GO BIT(0)*/ +# define ARBIT_WIN BIT(1) +# define ARBIT_FAIL BIT(2) +# define RESELECT_FLAG BIT(3) + +#define PARITYCTRL 0x1B /* W */ +#define PARITYSTATUS 0x1B /* R */ + +#define COMMANDCTRL 0x1C /* W */ +# define CLEAR_COMMAND_POINTER BIT(0) +# define AUTO_COMMAND_GO BIT(1) + +#define RESELECTID 0x1C /* R */ +#define COMMANDDATA 0x1D + +#define POINTERCLR 0x1E /* W */ +# define POINTER_CLEAR BIT(0) +# define ACK_COUNTER_CLEAR BIT(1) +# define REQ_COUNTER_CLEAR BIT(2) +# define HOST_COUNTER_CLEAR BIT(3) +# define READ_SOURCE 0x30 + +#define TRANSFERCOUNT 0x1E /* R */ + +#define TRANSFERMODE 0x20 +# define MODE_MEM8 BIT(0) +# define MODE_MEM32 BIT(1) +# define MODE_ADR24 BIT(2) +# define MODE_ADR32 BIT(3) +# define MODE_IO8 BIT(4) +# define MODE_IO32 BIT(5) +# define TRANSFER_GO BIT(6) +# define BRAIND BIT(7) + +#define SYNCREG 0x21 +# define SYNCREG_OFFSET_MASK 0x0f +# define SYNCREG_PERIOD_MASK 0xf0 +# define SYNCREG_PERIOD_SHIFT 4 + +#define SCSIDATALATCH 0x22 +#define SCSIDATAIN 0x22 +#define SCSIDATAWITHACK 0x23 +#define SCAMCONTROL 0x24 +#define SCAMSTATUS 0x24 +#define SCAMDATA 0x25 + +#define OTHERCONTROL 0x26 +# define TPL_ROM_WRITE_EN BIT(0) +# define TPWR_OUT BIT(1) +# define TPWR_SENSE BIT(2) +# define RA8_CONTROL BIT(3) + +#define ACKWIDTH 0x27 +#define CLRTESTPNT 0x28 +#define ACKCNTLD 0x29 +#define REQCNTLD 0x2A +#define HSTCNTLD 0x2B +#define CHECKSUM 0x2C + +/* + * Input status bit definitions. + */ +#define S_ATN 0x80 /**/ +#define S_SELECT 0x40 /**/ +#define S_REQUEST 0x20 /* Request line from SCSI bus*/ +#define S_ACK 0x10 /* Acknowlege line from SCSI bus*/ +#define S_BUSY 0x08 /* Busy line from SCSI bus*/ +#define S_CD 0x04 /* Command/Data line from SCSI bus*/ +#define S_IO 0x02 /* Input/Output line from SCSI bus*/ +#define S_MESSAGE 0x01 /* Message line from SCSI bus*/ + +/* + * Useful Bus Monitor status combinations. + */ +#define BUSMON_SEL S_SELECT +#define BUSMON_BSY S_BUSY +#define BUSMON_REQ S_REQUEST +#define BUSMON_IO S_IO +#define BUSMON_ACK S_ACK +#define BUSMON_BUS_FREE 0 +#define BUSMON_COMMAND ( S_BUSY | S_CD | S_REQUEST ) +#define BUSMON_MESSAGE_IN ( S_BUSY | S_MESSAGE | S_IO | S_CD | S_REQUEST ) +#define BUSMON_MESSAGE_OUT ( S_BUSY | S_MESSAGE | S_CD | S_REQUEST ) +#define BUSMON_DATA_IN ( S_BUSY | S_IO | S_REQUEST ) +#define BUSMON_DATA_OUT ( S_BUSY | S_REQUEST ) +#define BUSMON_STATUS ( S_BUSY | S_IO | S_CD | S_REQUEST ) +#define BUSMON_RESELECT ( S_SELECT | S_IO ) +#define BUSMON_PHASE_MASK ( S_SELECT | S_CD | S_MESSAGE | S_IO ) + +#define BUSPHASE_COMMAND ( BUSMON_COMMAND & BUSMON_PHASE_MASK ) +#define BUSPHASE_MESSAGE_IN ( BUSMON_MESSAGE_IN & BUSMON_PHASE_MASK ) +#define BUSPHASE_MESSAGE_OUT ( BUSMON_MESSAGE_OUT & BUSMON_PHASE_MASK ) +#define BUSPHASE_DATA_IN ( BUSMON_DATA_IN & BUSMON_PHASE_MASK ) +#define BUSPHASE_DATA_OUT ( BUSMON_DATA_OUT & BUSMON_PHASE_MASK ) +#define BUSPHASE_STATUS ( BUSMON_STATUS & BUSMON_PHASE_MASK ) +#define BUSPHASE_SELECT ( S_SELECT | S_IO ) + +/* synchronous transfer negotiation data */ +typedef struct _sync_data { + unsigned int SyncNegotiation; +#define SYNC_NOT_YET 0 +#define SYNC_OK 1 +#define SYNC_NG 2 + + unsigned int SyncPeriod; + unsigned int SyncOffset; + unsigned char SyncRegister; + unsigned char AckWidth; +} sync_data; + +typedef struct _nsp_data { + unsigned int BaseAddress; + unsigned int NumAddress; + unsigned int IrqNumber; + + unsigned char ScsiClockDiv; + + unsigned char TransferMode; + + int TimerCount; + int SelectionTimeOut; + Scsi_Cmnd *CurrentSC; + + int FifoCount; +#if (KERNEL_VERSION(2,4,0) > LINUX_VERSION_CODE) + int Residual; +#define RESID nsp_data.Residual +#else +#define RESID SCpnt->resid +#endif + +#define MSGBUF_SIZE 20 + unsigned char MsgBuffer[MSGBUF_SIZE]; + int MsgLen; + +#define N_TARGET 8 +#define N_LUN 8 + sync_data Sync[N_TARGET][N_LUN]; +} nsp_hw_data; + + +static unsigned int nsphw_start_selection(Scsi_Cmnd *SCpnt); +static void nsp_start_timer(Scsi_Cmnd *SCpnt, int time); + +static int nsp_eh_bus_reset(Scsi_Cmnd *SCpnt); + +static int nsp_fifo_count(Scsi_Cmnd *SCpnt); +static void nsp_pio_read(Scsi_Cmnd *SCpnt); +static int nsp_nexus(Scsi_Cmnd *SCpnt); + +#ifdef PCMCIA_DEBUG +# ifdef DBG_SHOWCOMMAND +static void show_command(Scsi_Cmnd *ptr); +static void show_phase(Scsi_Cmnd *SCpnt); +static void show_busphase(unsigned char stat); +# endif /* DBG_SHOWCOMMAND */ +#endif + +/* + * SCSI phase + */ +enum _scsi_phase { + PH_UNDETERMINED, + PH_ARBSTART, + PH_SELSTART, + PH_SELECTED, + PH_COMMAND, + PH_DATA, + PH_STATUS, + PH_MSG_IN, + PH_MSG_OUT, + PH_DISCONNECT, + PH_RESELECT +}; + +enum _data_in_out { + IO_UNKNOWN, + IO_IN, + IO_OUT +}; + + +#define NSP_SELTIMEOUT 200 + +#endif /*__nsp_cs__*/ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/nsp_debug.c linux.ac/drivers/scsi/pcmcia/nsp_debug.c --- linux.vanilla/drivers/scsi/pcmcia/nsp_debug.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/pcmcia/nsp_debug.c Tue Apr 3 17:55:04 2001 @@ -0,0 +1,195 @@ +/*======================================================================== + Debug routines for nsp_cs + By: YOKOTA Hiroshi + + This software may be used and distributed according to the terms of + the GNU General Public License. +=========================================================================*/ + +/* $Id: nsp_debug.c,v 1.5 2001/02/08 08:08:58 elca Exp $ */ + +/* + * Show the command data of a command + */ +static const char unknown[] = "UNKNOWN"; + +static const char * group_0_commands[] = { +/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense", +/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks", +/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown, +/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry", +/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve", +/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit", +/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic", +/* 1e-1f */ "Prevent/Allow Medium Removal", unknown, +}; + + +static const char *group_1_commands[] = { +/* 20-22 */ unknown, unknown, unknown, +/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)", +/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown, +/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal", +/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position", +/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data", +/* 38-3c */ "Medium Scan", "Compare","Copy Verify", "Write Buffer", "Read Buffer", +/* 3d-3f */ "Update Block", "Read Long", "Write Long", +}; + + +static const char *group_2_commands[] = { +/* 40-41 */ "Change Definition", "Write Same", +/* 42-48 */ "Read Sub-Ch(cd)", "Read TOC", "Read Header(cd)", "Play Audio(cd)", unknown, "Play Audio MSF(cd)", "Play Audio Track/Index(cd)", +/* 49-4f */ "Play Track Relative(10)(cd)", unknown, "Pause/Resume(cd)", "Log Select", "Log Sense", unknown, unknown, +/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)", +/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown, +/* 5c-5f */ unknown, unknown, unknown, +}; + +#define group(opcode) (((opcode) >> 5) & 7) + +#define RESERVED_GROUP 0 +#define VENDOR_GROUP 1 +#define NOTEXT_GROUP 2 + +static const char **commands[] = { + group_0_commands, group_1_commands, group_2_commands, + (const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP, + (const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP, + (const char **) VENDOR_GROUP +}; + +static const char reserved[] = "RESERVED"; +static const char vendor[] = "VENDOR SPECIFIC"; + +static void print_opcodek(unsigned char opcode) +{ + const char **table = commands[ group(opcode) ]; + + switch ((unsigned long) table) { + case RESERVED_GROUP: + printk("%s[%02x] ", reserved, opcode); + break; + case NOTEXT_GROUP: + printk("%s(notext)[%02x] ", unknown, opcode); + break; + case VENDOR_GROUP: + printk("%s[%02x] ", vendor, opcode); + break; + default: + if (table[opcode & 0x1f] != unknown) + printk("%s[%02x] ", table[opcode & 0x1f], opcode); + else + printk("%s[%02x] ", unknown, opcode); + break; + } +} + +void print_commandk (unsigned char *command) +{ + int i,s; + printk(KERN_DEBUG); + print_opcodek(command[0]); + /*printk(KERN_DEBUG __FUNCTION__ " ");*/ + for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i) { + printk("%02x ", command[i]); + } + switch (COMMAND_SIZE(command[0])) { + case 6: + printk("LBA=%d len=%d", + (((unsigned int)command[1] & 0x0f) << 16) | + ( (unsigned int)command[2] << 8) | + ( (unsigned int)command[3] ), + (unsigned int)command[4] + ); + break; + case 10: + printk("LBA=%d len=%d", + ((unsigned int)command[2] << 24) | + ((unsigned int)command[3] << 16) | + ((unsigned int)command[4] << 8) | + ((unsigned int)command[5] ), + ((unsigned int)command[7] << 8) | + ((unsigned int)command[8] ) + ); + break; + case 12: + printk("LBA=%d len=%d", + ((unsigned int)command[2] << 24) | + ((unsigned int)command[3] << 16) | + ((unsigned int)command[4] << 8) | + ((unsigned int)command[5] ), + ((unsigned int)command[6] << 24) | + ((unsigned int)command[7] << 16) | + ((unsigned int)command[8] << 8) | + ((unsigned int)command[9] ) + ); + break; + default: + break; + } + printk("\n"); +} + +static void show_command(Scsi_Cmnd *ptr) +{ + print_commandk(ptr->cmnd); +} + +static void show_phase(Scsi_Cmnd *SCpnt) +{ + int i = SCpnt->SCp.phase; + + char *ph[] = { + "PH_UNDETERMINED", + "PH_ARBSTART", + "PH_SELSTART", + "PH_SELECTED", + "PH_COMMAND", + "PH_DATA", + "PH_STATUS", + "PH_MSG_IN", + "PH_MSG_OUT", + "PH_DISCONNECT", + "PH_RESELECT" + }; + + if ( i < PH_UNDETERMINED || i > PH_RESELECT ) { + printk(KERN_DEBUG "scsi phase: unknown(%d)\n", i); + return; + } + + printk(KERN_DEBUG "scsi phase: %s\n", ph[i]); + + return; +} + +static void show_busphase(unsigned char stat) +{ + switch(stat) { + case BUSPHASE_COMMAND: + printk(KERN_DEBUG "BUSPHASE_COMMAND\n"); + break; + case BUSPHASE_MESSAGE_IN: + printk(KERN_DEBUG "BUSPHASE_MESSAGE_IN\n"); + break; + case BUSPHASE_MESSAGE_OUT: + printk(KERN_DEBUG "BUSPHASE_MESSAGE_OUT\n"); + break; + case BUSPHASE_DATA_IN: + printk(KERN_DEBUG "BUSPHASE_DATA_IN\n"); + break; + case BUSPHASE_DATA_OUT: + printk(KERN_DEBUG "BUSPHASE_DATA_OUT\n"); + break; + case BUSPHASE_STATUS: + printk(KERN_DEBUG "BUSPHASE_STATUS\n"); + break; + case BUSPHASE_SELECT: + printk(KERN_DEBUG "BUSPHASE_SELECT\n"); + break; + default: + printk(KERN_DEBUG "BUSPHASE_other\n"); + break; + } +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/pcmcia/nsp_io.h linux.ac/drivers/scsi/pcmcia/nsp_io.h --- linux.vanilla/drivers/scsi/pcmcia/nsp_io.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/scsi/pcmcia/nsp_io.h Tue Apr 3 17:55:04 2001 @@ -0,0 +1,176 @@ +/* + NinjaSCSI I/O funtions + By: YOKOTA Hiroshi + + This software may be used and distributed according to the terms of + the GNU General Public License. + + */ + +/* $Id: nsp_io.h,v 1.8 2001/01/30 05:16:02 elca Exp $ */ + +#ifndef __NSP_IO_H__ +#define __NSP_IO_H__ + +static inline void nsp_write(unsigned int base, + unsigned int index, + unsigned char val); +static inline unsigned char nsp_read(unsigned int base, + unsigned int index); +static inline void nsp_index_write(unsigned int BaseAddr, + unsigned int Register, + unsigned char Value); +static inline unsigned char nsp_index_read(unsigned int BaseAddr, + unsigned int Register); + +/******************************************************************* + * Basic IO + */ + +static inline void nsp_write(unsigned int base, + unsigned int index, + unsigned char val) +{ + outb(val, (base + index)); +} + +static inline unsigned char nsp_read(unsigned int base, + unsigned int index) +{ + return inb(base + index); +} + + +/********************************************************************** + * Indexed IO + */ +static inline unsigned char nsp_index_read(unsigned int BaseAddr, + unsigned int Register) +{ + outb(Register, BaseAddr + INDEXREG); + return inb(BaseAddr + DATAREG); +} + +static inline void nsp_index_write(unsigned int BaseAddr, + unsigned int Register, + unsigned char Value) +{ + outb(Register, BaseAddr + INDEXREG); + outb(Value, BaseAddr + DATAREG); +} + +/********************************************************************* + * fifo func + */ + +/* read 8 bit FIFO */ +static inline void nsp_multi_read_1(unsigned int BaseAddr, + unsigned int Register, + void *buf, + unsigned long count) +{ + insb(BaseAddr + Register, buf, count); +} + +static inline void nsp_fifo8_read(unsigned int base, + void *buf, + unsigned long count) +{ + //DEBUG(0, __FUNCTION__ "() buf=0x%p, count=0x%lx\n", buf, count); + nsp_multi_read_1(base, FIFODATA, buf, count); +} + +/*--------------------------------------------------------------*/ + +/* read 16 bit FIFO */ +static inline void nsp_multi_read_2(unsigned int BaseAddr, + unsigned int Register, + void *buf, + unsigned long count) +{ + insw(BaseAddr + Register, buf, count); +} + +static inline void nsp_fifo16_read(unsigned int base, + void *buf, + unsigned long count) +{ + //DEBUG(0, __FUNCTION__ "() buf=0x%p, count=0x%lx*2\n", buf, count); + nsp_multi_read_2(base, FIFODATA, buf, count); +} + +/*--------------------------------------------------------------*/ + +/* read 32bit FIFO */ +static inline void nsp_multi_read_4(unsigned int BaseAddr, + unsigned int Register, + void *buf, + unsigned long count) +{ + insl(BaseAddr + Register, buf, count); +} + +static inline void nsp_fifo32_read(unsigned int base, + void *buf, + unsigned long count) +{ + //DEBUG(0, __FUNCTION__ "() buf=0x%p, count=0x%lx*4\n", buf, count); + nsp_multi_read_4(base, FIFODATA, buf, count); +} + +/*----------------------------------------------------------*/ + +/* write 8bit FIFO */ +static inline void nsp_multi_write_1(unsigned int BaseAddr, + unsigned int Register, + void *buf, + unsigned long count) +{ + outsb(BaseAddr + Register, buf, count); +} + +static inline void nsp_fifo8_write(unsigned int base, + void *buf, + unsigned long count) +{ + nsp_multi_write_1(base, FIFODATA, buf, count); +} + +/*---------------------------------------------------------*/ + +/* write 16bit FIFO */ +static inline void nsp_multi_write_2(unsigned int BaseAddr, + unsigned int Register, + void *buf, + unsigned long count) +{ + outsw(BaseAddr + Register, buf, count); +} + +static inline void nsp_fifo16_write(unsigned int base, + void *buf, + unsigned long count) +{ + nsp_multi_write_2(base, FIFODATA, buf, count); +} + +/*---------------------------------------------------------*/ + +/* write 32bit FIFO */ +static inline void nsp_multi_write_4(unsigned int BaseAddr, + unsigned int Register, + void *buf, + unsigned long count) +{ + outsl(BaseAddr + Register, buf, count); +} + +static inline void nsp_fifo32_write(unsigned int base, + void *buf, + unsigned long count) +{ + nsp_multi_write_4(base, FIFODATA, buf, count); +} + +#endif +/* end */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/qlogicfc.c linux.ac/drivers/scsi/qlogicfc.c --- linux.vanilla/drivers/scsi/qlogicfc.c Mon Apr 30 15:13:25 2001 +++ linux.ac/drivers/scsi/qlogicfc.c Mon Apr 30 15:24:18 2001 @@ -1870,6 +1870,11 @@ hostdata = (struct isp2x00_hostdata *) host->hostdata; + /* + * This cannot be right - PCI writes are posted + * (apparently this is hardware design flaw not software ?) + */ + outw(0x01, host->io_port + ISP_CTRL_STATUS); udelay(100); outw(HCCR_RESET, host->io_port + HOST_HCCR); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/qlogicisp.c linux.ac/drivers/scsi/qlogicisp.c --- linux.vanilla/drivers/scsi/qlogicisp.c Mon Apr 30 15:13:25 2001 +++ linux.ac/drivers/scsi/qlogicisp.c Mon Apr 30 15:24:18 2001 @@ -706,6 +706,7 @@ } host->this_id = hostdata->host_param.initiator_scsi_id; + host->max_sectors = 64; if (request_irq(host->irq, do_isp1020_intr_handler, SA_INTERRUPT | SA_SHIRQ, "qlogicisp", host)) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/qlogicpti.c linux.ac/drivers/scsi/qlogicpti.c --- linux.vanilla/drivers/scsi/qlogicpti.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/scsi/qlogicpti.c Tue Apr 3 17:55:04 2001 @@ -737,6 +737,7 @@ prom_getintdefault(qpti->sdev->bus->prom_node, "scsi-initiator-id", 7); qpti->qhost->this_id = qpti->scsi_id; + qpti->qhost->max_sectors = 64; printk("SCSI ID %d ", qpti->scsi_id); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi.c linux.ac/drivers/scsi/scsi.c --- linux.vanilla/drivers/scsi/scsi.c Sat May 26 16:53:13 2001 +++ linux.ac/drivers/scsi/scsi.c Sat May 26 17:06:37 2001 @@ -53,6 +53,7 @@ #include #include #include +#include #define __KERNEL_SYSCALLS__ @@ -1098,7 +1099,7 @@ * the response. We treat it as if the command never finished. * * Since serial_number is now 0, the error handler cound detect this - * situation and avoid to call the the low level driver abort routine. + * situation and avoid to call the low level driver abort routine. * (DB) * * FIXME(eric) - I believe that this test is now redundant, due to @@ -1373,7 +1374,7 @@ } static int scsi_register_host(Scsi_Host_Template *); -static void scsi_unregister_host(Scsi_Host_Template *); +static int scsi_unregister_host(Scsi_Host_Template *); /* * Function: scsi_release_commandblocks() @@ -1569,12 +1570,17 @@ if (!(buffer = (char *) __get_free_page(GFP_KERNEL))) return -ENOMEM; - copy_from_user(buffer, buf, length); + if(copy_from_user(buffer, buf, length)) + { + err =-EFAULT; + goto out; + } err = -EINVAL; + if (length < PAGE_SIZE) - buffer[length] ='\0'; - else if (buffer[length]) + buffer[length] = '\0'; + else if (buffer[PAGE_SIZE-1]) goto out; if (length < 11 || strncmp("scsi", buffer, 4)) @@ -1821,6 +1827,11 @@ return 1; /* Must be already loaded, or * no detect routine available */ + + /* If max_sectors isn't set, default to max */ + if (!tpnt->max_sectors) + tpnt->max_sectors = MAX_SECTORS; + pcount = next_scsi_host; /* The detect routine must carefully spinunlock/spinlock if @@ -1965,14 +1976,8 @@ /* * Similarly, this entry point should be called by a loadable module if it * is trying to remove a low level scsi driver from the system. - * - * Note - there is a fatal flaw in the deregister module function. - * There is no way to return a code that says 'I cannot be unloaded now'. - * The system relies entirely upon usage counts that are maintained, - * and the assumption is that if the usage count is 0, then the module - * can be unloaded. */ -static void scsi_unregister_host(Scsi_Host_Template * tpnt) +static int scsi_unregister_host(Scsi_Host_Template * tpnt) { int online_status; int pcount0, pcount; @@ -1984,6 +1989,9 @@ struct Scsi_Host *shpnt; char name[10]; /* host_no>=10^9? I don't think so. */ + /* get the big kernel lock, so we don't race with open() */ + lock_kernel(); + /* * First verify that this host adapter is completely free with no pending * commands @@ -1994,7 +2002,7 @@ if (SDpnt->host->hostt == tpnt && SDpnt->host->hostt->module && GET_USE_COUNT(SDpnt->host->hostt->module)) - return; + goto err_out; /* * FIXME(eric) - We need to find a way to notify the * low level driver that we are shutting down - via the @@ -2046,7 +2054,7 @@ } SDpnt->online = online_status; printk(KERN_ERR "Device busy???\n"); - return; + goto err_out; } /* * No, this device is really free. Mark it as such, and @@ -2072,7 +2080,7 @@ /* If something still attached, punt */ if (SDpnt->attached) { printk(KERN_ERR "Attached usage count = %d\n", SDpnt->attached); - return; + goto err_out; } devfs_unregister (SDpnt->de); } @@ -2180,6 +2188,13 @@ } } MOD_DEC_USE_COUNT; + + unlock_kernel(); + return 0; + +err_out: + unlock_kernel(); + return -1; } static int scsi_unregister_device(struct Scsi_Device_Template *tpnt); @@ -2261,12 +2276,13 @@ struct Scsi_Host *shpnt; struct Scsi_Device_Template *spnt; struct Scsi_Device_Template *prev_spnt; - + + lock_kernel(); /* * If we are busy, this is not going to fly. */ if (GET_USE_COUNT(tpnt->module) != 0) - return 0; + goto error_out; /* * Next, detach the devices from the driver. @@ -2303,11 +2319,15 @@ prev_spnt->next = spnt->next; MOD_DEC_USE_COUNT; + unlock_kernel(); /* * Final cleanup for the driver is done in the driver sources in the * cleanup function. */ return 0; +error_out: + unlock_kernel(); + return -1; } @@ -2344,22 +2364,24 @@ /* Reverse the actions taken above */ -void scsi_unregister_module(int module_type, void *ptr) +int scsi_unregister_module(int module_type, void *ptr) { + int retval = 0; + switch (module_type) { case MODULE_SCSI_HA: - scsi_unregister_host((Scsi_Host_Template *) ptr); + retval = scsi_unregister_host((Scsi_Host_Template *) ptr); break; case MODULE_SCSI_DEV: - scsi_unregister_device((struct Scsi_Device_Template *) ptr); - break; + retval = scsi_unregister_device((struct Scsi_Device_Template *)ptr); + break; /* The rest of these are not yet implemented. */ case MODULE_SCSI_CONST: case MODULE_SCSI_IOCTL: break; - default: + default:; } - return; + return retval; } #ifdef CONFIG_PROC_FS diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi.h linux.ac/drivers/scsi/scsi.h --- linux.vanilla/drivers/scsi/scsi.h Thu Feb 22 00:10:50 2001 +++ linux.ac/drivers/scsi/scsi.h Sat May 26 17:36:18 2001 @@ -617,6 +617,9 @@ unsigned remap:1; /* support remapping */ unsigned starved:1; /* unable to process commands because host busy */ + + // Flag to allow revalidate to succeed in sd_open + int allow_revalidate; }; @@ -668,7 +671,7 @@ unsigned short sr_use_sg; /* Number of pieces of scatter-gather */ unsigned short sr_sglist_len; /* size of malloc'd scatter-gather list */ unsigned sr_underflow; /* Return error if less than - this amount is transfered */ + this amount is transferred */ }; /* @@ -756,7 +759,7 @@ void *buffer; /* Data buffer */ unsigned underflow; /* Return error if less than - this amount is transfered */ + this amount is transferred */ unsigned old_underflow; /* save underflow here when reusing the * command for error handling */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi_error.c linux.ac/drivers/scsi/scsi_error.c --- linux.vanilla/drivers/scsi/scsi_error.c Tue Apr 3 17:32:21 2001 +++ linux.ac/drivers/scsi/scsi_error.c Tue Apr 3 17:55:04 2001 @@ -427,7 +427,8 @@ memcpy((void *) SCpnt->cmnd, (void *) generic_sense, sizeof(generic_sense)); - SCpnt->cmnd[1] = SCpnt->lun << 5; + if (SCpnt->device->scsi_level <= SCSI_2) + SCpnt->cmnd[1] = SCpnt->lun << 5; scsi_result = (!SCpnt->host->hostt->unchecked_isa_dma) ? &scsi_result0[0] : kmalloc(512, GFP_ATOMIC | GFP_DMA); @@ -496,7 +497,8 @@ memcpy((void *) SCpnt->cmnd, (void *) tur_command, sizeof(tur_command)); - SCpnt->cmnd[1] = SCpnt->lun << 5; + if (SCpnt->device->scsi_level <= SCSI_2) + SCpnt->cmnd[1] = SCpnt->lun << 5; /* * Zero the sense buffer. The SCSI spec mandates that any diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi_ioctl.c linux.ac/drivers/scsi/scsi_ioctl.c --- linux.vanilla/drivers/scsi/scsi_ioctl.c Mon Apr 30 15:13:25 2001 +++ linux.ac/drivers/scsi/scsi_ioctl.c Thu May 24 23:40:16 2001 @@ -79,7 +79,7 @@ * * *(char *) ((int *) arg)[2] the actual command byte. * - * Note that if more than MAX_BUF bytes are requested to be transfered, + * Note that if more than MAX_BUF bytes are requested to be transferred, * the ioctl will fail with error EINVAL. MAX_BUF can be increased in * the future by increasing the size that scsi_malloc will accept. * @@ -196,8 +196,8 @@ Scsi_Request *SRpnt; Scsi_Device *SDpnt; unsigned char opcode; - int inlen, outlen, cmdlen; - int needed, buf_needed; + unsigned int inlen, outlen, cmdlen; + unsigned int needed, buf_needed; int timeout, retries, result; int data_direction; @@ -256,7 +256,11 @@ cmdlen = COMMAND_SIZE(opcode); if (verify_area(VERIFY_READ, cmd_in, cmdlen + inlen)) + { + if(buf) + scsi_free(buf, buf_needed); return -EFAULT; + } __copy_from_user(cmd, cmd_in, cmdlen); @@ -268,7 +272,8 @@ /* * Set the lun field to the correct value. */ - cmd[1] = (cmd[1] & 0x1f) | (dev->lun << 5); + if (dev->scsi_level <= SCSI_2) + cmd[1] = (cmd[1] & 0x1f) | (dev->lun << 5); switch (opcode) { case FORMAT_UNIT: @@ -303,6 +308,8 @@ SRpnt = scsi_allocate_request(dev); if( SRpnt == NULL ) { + if (buf) + scsi_free(buf, buf_needed); return -EINTR; } @@ -317,11 +324,19 @@ sb_len = (sb_len > OMAX_SB_LEN) ? OMAX_SB_LEN : sb_len; if (copy_to_user(cmd_in, SRpnt->sr_sense_buffer, sb_len)) + { + if (buf) + scsi_free(buf, buf_needed); return -EFAULT; - } else + } + } else { if (copy_to_user(cmd_in, buf, outlen)) + { + if (buf) + scsi_free(buf, buf_needed); return -EFAULT; - + } + } result = SRpnt->sr_result; SDpnt = SRpnt->sr_device; @@ -379,6 +394,7 @@ int scsi_ioctl(Scsi_Device * dev, int cmd, void *arg) { char scsi_cmd[MAX_COMMAND_SIZE]; + char cmd_byte1; /* No idea how this happens.... */ if (!dev) @@ -393,14 +409,16 @@ if (!scsi_block_when_processing_errors(dev)) { return -ENODEV; } + cmd_byte1 = (dev->scsi_level <= SCSI_2) ? (dev->lun << 5) : 0; + switch (cmd) { case SCSI_IOCTL_GET_IDLUN: if (verify_area(VERIFY_WRITE, arg, sizeof(Scsi_Idlun))) return -EFAULT; - __put_user(dev->id - + (dev->lun << 8) - + (dev->channel << 16) + __put_user((dev->id & 0xff) + + ((dev->lun & 0xff) << 8) + + ((dev->channel & 0xff) << 16) + ((dev->host->host_no & 0xff) << 24), &((Scsi_Idlun *) arg)->dev_id); __put_user(dev->host->unique_id, &((Scsi_Idlun *) arg)->host_unique_id); @@ -434,7 +452,7 @@ if (!dev->removable || !dev->lockable) return 0; scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL; - scsi_cmd[1] = dev->lun << 5; + scsi_cmd[1] = cmd_byte1; scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0; scsi_cmd[4] = SCSI_REMOVAL_PREVENT; return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd, @@ -444,14 +462,14 @@ if (!dev->removable || !dev->lockable) return 0; scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL; - scsi_cmd[1] = dev->lun << 5; + scsi_cmd[1] = cmd_byte1; scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0; scsi_cmd[4] = SCSI_REMOVAL_ALLOW; return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd, IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES); case SCSI_IOCTL_TEST_UNIT_READY: scsi_cmd[0] = TEST_UNIT_READY; - scsi_cmd[1] = dev->lun << 5; + scsi_cmd[1] = cmd_byte1; scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0; scsi_cmd[4] = 0; return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd, @@ -459,7 +477,7 @@ break; case SCSI_IOCTL_START_UNIT: scsi_cmd[0] = START_STOP; - scsi_cmd[1] = dev->lun << 5; + scsi_cmd[1] = cmd_byte1; scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0; scsi_cmd[4] = 1; return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd, @@ -467,7 +485,7 @@ break; case SCSI_IOCTL_STOP_UNIT: scsi_cmd[0] = START_STOP; - scsi_cmd[1] = dev->lun << 5; + scsi_cmd[1] = cmd_byte1; scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0; scsi_cmd[4] = 0; return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd, diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi_lib.c linux.ac/drivers/scsi/scsi_lib.c --- linux.vanilla/drivers/scsi/scsi_lib.c Sat May 26 16:53:13 2001 +++ linux.ac/drivers/scsi/scsi_lib.c Thu May 17 14:13:55 2001 @@ -377,12 +377,15 @@ nsect = bh->b_size >> 9; blk_finished_io(nsect); req->bh = bh->b_reqnext; - req->nr_sectors -= nsect; - req->sector += nsect; bh->b_reqnext = NULL; sectors -= nsect; bh->b_end_io(bh, uptodate); if ((bh = req->bh) != NULL) { + req->hard_sector += nsect; + req->hard_nr_sectors -= nsect; + req->sector += nsect; + req->nr_sectors -= nsect; + req->current_nr_sectors = bh->b_size >> 9; if (req->nr_sectors < req->current_nr_sectors) { req->nr_sectors = req->current_nr_sectors; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi_merge.c linux.ac/drivers/scsi/scsi_merge.c --- linux.vanilla/drivers/scsi/scsi_merge.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/scsi/scsi_merge.c Tue Apr 3 23:30:07 2001 @@ -417,6 +417,9 @@ max_segments = 64; #endif + if ((req->nr_sectors + (bh->b_size >> 9)) > SHpnt->max_sectors) + return 0; + if (use_clustering) { /* * See if we can do this without creating another @@ -473,6 +476,9 @@ max_segments = 64; #endif + if ((req->nr_sectors + (bh->b_size >> 9)) > SHpnt->max_sectors) + return 0; + if (use_clustering) { /* * See if we can do this without creating another @@ -597,6 +603,13 @@ Scsi_Device *SDpnt; struct Scsi_Host *SHpnt; + /* + * First check if the either of the requests are re-queued + * requests. Can't merge them if they are. + */ + if (req->special || next->special) + return 0; + SDpnt = (Scsi_Device *) q->queuedata; SHpnt = SDpnt->host; @@ -624,6 +637,10 @@ return 0; } #endif + + if ((req->nr_sectors + next->nr_sectors) > SHpnt->max_sectors) + return 0; + /* * The main question is whether the two segments at the boundaries * would be considered one or two. diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi_obsolete.c linux.ac/drivers/scsi/scsi_obsolete.c --- linux.vanilla/drivers/scsi/scsi_obsolete.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/scsi/scsi_obsolete.c Tue Apr 3 17:55:04 2001 @@ -223,7 +223,8 @@ memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); - SCpnt->cmnd[1] = SCpnt->lun << 5; + if (SCpnt->device->scsi_level <= SCSI_2) + SCpnt->cmnd[1] = SCpnt->lun << 5; SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); SCpnt->request_buffer = &SCpnt->sense_buffer; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi_proc.c linux.ac/drivers/scsi/scsi_proc.c --- linux.vanilla/drivers/scsi/scsi_proc.c Sat May 26 16:53:13 2001 +++ linux.ac/drivers/scsi/scsi_proc.c Sat May 26 00:30:09 2001 @@ -99,6 +99,9 @@ char * page; char *start; + if (hpnt->hostt->proc_info == NULL) + ret = -ENOSYS; + if (count > PROC_BLOCK_SIZE) return -EOVERFLOW; @@ -110,11 +113,9 @@ return -EFAULT; } - if (hpnt->hostt->proc_info == NULL) - ret = -ENOSYS; - else - ret = hpnt->hostt->proc_info(page, &start, 0, count, - hpnt->host_no, 1); + ret = hpnt->hostt->proc_info(page, &start, 0, count, + hpnt->host_no, 1); + free_page((ulong) page); return(ret); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/scsi_scan.c linux.ac/drivers/scsi/scsi_scan.c --- linux.vanilla/drivers/scsi/scsi_scan.c Mon Apr 30 15:13:25 2001 +++ linux.ac/drivers/scsi/scsi_scan.c Tue May 22 18:40:16 2001 @@ -40,9 +40,13 @@ #define BLIST_ISROM 0x200 static void print_inquiry(unsigned char *data); -static int scan_scsis_single(int channel, int dev, int lun, int *max_scsi_dev, - int *sparse_lun, Scsi_Device ** SDpnt, - struct Scsi_Host *shpnt, char *scsi_result); +static int scan_scsis_single(unsigned int channel, unsigned int dev, + unsigned int lun, int lun0_scsi_level, + unsigned int *max_scsi_dev, unsigned int *sparse_lun, + Scsi_Device ** SDpnt, struct Scsi_Host *shpnt, + char *scsi_result); +static int find_lun0_scsi_level(unsigned int channel, unsigned int dev, + struct Scsi_Host *shpnt); struct dev_info { const char *vendor; @@ -135,6 +139,7 @@ {"nCipher", "Fastness Crypto", "*", BLIST_FORCELUN}, {"DEC","HSG80","*", BLIST_FORCELUN}, {"COMPAQ","LOGICAL VOLUME","*", BLIST_FORCELUN}, + {"COMPAQ","CR3500","*", BLIST_FORCELUN}, {"NEC", "PD-1 ODX654P", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"MATSHITA", "PD-1", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN}, @@ -143,6 +148,9 @@ {"MegaRAID", "LD", "*", BLIST_FORCELUN}, {"DGC", "RAID", "*", BLIST_SPARSELUN}, // Dell PV 650F (tgt @ LUN 0) {"DGC", "DISK", "*", BLIST_SPARSELUN}, // Dell PV 650F (no tgt @ LUN 0) + {"DELL", "PV660F", "*", BLIST_SPARSELUN}, + {"DELL", "PV660F PSEUDO", "*", BLIST_SPARSELUN}, + {"DELL", "PSEUDO DEVICE .", "*", BLIST_SPARSELUN}, // Dell PV 530F {"DELL", "PV530F", "*", BLIST_SPARSELUN}, // Dell PV 530F {"SONY", "TSL", "*", BLIST_FORCELUN}, // DDS3 & DDS4 autoloaders {"DELL", "PERCRAID", "*", BLIST_FORCELUN}, @@ -154,29 +162,31 @@ {NULL, NULL, NULL} }; +#define MAX_SCSI_LUNS 0xFFFFFFFF + #ifdef CONFIG_SCSI_MULTI_LUN -static int max_scsi_luns = 8; +static unsigned int max_scsi_luns = MAX_SCSI_LUNS; #else -static int max_scsi_luns = 1; +static unsigned int max_scsi_luns = 1; #endif #ifdef MODULE MODULE_PARM(max_scsi_luns, "i"); -MODULE_PARM_DESC(max_scsi_luns, "last scsi LUN (should be between 1 and 8)"); +MODULE_PARM_DESC(max_scsi_luns, "last scsi LUN (should be between 1 and 2^32-1)"); #else static int __init scsi_luns_setup(char *str) { - int tmp; + unsigned int tmp; if (get_option(&str, &tmp) == 1) { max_scsi_luns = tmp; return 1; } else { printk("scsi_luns_setup : usage max_scsi_luns=n " - "(n should be between 1 and 8)\n"); + "(n should be between 1 and 2^32-1)\n"); return 0; } } @@ -264,14 +274,15 @@ uint hlun) { uint channel; - int dev; - int lun; - int max_dev_lun; + unsigned int dev; + unsigned int lun; + unsigned int max_dev_lun; unsigned char *scsi_result; unsigned char scsi_result0[256]; Scsi_Device *SDpnt; Scsi_Device *SDtail; - int sparse_lun; + unsigned int sparse_lun; + int lun0_sl; scsi_result = NULL; @@ -343,8 +354,12 @@ lun = hlun; if (lun >= shpnt->max_lun) goto leave; - scan_scsis_single(channel, dev, lun, &max_dev_lun, &sparse_lun, - &SDpnt, shpnt, scsi_result); + if ((0 == lun) || (lun > 7)) + lun0_sl = SCSI_3; /* actually don't care for 0 == lun */ + else + lun0_sl = find_lun0_scsi_level(channel, dev, shpnt); + scan_scsis_single(channel, dev, lun, lun0_sl, &max_dev_lun, + &sparse_lun, &SDpnt, shpnt, scsi_result); if (SDpnt != oldSDpnt) { /* it could happen the blockdevice hasn't yet been inited */ @@ -400,12 +415,14 @@ max_dev_lun = (max_scsi_luns < shpnt->max_lun ? max_scsi_luns : shpnt->max_lun); sparse_lun = 0; - for (lun = 0; lun < max_dev_lun; ++lun) { - if (!scan_scsis_single(channel, order_dev, lun, &max_dev_lun, - &sparse_lun, &SDpnt, shpnt, - scsi_result) + for (lun = 0, lun0_sl = SCSI_2; lun < max_dev_lun; ++lun) { + if (!scan_scsis_single(channel, order_dev, lun, lun0_sl, + &max_dev_lun, &sparse_lun, &SDpnt, shpnt, + scsi_result) && !sparse_lun) break; /* break means don't probe further for luns!=0 */ + if (SDpnt && (0 == lun)) + lun0_sl = SDpnt->scsi_level; } /* for lun ends */ } /* if this_id != id ends */ } /* for dev ends */ @@ -461,9 +478,11 @@ * Returning 0 means Please don't ask further for lun!=0, 1 means OK go on. * Global variables used : scsi_devices(linked list) */ -static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, - int *sparse_lun, Scsi_Device ** SDpnt2, - struct Scsi_Host *shpnt, char *scsi_result) +static int scan_scsis_single(unsigned int channel, unsigned int dev, + unsigned int lun, int lun0_scsi_level, + unsigned int *max_dev_lun, unsigned int *sparse_lun, + Scsi_Device ** SDpnt2, struct Scsi_Host *shpnt, + char *scsi_result) { char devname[64]; unsigned char scsi_cmd[MAX_COMMAND_SIZE]; @@ -509,7 +528,10 @@ * Build an INQUIRY command block. */ scsi_cmd[0] = INQUIRY; - scsi_cmd[1] = (lun << 5) & 0xe0; + if ((lun > 0) && (lun0_scsi_level <= SCSI_2)) + scsi_cmd[1] = (lun << 5) & 0xe0; + else + scsi_cmd[1] = 0; /* SCSI_3 and higher, don't touch */ scsi_cmd[2] = 0; scsi_cmd[3] = 0; scsi_cmd[4] = 255; @@ -665,7 +687,9 @@ printk("Unlocked floptical drive.\n"); SDpnt->lockable = 0; scsi_cmd[0] = MODE_SENSE; - scsi_cmd[1] = (lun << 5) & 0xe0; + if (shpnt->max_lun <= 8) + scsi_cmd[1] = (lun << 5) & 0xe0; + else scsi_cmd[1] = 0; /* any other idea? */ scsi_cmd[2] = 0x2e; scsi_cmd[3] = 0; scsi_cmd[4] = 0x2a; @@ -748,7 +772,19 @@ * other settings, and scan all of them. */ if (bflags & BLIST_SPARSELUN) { - *max_dev_lun = 8; + /* + * Scanning MAX_SCSI_LUNS units would be a bad idea. + * Any better idea? + * I think we need REPORT LUNS in future to avoid scanning + * of unused LUNs. But, that is another item. + * + * FIXME(eric) - perhaps this should be a kernel configurable? + */ + if (*max_dev_lun < shpnt->max_lun) + *max_dev_lun = shpnt->max_lun; + else if ((max_scsi_luns >> 1) >= *max_dev_lun) + *max_dev_lun += shpnt->max_lun; + else *max_dev_lun = max_scsi_luns; *sparse_lun = 1; return 1; } @@ -757,7 +793,17 @@ * settings, and scan all of them. */ if (bflags & BLIST_FORCELUN) { - *max_dev_lun = 8; + /* + * Scanning MAX_SCSI_LUNS units would be a bad idea. + * Any better idea? + * I think we need REPORT LUNS in future to avoid scanning + * of unused LUNs. But, that is another item. + */ + if (*max_dev_lun < shpnt->max_lun) + *max_dev_lun = shpnt->max_lun; + else if ((max_scsi_luns >> 1) >= *max_dev_lun) + *max_dev_lun += shpnt->max_lun; + else *max_dev_lun = max_scsi_luns; return 1; } /* @@ -781,3 +827,23 @@ return 1; } +/* + * The worker for scan_scsis. + * Returns the scsi_level of lun0 on this host, channel and dev (if already + * known), otherwise returns SCSI_2. + */ +static int find_lun0_scsi_level(unsigned int channel, unsigned int dev, + struct Scsi_Host *shpnt) +{ + int res = SCSI_2; + Scsi_Device *SDpnt; + + for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) + { + if ((0 == SDpnt->lun) && (dev == SDpnt->id) && + (channel == SDpnt->channel)) + return (int)SDpnt->scsi_level; + } + /* haven't found lun0, should send INQUIRY but take easy route */ + return res; +} diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sd.c linux.ac/drivers/scsi/sd.c --- linux.vanilla/drivers/scsi/sd.c Sat May 26 16:53:13 2001 +++ linux.ac/drivers/scsi/sd.c Sat May 26 00:36:09 2001 @@ -22,6 +22,12 @@ * * Modified by Torben Mathiasen tmm@image.dk * Resource allocation fixes in sd_init and cleanups. + * + * Modified by Alex Davis + * Fix problem where partition info not being read in sd_open. + * + * Modified by Alex Davis + * Fix problem where removable media could be ejected after sd_open. */ #include @@ -372,7 +378,8 @@ (SCpnt->request.cmd == WRITE) ? "writing" : "reading", this_count, SCpnt->request.nr_sectors)); - SCpnt->cmnd[1] = (SCpnt->lun << 5) & 0xe0; + SCpnt->cmnd[1] = (SCpnt->device->scsi_level <= SCSI_2) ? + ((SCpnt->lun << 5) & 0xe0) : 0; if (((this_count > 0xff) || (block > 0x1fffff)) || SCpnt->device->ten) { if (this_count > 0xffff) @@ -424,7 +431,7 @@ static int sd_open(struct inode *inode, struct file *filp) { - int target; + int target, retval = -ENXIO; Scsi_Device * SDev; target = DEVICE_NR(inode->i_rdev); @@ -448,24 +455,40 @@ while (rscsi_disks[target].device->busy) barrier(); + /* + * The following code can sleep. + * Module unloading must be prevented + */ + SDev = rscsi_disks[target].device; + if (SDev->host->hostt->module) + __MOD_INC_USE_COUNT(SDev->host->hostt->module); + if (sd_template.module) + __MOD_INC_USE_COUNT(sd_template.module); + SDev->access_count++; + if (rscsi_disks[target].device->removable) { + SDev->allow_revalidate = 1; check_disk_change(inode->i_rdev); + SDev->allow_revalidate = 0; /* * If the drive is empty, just let the open fail. */ - if (!rscsi_disks[target].ready) - return -ENXIO; + if ((!rscsi_disks[target].ready) && !(filp->f_flags & O_NDELAY)) { + retval = -ENOMEDIUM; + goto error_out; + } /* * Similarly, if the device has the write protect tab set, * have the open fail if the user expects to be able to write * to the thing. */ - if ((rscsi_disks[target].write_prot) && (filp->f_mode & 2)) - return -EROFS; + if ((rscsi_disks[target].write_prot) && (filp->f_mode & 2)) { + retval = -EROFS; + goto error_out; + } } - SDev = rscsi_disks[target].device; /* * It is possible that the disk changing stuff resulted in the device * being taken offline. If this is the case, report this to the user, @@ -473,26 +496,31 @@ * the open actually succeeded. */ if (!SDev->online) { - return -ENXIO; + goto error_out; } /* * See if we are requesting a non-existent partition. Do this * after checking for disk change. */ - if (sd_sizes[SD_PARTITION(inode->i_rdev)] == 0) - return -ENXIO; + if (sd_sizes[SD_PARTITION(inode->i_rdev)] == 0) { + goto error_out; + } if (SDev->removable) - if (!SDev->access_count) + if (SDev->access_count==1) if (scsi_block_when_processing_errors(SDev)) scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, NULL); - SDev->access_count++; + + return 0; + +error_out: + SDev->access_count--; if (SDev->host->hostt->module) - __MOD_INC_USE_COUNT(SDev->host->hostt->module); + __MOD_DEC_USE_COUNT(SDev->host->hostt->module); if (sd_template.module) - __MOD_INC_USE_COUNT(sd_template.module); - return 0; + __MOD_DEC_USE_COUNT(sd_template.module); + return retval; } static int sd_release(struct inode *inode, struct file *file) @@ -746,7 +774,8 @@ while (retries < 3) { cmd[0] = TEST_UNIT_READY; - cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0; + cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ? + ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0; memset((void *) &cmd[2], 0, 8); SRpnt->sr_cmd_len = 0; SRpnt->sr_sense_buffer[0] = 0; @@ -787,7 +816,8 @@ if (!spintime) { printk("%s: Spinning up disk...", nbuff); cmd[0] = START_STOP; - cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0; + cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ? + ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0; cmd[1] |= 1; /* Return immediately */ memset((void *) &cmd[2], 0, 8); cmd[4] = 1; /* Start spin cycle */ @@ -820,7 +850,8 @@ retries = 3; do { cmd[0] = READ_CAPACITY; - cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0; + cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ? + ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0; memset((void *) &cmd[2], 0, 8); memset((void *) buffer, 0, 8); SRpnt->sr_cmd_len = 0; @@ -978,7 +1009,8 @@ memset((void *) &cmd[0], 0, 8); cmd[0] = MODE_SENSE; - cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0; + cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ? + ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0; cmd[2] = 0x3f; /* Get all pages */ cmd[4] = 8; /* But we only want the 8 byte header */ SRpnt->sr_cmd_len = 0; @@ -1184,16 +1216,9 @@ static int sd_detect(Scsi_Device * SDp) { - char nbuff[6]; if (SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0; - - sd_devname(sd_template.dev_noticed++, nbuff); - printk("Detected scsi %sdisk %s at scsi%d, channel %d, id %d, lun %d\n", - SDp->removable ? "removable " : "", - nbuff, - SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); - + sd_template.dev_noticed++; return 1; } @@ -1202,6 +1227,7 @@ unsigned int devnum; Scsi_Disk *dpnt; int i; + char nbuff[6]; if (SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0; @@ -1225,10 +1251,15 @@ SD_GENDISK(i).de_arr[devnum] = SDp->de; if (SDp->removable) SD_GENDISK(i).flags[devnum] |= GENHD_FL_REMOVABLE; + sd_devname(i, nbuff); + printk("Attached scsi %sdisk %s at scsi%d, channel %d, id %d, lun %d\n", + SDp->removable ? "removable " : "", + nbuff, SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); return 0; } #define DEVICE_BUSY rscsi_disks[target].device->busy +#define ALLOW_REVALIDATE rscsi_disks[target].device->allow_revalidate #define USAGE rscsi_disks[target].device->access_count #define CAPACITY rscsi_disks[target].capacity #define MAYBE_REINIT sd_init_onedisk(target) @@ -1249,7 +1280,7 @@ target = DEVICE_NR(dev); - if (DEVICE_BUSY || USAGE > maxusage) { + if (DEVICE_BUSY || (ALLOW_REVALIDATE == 0 && USAGE > maxusage)) { printk("Device busy for revalidation (usage=%d)\n", USAGE); return -EBUSY; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sg.c linux.ac/drivers/scsi/sg.c --- linux.vanilla/drivers/scsi/sg.c Mon Jan 15 21:08:15 2001 +++ linux.ac/drivers/scsi/sg.c Tue May 8 18:16:00 2001 @@ -7,7 +7,7 @@ * Original driver (sg.c): * Copyright (C) 1992 Lawrence Foard * Version 2 and 3 extensions to driver: - * Copyright (C) 1998 - 2000 Douglas Gilbert + * Copyright (C) 1998 - 2001 Douglas Gilbert * * Modified 19-JAN-1998 Richard Gooch Devfs support * @@ -19,9 +19,9 @@ */ #include #ifdef CONFIG_PROC_FS - static char * sg_version_str = "Version: 3.1.17 (20001002)"; + static char sg_version_str[] = "Version: 3.1.18 (20010505)"; #endif - static int sg_version_num = 30117; /* 2 digits for each component */ + static int sg_version_num = 30118; /* 2 digits for each component */ /* * D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes: * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First @@ -76,8 +76,9 @@ #include #endif /* LINUX_VERSION_CODE */ -/* #define SG_ALLOW_DIO */ -#ifdef SG_ALLOW_DIO +#define SG_ALLOW_DIO_DEF 0 +#define SG_ALLOW_DIO_CODE /* compile out be commenting this define */ +#ifdef SG_ALLOW_DIO_CODE #include #endif @@ -89,6 +90,7 @@ readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into the kernel (i.e. it is not a module).] */ static int def_reserved_size = -1; /* picks up init parameter */ +static int sg_allow_dio = SG_ALLOW_DIO_DEF; #define SG_SECTOR_SZ 512 #define SG_SECTOR_MSK (SG_SECTOR_SZ - 1) @@ -112,7 +114,7 @@ static int sg_detect(Scsi_Device *); static void sg_detach(Scsi_Device *); -static Scsi_Request * dummy_cmdp = 0; /* only used for sizeof */ +static Scsi_Request * dummy_cmdp; /* only used for sizeof */ static rwlock_t sg_dev_arr_lock = RW_LOCK_UNLOCKED; /* Also used to lock file descriptor list for device */ @@ -157,7 +159,7 @@ char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ char orphan; /* 1 -> drop on sight, 0 -> normal */ char sg_io_owned; /* 1 -> packet belongs to SG_IO */ - char done; /* 0->before bh, 1->before read, 2->read */ + volatile char done; /* 0->before bh, 1->before read, 2->read */ } Sg_request; /* 168 bytes long on i386 */ typedef struct sg_fd /* holds the state of a file descriptor */ @@ -174,12 +176,12 @@ Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ char low_dma; /* as in parent but possibly overridden to 1 */ char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ - char closed; /* 1 -> fd closed but request(s) outstanding */ + volatile char closed; /* 1 -> fd closed but request(s) outstanding */ char fd_mem_src; /* heap whereabouts of this Sg_fd object */ char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ char next_cmd_len; /* 0 -> automatic (def), >0 -> use on next write() */ char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ -} Sg_fd; /* 2768 bytes long on i386 */ +} Sg_fd; /* 2760 bytes long on i386 */ typedef struct sg_device /* holds the state of each scsi generic device */ { @@ -189,10 +191,10 @@ Sg_fd * headfp; /* first open fd belonging to this device */ devfs_handle_t de; kdev_t i_rdev; /* holds device major+minor number */ - char exclude; /* opened for exclusive access */ + volatile char detached; /* 0->attached, 1->detached pending removal */ + volatile char exclude; /* opened for exclusive access */ char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ - char detached; /* 0->attached, 1->detached pending removal */ -} Sg_device; /* 44 bytes long on i386 */ +} Sg_device; /* 36 bytes long on i386 */ static int sg_fasync(int fd, struct file * filp, int mode); @@ -225,13 +227,12 @@ static void sg_low_free(char * buff, int size, int mem_src); static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev); static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp); +static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp); static Sg_request * sg_get_rq_mark(Sg_fd * sfp, int pack_id); static Sg_request * sg_add_request(Sg_fd * sfp); static int sg_remove_request(Sg_fd * sfp, Sg_request * srp); static int sg_res_in_use(Sg_fd * sfp); -static int sg_dio_in_use(Sg_fd * sfp); static void sg_clr_srpnt(Scsi_Request * SRpnt); -static void sg_shorten_timeout(Scsi_Request * srpnt); static int sg_ms_to_jif(unsigned int msecs); static unsigned sg_jif_to_ms(int jifs); static int sg_allow_access(unsigned char opcode, char dev_type); @@ -244,10 +245,10 @@ static Sg_device ** sg_dev_arr = NULL; -static const int size_sg_header = sizeof(struct sg_header); -static const int size_sg_io_hdr = sizeof(sg_io_hdr_t); -static const int size_sg_iovec = sizeof(sg_iovec_t); -static const int size_sg_req_info = sizeof(sg_req_info_t); +#define SZ_SG_HEADER sizeof(struct sg_header) +#define SZ_SG_IO_HDR sizeof(sg_io_hdr_t) +#define SZ_SG_IOVEC sizeof(sg_iovec_t) +#define SZ_SG_REQ_INFO sizeof(sg_req_info_t) static int sg_open(struct inode * inode, struct file * filp) @@ -257,43 +258,56 @@ Sg_device * sdp; Sg_fd * sfp; int res; + int retval = -EBUSY; + SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags)); sdp = sg_get_dev(dev); - if ((! sdp) || (! sdp->device) || (! sdp->device->host)) - return -ENXIO; - if (sdp->i_rdev != inode->i_rdev) - printk("sg_open: inode maj=%d, min=%d sdp maj=%d, min=%d\n", - MAJOR(inode->i_rdev), MINOR(inode->i_rdev), - MAJOR(sdp->i_rdev), MINOR(sdp->i_rdev)); - /* If we are in the middle of error recovery, don't let anyone - * else try and use this device. Also, if error recovery fails, it - * may try and take the device offline, in which case all further - * access to the device is prohibited. */ - if (! ((flags & O_NONBLOCK) || - scsi_block_when_processing_errors(sdp->device))) + if ((! sdp) || (! sdp->device)) return -ENXIO; + if (sdp->detached) + return -ENODEV; - SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags)); + /* This driver's module count bumped by fops_get in */ + /* Prevent the device driver from vanishing while we sleep */ + if (sdp->device->host->hostt->module) + __MOD_INC_USE_COUNT(sdp->device->host->hostt->module); + + if (! ((flags & O_NONBLOCK) || + scsi_block_when_processing_errors(sdp->device))) { + retval = -ENXIO; + /* we are in error recovery for this device */ + goto error_out; + } if (flags & O_EXCL) { - if (O_RDONLY == (flags & O_ACCMODE)) - return -EACCES; /* Can't lock it with read only access */ - if (sdp->headfp && (flags & O_NONBLOCK)) - return -EBUSY; - res = 0; /* following is a macro that beats race condition */ + if (O_RDONLY == (flags & O_ACCMODE)) { + retval = -EACCES; /* Can't lock it with read only access */ + goto error_out; + } + if (sdp->headfp && (flags & O_NONBLOCK)) + goto error_out; + res = 0; __wait_event_interruptible(sdp->o_excl_wait, ((sdp->headfp || sdp->exclude) ? 0 : (sdp->exclude = 1)), res); - if (res) - return res; /* -ERESTARTSYS because signal hit process */ + if (res) { + retval = res; /* -ERESTARTSYS because signal hit process */ + goto error_out; + } } else if (sdp->exclude) { /* some other fd has an exclusive lock on dev */ if (flags & O_NONBLOCK) - return -EBUSY; - res = 0; /* following is a macro that beats race condition */ + goto error_out; + res = 0; __wait_event_interruptible(sdp->o_excl_wait, (! sdp->exclude), res); - if (res) - return res; /* -ERESTARTSYS because signal hit process */ + if (res) { + retval = res; /* -ERESTARTSYS because signal hit process */ + goto error_out; + } + } + if (sdp->detached) { + retval = -ENODEV; + goto error_out; } if (! sdp->headfp) { /* no existing opens on this device */ sdp->sgdebug = 0; @@ -303,12 +317,15 @@ filp->private_data = sfp; else { if (flags & O_EXCL) sdp->exclude = 0; /* undo if error */ - return -ENOMEM; + retval = -ENOMEM; + goto error_out; } - - if (sdp->device->host->hostt->module) - __MOD_INC_USE_COUNT(sdp->device->host->hostt->module); return 0; + +error_out: + if ((! sdp->detached) && sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); + return retval; } /* Following function was formerly called 'sg_close' */ @@ -324,14 +341,12 @@ } SCSI_LOG_TIMEOUT(3, printk("sg_release: dev=%d\n", MINOR(sdp->i_rdev))); sg_fasync(-1, filp, 0); /* remove filp from async notification list */ - sg_remove_sfp(sdp, sfp); - if (! sdp->headfp) - filp->private_data = NULL; - - if (sdp->device->host->hostt->module) - __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); - sdp->exclude = 0; - wake_up_interruptible(&sdp->o_excl_wait); + if (0 == sg_remove_sfp(sdp, sfp)) { /* Returns 1 when sdp gone */ + if ((! sdp->detached) && sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); + sdp->exclude = 0; + wake_up_interruptible(&sdp->o_excl_wait); + } unlock_kernel(); return 0; } @@ -356,11 +371,11 @@ ; /* FIXME: Hmm. Seek to the right place, or fail? */ if ((k = verify_area(VERIFY_WRITE, buf, count))) return k; - if (sfp->force_packid && (count >= size_sg_header)) { - __copy_from_user(&old_hdr, buf, size_sg_header); + if (sfp->force_packid && (count >= SZ_SG_HEADER)) { + __copy_from_user(&old_hdr, buf, SZ_SG_HEADER); if (old_hdr.reply_len < 0) { - if (count >= size_sg_io_hdr) { - __copy_from_user(&new_hdr, buf, size_sg_io_hdr); + if (count >= SZ_SG_IO_HDR) { + __copy_from_user(&new_hdr, buf, SZ_SG_IO_HDR); req_pack_id = new_hdr.pack_id; } } @@ -369,25 +384,26 @@ } srp = sg_get_rq_mark(sfp, req_pack_id); if (! srp) { /* now wait on packet to arrive */ + if (sdp->detached) + return -ENODEV; if (filp->f_flags & O_NONBLOCK) return -EAGAIN; while (1) { - int dio = sg_dio_in_use(sfp); res = 0; /* following is a macro that beats race condition */ - __wait_event_interruptible(sfp->read_wait, - (srp = sg_get_rq_mark(sfp, req_pack_id)), - res); + __wait_event_interruptible(sfp->read_wait, (sdp->detached || + (srp = sg_get_rq_mark(sfp, req_pack_id))), res); + if (sdp->detached) + return -ENODEV; if (0 == res) break; - else if (! dio) /* only let signal out if no dio */ - return res; /* -ERESTARTSYS because signal hit process */ + return res; /* -ERESTARTSYS because signal hit process */ } } if (srp->header.interface_id != '\0') return sg_new_read(sfp, buf, count, srp); hp = &srp->header; - memset(&old_hdr, 0, size_sg_header); + memset(&old_hdr, 0, SZ_SG_HEADER); old_hdr.reply_len = (int)hp->timeout; old_hdr.pack_len = old_hdr.reply_len; /* very old, strange behaviour */ old_hdr.pack_id = hp->pack_id; @@ -430,13 +446,13 @@ } /* Now copy the result back to the user buffer. */ - if (count >= size_sg_header) { - __copy_to_user(buf, &old_hdr, size_sg_header); - buf += size_sg_header; + if (count >= SZ_SG_HEADER) { + __copy_to_user(buf, &old_hdr, SZ_SG_HEADER); + buf += SZ_SG_HEADER; if (count > old_hdr.reply_len) count = old_hdr.reply_len; - if (count > size_sg_header) - sg_read_oxfer(srp, buf, count - size_sg_header); + if (count > SZ_SG_HEADER) + sg_read_oxfer(srp, buf, count - SZ_SG_HEADER); } else count = (old_hdr.result == 0) ? 0 : -EIO; @@ -450,7 +466,7 @@ sg_io_hdr_t * hp = &srp->header; int k, len; - if (count < size_sg_io_hdr) + if (count < SZ_SG_IO_HDR) return -EINVAL; hp->sb_len_wr = 0; if ((hp->mx_sb_len > 0) && hp->sbp) { @@ -468,7 +484,7 @@ } if (hp->masked_status || hp->host_status || hp->driver_status) hp->info |= SG_INFO_CHECK; - copy_to_user(buf, hp, size_sg_io_hdr); + copy_to_user(buf, hp, SZ_SG_IO_HDR); k = sg_read_xfer(srp); if (k) return k; /* probably -EFAULT, bad addr in dxferp or iovec list */ @@ -494,7 +510,8 @@ return -ENXIO; SCSI_LOG_TIMEOUT(3, printk("sg_write: dev=%d, count=%d\n", MINOR(sdp->i_rdev), (int)count)); - + if (sdp->detached) + return -ENODEV; if (! ((filp->f_flags & O_NONBLOCK) || scsi_block_when_processing_errors(sdp->device))) return -ENXIO; @@ -503,20 +520,20 @@ if ((k = verify_area(VERIFY_READ, buf, count))) return k; /* protects following copy_from_user()s + get_user()s */ - if (count < size_sg_header) + if (count < SZ_SG_HEADER) return -EIO; - __copy_from_user(&old_hdr, buf, size_sg_header); + __copy_from_user(&old_hdr, buf, SZ_SG_HEADER); blocking = !(filp->f_flags & O_NONBLOCK); if (old_hdr.reply_len < 0) return sg_new_write(sfp, buf, count, blocking, 0, NULL); - if (count < (size_sg_header + 6)) + if (count < (SZ_SG_HEADER + 6)) return -EIO; /* The minimum scsi command length is 6 bytes. */ if (! (srp = sg_add_request(sfp))) { SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n")); return -EDOM; } - buf += size_sg_header; + buf += SZ_SG_HEADER; __get_user(opcode, buf); if (sfp->next_cmd_len > 0) { if (sfp->next_cmd_len > MAX_COMMAND_SIZE) { @@ -539,8 +556,8 @@ input_size = count - cmd_size; mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len; - mxsize -= size_sg_header; - input_size -= size_sg_header; + mxsize -= SZ_SG_HEADER; + input_size -= SZ_SG_HEADER; if (input_size < 0) { sg_remove_request(sfp, srp); return -EIO; /* User did not pass enough bytes for this command. */ @@ -550,16 +567,12 @@ hp->cmd_len = (unsigned char)cmd_size; hp->iovec_count = 0; hp->mx_sb_len = 0; -#if 0 - hp->dxfer_direction = SG_DXFER_UNKNOWN; -#else if (input_size > 0) - hp->dxfer_direction = ((old_hdr.reply_len - size_sg_header) > 0) ? + hp->dxfer_direction = ((old_hdr.reply_len - SZ_SG_HEADER) > 0) ? SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV; else hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE; -#endif hp->dxfer_len = mxsize; hp->dxferp = (unsigned char *)buf + cmd_size; hp->sbp = NULL; @@ -581,7 +594,7 @@ unsigned char cmnd[sizeof(dummy_cmdp->sr_cmnd)]; int timeout; - if (count < size_sg_io_hdr) + if (count < SZ_SG_IO_HDR) return -EINVAL; if ((k = verify_area(VERIFY_READ, buf, count))) return k; /* protects following copy_from_user()s + get_user()s */ @@ -592,7 +605,7 @@ return -EDOM; } hp = &srp->header; - __copy_from_user(hp, buf, size_sg_io_hdr); + __copy_from_user(hp, buf, SZ_SG_IO_HDR); if (hp->interface_id != 'S') { sg_remove_request(sfp, srp); return -ENOSYS; @@ -648,7 +661,10 @@ sg_finish_rem_req(srp); return k; } -/* SCSI_LOG_TIMEOUT(7, printk("sg_write: allocating device\n")); */ + if (sdp->detached) { + sg_finish_rem_req(srp); + return -ENODEV; + } SRpnt = scsi_allocate_request(sdp->device); if(SRpnt == NULL) { SCSI_LOG_TIMEOUT(1, printk("sg_write: no mem\n")); @@ -656,17 +672,15 @@ return -ENOMEM; } -/* SCSI_LOG_TIMEOUT(7, printk("sg_write: device allocated\n")); */ srp->my_cmdp = SRpnt; SRpnt->sr_request.rq_dev = sdp->i_rdev; SRpnt->sr_request.rq_status = RQ_ACTIVE; SRpnt->sr_sense_buffer[0] = 0; SRpnt->sr_cmd_len = hp->cmd_len; -/* Set the LUN field in the command structure, overriding user input */ - if (! (hp->flags & SG_FLAG_LUN_INHIBIT)) - cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5); - -/* SCSI_LOG_TIMEOUT(7, printk("sg_write: do cmd\n")); */ + if (! (hp->flags & SG_FLAG_LUN_INHIBIT)) { + if (sdp->device->scsi_level <= SCSI_2) + cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5); + } SRpnt->sr_use_sg = srp->data.k_use_sg; SRpnt->sr_sglist_len = srp->data.sglist_len; SRpnt->sr_bufflen = srp->data.bufflen; @@ -719,30 +733,31 @@ { int blocking = 1; /* ignore O_NONBLOCK flag */ + if (sdp->detached) + return -ENODEV; if(! scsi_block_when_processing_errors(sdp->device) ) return -ENXIO; - result = verify_area(VERIFY_WRITE, (void *)arg, size_sg_io_hdr); + result = verify_area(VERIFY_WRITE, (void *)arg, SZ_SG_IO_HDR); if (result) return result; - result = sg_new_write(sfp, (const char *)arg, size_sg_io_hdr, + result = sg_new_write(sfp, (const char *)arg, SZ_SG_IO_HDR, blocking, read_only, &srp); if (result < 0) return result; srp->sg_io_owned = 1; while (1) { - int dio = sg_dio_in_use(sfp); result = 0; /* following macro to beat race condition */ __wait_event_interruptible(sfp->read_wait, - (sfp->closed || srp->done), result); + (sdp->detached || sfp->closed || srp->done), result); + if (sdp->detached) + return -ENODEV; if (sfp->closed) return 0; /* request packet dropped already */ if (0 == result) break; - else if (! dio) { /* only let signal out if no dio */ - srp->orphan = 1; - return result; /* -ERESTARTSYS because signal hit process */ - } + srp->orphan = 1; + return result; /* -ERESTARTSYS because signal hit process */ } srp->done = 2; - result = sg_new_read(sfp, (char *)arg, size_sg_io_hdr, srp); + result = sg_new_read(sfp, (char *)arg, SZ_SG_IO_HDR, srp); return (result < 0) ? result : 0; } case SG_SET_TIMEOUT: @@ -765,8 +780,11 @@ sg_build_reserve(sfp, val); } } - else + else { + if (sdp->detached) + return -ENODEV; sfp->low_dma = sdp->device->host->unchecked_isa_dma; + } return 0; case SG_GET_LOW_DMA: return put_user((int)sfp->low_dma, (int *)arg); @@ -775,6 +793,9 @@ if (result) return result; else { sg_scsi_id_t * sg_idp = (sg_scsi_id_t *)arg; + + if (sdp->detached) + return -ENODEV; __put_user((int)sdp->device->host->host_no, &sg_idp->host_no); __put_user((int)sdp->device->channel, &sg_idp->channel); __put_user((int)sdp->device->id, &sg_idp->scsi_id); @@ -853,7 +874,7 @@ return put_user(sg_version_num, (int *)arg); case SG_GET_REQUEST_TABLE: result = verify_area(VERIFY_WRITE, (void *) arg, - size_sg_req_info * SG_MAX_QUEUE); + SZ_SG_REQ_INFO * SG_MAX_QUEUE); if (result) return result; else { sg_req_info_t rinfo[SG_MAX_QUEUE]; @@ -861,7 +882,7 @@ read_lock_irqsave(&sfp->rq_list_lock, iflags); for (srp = sfp->headrp, val = 0; val < SG_MAX_QUEUE; ++val, srp = srp ? srp->nextrp : srp) { - memset(&rinfo[val], 0, size_sg_req_info); + memset(&rinfo[val], 0, SZ_SG_REQ_INFO); if (srp) { rinfo[val].req_state = srp->done + 1; rinfo[val].problem = srp->header.masked_status & @@ -876,12 +897,16 @@ } } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - __copy_to_user((void *)arg, rinfo, size_sg_req_info * SG_MAX_QUEUE); + __copy_to_user((void *)arg, rinfo, SZ_SG_REQ_INFO * SG_MAX_QUEUE); return 0; } case SG_EMULATED_HOST: + if (sdp->detached) + return -ENODEV; return put_user(sdp->device->host->hostt->emulated, (int *)arg); case SG_SCSI_RESET: + if (sdp->detached) + return -ENODEV; if (filp->f_flags & O_NONBLOCK) { if (sdp->device->host->in_recovery) return -EBUSY; @@ -907,13 +932,16 @@ default: return -EINVAL; } - if(! capable(CAP_SYS_ADMIN)) return -EACCES; + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; return (scsi_reset_provider(sdp->device, val) == SUCCESS) ? 0 : -EIO; #else SCSI_LOG_TIMEOUT(1, printk("sg_ioctl: SG_RESET_SCSI not supported\n")); result = -EINVAL; #endif case SCSI_IOCTL_SEND_COMMAND: + if (sdp->detached) + return -ENODEV; if (read_only) { unsigned char opcode = WRITE_6; Scsi_Ioctl_Command * siocp = (void *)arg; @@ -932,6 +960,8 @@ case SCSI_IOCTL_GET_BUS_NUMBER: case SCSI_IOCTL_PROBE_HOST: case SG_GET_TRANSFORM: + if (sdp->detached) + return -ENODEV; return scsi_ioctl(sdp->device, cmd_in, (void *)arg); default: if (read_only) @@ -949,7 +979,8 @@ int count = 0; unsigned long iflags; - if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp))) + if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)) + || sfp->closed) return POLLERR; poll_wait(filp, &sfp->read_wait, wait); read_lock_irqsave(&sfp->rq_list_lock, iflags); @@ -960,7 +991,10 @@ ++count; } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - if (! sfp->cmd_q) { + + if (sdp->detached) + res |= POLLHUP; + else if (! sfp->cmd_q) { if (0 == count) res |= POLLOUT | POLLWRNORM; } @@ -1001,9 +1035,9 @@ if (dev < sg_template.dev_max) sdp = sg_dev_arr[dev]; } - if (NULL == sdp) { + if ((NULL == sdp) || sdp->detached) { read_unlock(&sg_dev_arr_lock); - SCSI_LOG_TIMEOUT(1, printk("sg...bh: bad args dev=%d\n", dev)); + SCSI_LOG_TIMEOUT(1, printk("sg...bh: dev=%d gone\n", dev)); scsi_release_request(SRpnt); SRpnt = NULL; return; @@ -1020,8 +1054,8 @@ break; sfp = sfp->nextfp; } - read_unlock(&sg_dev_arr_lock); if (! srp) { + read_unlock(&sg_dev_arr_lock); SCSI_LOG_TIMEOUT(1, printk("sg...bh: req missing, dev=%d\n", dev)); scsi_release_request(SRpnt); SRpnt = NULL; @@ -1035,6 +1069,7 @@ sg_clr_srpnt(SRpnt); srp->my_cmdp = NULL; srp->done = 1; + read_unlock(&sg_dev_arr_lock); SCSI_LOG_TIMEOUT(4, printk("sg...bh: dev=%d, pack_id=%d, res=0x%x\n", dev, srp->header.pack_id, (int)SRpnt->sr_result)); @@ -1071,7 +1106,6 @@ if (sfp->closed) { /* whoops this fd already released, cleanup */ SCSI_LOG_TIMEOUT(1, printk("sg...bh: already closed, freeing ...\n")); - /* should check if module is unloaded <<<<<<< */ sg_finish_rem_req(srp); srp = NULL; if (NULL == sfp->headrp) { @@ -1080,6 +1114,9 @@ sg_remove_sfp(sdp, sfp); sfp = NULL; } + __MOD_DEC_USE_COUNT(sg_template.module); + if (sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); } else if (srp && srp->orphan) { if (sfp->keep_orphan) @@ -1110,19 +1147,6 @@ static int sg_detect(Scsi_Device * scsidp) { - switch (scsidp->type) { - case TYPE_DISK: - case TYPE_MOD: - case TYPE_ROM: - case TYPE_WORM: - case TYPE_TAPE: break; - default: - printk("Detected scsi generic sg%d at scsi%d," - " channel %d, id %d, lun %d, type %d\n", - sg_template.dev_noticed, - scsidp->host->host_no, scsidp->channel, - scsidp->id, scsidp->lun, scsidp->type); - } sg_template.dev_noticed++; return 1; } @@ -1241,51 +1265,72 @@ sg_template.nr_dev++; sg_dev_arr[k] = sdp; write_unlock_irqrestore(&sg_dev_arr_lock, iflags); + switch (scsidp->type) { + case TYPE_DISK: + case TYPE_MOD: + case TYPE_ROM: + case TYPE_WORM: + case TYPE_TAPE: break; + default: + printk("Attached scsi generic sg%d at scsi%d, channel %d, id %d," + " lun %d, type %d\n", k, scsidp->host->host_no, + scsidp->channel, scsidp->id, scsidp->lun, scsidp->type); + } return 0; } /* Called at 'finish' of init process, after all attaches */ static void sg_finish(void) -{ - SCSI_LOG_TIMEOUT(3, printk("sg_finish: dma_free_sectors=%u\n", - scsi_dma_free_sectors)); -} +{ } static void sg_detach(Scsi_Device * scsidp) { Sg_device * sdp; unsigned long iflags; Sg_fd * sfp; + Sg_fd * tsfp; Sg_request * srp; - int k; + Sg_request * tsrp; + int k, delay; if (NULL == sg_dev_arr) return; + delay = 0; write_lock_irqsave(&sg_dev_arr_lock, iflags); for (k = 0; k < sg_template.dev_max; k++) { sdp = sg_dev_arr[k]; if ((NULL == sdp) || (sdp->device != scsidp)) continue; /* dirty but lowers nesting */ if (sdp->headfp) { - for (sfp = sdp->headfp; sfp; sfp = sfp->nextfp) { - /* no lock on request list here */ - for (srp = sfp->headrp; srp; srp = srp->nextrp) { - if (! srp->done) { - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - sg_shorten_timeout(srp->my_cmdp); - write_lock_irqsave(&sg_dev_arr_lock, iflags); - } - } + sdp->detached = 1; + for (sfp = sdp->headfp; sfp; sfp = tsfp) { + tsfp = sfp->nextfp; + for (srp = sfp->headrp; srp; srp = tsrp) { + tsrp = srp->nextrp; + if (sfp->closed || (0 == srp->done)) + sg_finish_rem_req(srp); + } + if (sfp->closed) { + __MOD_DEC_USE_COUNT(sg_template.module); + if (sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); + __sg_remove_sfp(sdp, sfp); + } + else { + delay = 1; + wake_up_interruptible(&sfp->read_wait); + kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP); + } } - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - 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 */ + SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d, dirty\n", k)); devfs_unregister (sdp->de); sdp->de = NULL; - sdp->detached = 1; - write_lock_irqsave(&sg_dev_arr_lock, iflags); + if (NULL == sdp->headfp) { + kfree((char *)sdp); + sg_dev_arr[k] = NULL; + } } - else { + else { /* nothing active, simple case */ SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k)); devfs_unregister (sdp->de); kfree((char *)sdp); @@ -1293,13 +1338,12 @@ } scsidp->attached--; sg_template.nr_dev--; -/* avoid associated device /dev/sg? being incremented - * each time module is inserted/removed , */ - sg_template.dev_noticed--; + sg_template.dev_noticed--; /* from */ break; } write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - return; + if (delay) + scsi_sleep(2); /* dirty detach so delay device destruction */ } MODULE_AUTHOR("Douglas Gilbert"); @@ -1322,8 +1366,6 @@ scsi_unregister_module(MODULE_SCSI_DEV, &sg_template); devfs_unregister_chrdev(SCSI_GENERIC_MAJOR, "sg"); if(sg_dev_arr != NULL) { -/* Really worrying situation of writes still pending and get here */ -/* Strategy: shorten timeout on release + wait on detach ... */ kfree((char *)sg_dev_arr); sg_dev_arr = NULL; } @@ -1331,29 +1373,6 @@ } -#if 0 -extern void scsi_times_out (Scsi_Cmnd * SCpnt); -extern void scsi_old_times_out (Scsi_Cmnd * SCpnt); -#endif - -/* Can't see clean way to abort a command so shorten timeout to 1 jiffy */ -static void sg_shorten_timeout(Scsi_Request * srpnt) -{ -#if 0 /* scsi_syms.c is very miserly about exported functions */ - scsi_delete_timer(scpnt); - if (! scpnt) - return; - scpnt->timeout_per_command = 1; /* try 1 jiffy (perhaps 0 jiffies) */ - if (scpnt->host->hostt->use_new_eh_code) - scsi_add_timer(scpnt, scpnt->timeout_per_command, scsi_times_out); - else - scsi_add_timer(scpnt, scpnt->timeout_per_command, - scsi_old_times_out); -#else - scsi_sleep(HZ); /* just sleep 1 second and hope ... */ -#endif -} - static int sg_start_req(Sg_request * srp) { int res; @@ -1367,9 +1386,8 @@ SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len)); if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) return 0; - if ((hp->flags & SG_FLAG_DIRECT_IO) && - (dxfer_dir != SG_DXFER_UNKNOWN) && - (0 == hp->iovec_count) && + if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && + (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) && (! sfp->parentdp->device->host->unchecked_isa_dma)) { res = sg_build_dir(srp, sfp, dxfer_len); if (res <= 0) /* -ve -> error, 0 -> done, 1 -> try indirect */ @@ -1427,7 +1445,7 @@ static void sg_unmap_and(Sg_scatter_hold * schp, int free_also) { -#ifdef SG_ALLOW_DIO +#ifdef SG_ALLOW_DIO_CODE if (schp && schp->kiobp) { if (schp->mapped) { unmap_kiobuf(schp->kiobp); @@ -1443,7 +1461,7 @@ static int sg_build_dir(Sg_request * srp, Sg_fd * sfp, int dxfer_len) { -#ifdef SG_ALLOW_DIO +#ifdef SG_ALLOW_DIO_CODE int res, k, split, offset, num, mx_sc_elems, rem_sz; struct kiobuf * kp; char * mem_src_arr; @@ -1519,7 +1537,7 @@ return 0; #else return 1; -#endif /* SG_ALLOW_DIO */ +#endif /* SG_ALLOW_DIO_CODE */ } static int sg_build_indi(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) @@ -1629,7 +1647,7 @@ if (iovec_count) { onum = iovec_count; if ((k = verify_area(VERIFY_READ, hp->dxferp, - size_sg_iovec * onum))) + SZ_SG_IOVEC * onum))) return k; } else @@ -1707,8 +1725,8 @@ } else { __copy_from_user(&u_iovec, - (unsigned char *)hp->dxferp + (ind * size_sg_iovec), - size_sg_iovec); + (unsigned char *)hp->dxferp + (ind * SZ_SG_IOVEC), + SZ_SG_IOVEC); p = (unsigned char *)u_iovec.iov_base; count = (int)u_iovec.iov_len; } @@ -1778,7 +1796,7 @@ if (iovec_count) { onum = iovec_count; if ((k = verify_area(VERIFY_READ, hp->dxferp, - size_sg_iovec * onum))) + SZ_SG_IOVEC * onum))) return k; } else @@ -2124,6 +2142,34 @@ return sfp; } +static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp) +{ + Sg_fd * fp; + Sg_fd * prev_fp; + + prev_fp = sdp->headfp; + if (sfp == prev_fp) + sdp->headfp = prev_fp->nextfp; + else { + while ((fp = prev_fp->nextfp)) { + if (sfp == fp) { + prev_fp->nextfp = fp->nextfp; + break; + } + prev_fp = fp; + } + } + if (sfp->reserve.bufflen > 0) { + SCSI_LOG_TIMEOUT(6, printk("__sg_remove_sfp: bufflen=%d, k_use_sg=%d\n", + (int)sfp->reserve.bufflen, (int)sfp->reserve.k_use_sg)); + sg_remove_scat(&sfp->reserve); + } + sfp->parentdp = NULL; + SCSI_LOG_TIMEOUT(6, printk("__sg_remove_sfp: sfp=0x%p\n", sfp)); + sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->fd_mem_src); +} + +/* Returns 0 in normal case, 1 when detached and sdp object removed */ static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp) { Sg_request * srp; @@ -2131,44 +2177,18 @@ int dirty = 0; int res = 0; - srp = sfp->headrp; - if (srp) { - while (srp) { - tsrp = srp->nextrp; - if (srp->done) - sg_finish_rem_req(srp); - else - ++dirty; - srp = tsrp; - } + for (srp = sfp->headrp; srp; srp = tsrp) { + tsrp = srp->nextrp; + if (srp->done) + sg_finish_rem_req(srp); + else + ++dirty; } if (0 == dirty) { - Sg_fd * fp; - Sg_fd * prev_fp; unsigned long iflags; write_lock_irqsave(&sg_dev_arr_lock, iflags); - prev_fp = sdp->headfp; - if (sfp == prev_fp) - sdp->headfp = prev_fp->nextfp; - else { - while ((fp = prev_fp->nextfp)) { - if (sfp == fp) { - prev_fp->nextfp = fp->nextfp; - break; - } - prev_fp = fp; - } - } - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - if (sfp->reserve.bufflen > 0) { -SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp: bufflen=%d, k_use_sg=%d\n", - (int)sfp->reserve.bufflen, (int)sfp->reserve.k_use_sg)); - sg_remove_scat(&sfp->reserve); - } - sfp->parentdp = NULL; - SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp: sfp=0x%p\n", sfp)); - sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->fd_mem_src); + __sg_remove_sfp(sdp, sfp); if (sdp->detached && (NULL == sdp->headfp)) { int k, maxd; @@ -2180,11 +2200,16 @@ if (k < maxd) sg_dev_arr[k] = NULL; kfree((char *)sdp); + res = 1; } - res = 1; + write_unlock_irqrestore(&sg_dev_arr_lock, iflags); } else { sfp->closed = 1; /* flag dirty state on this fd */ + /* MOD_INC's to inhibit unloading sg and associated adapter driver */ + __MOD_INC_USE_COUNT(sg_template.module); + if (sdp->device->host->hostt->module) + __MOD_INC_USE_COUNT(sdp->device->host->hostt->module); SCSI_LOG_TIMEOUT(1, printk( "sg_remove_sfp: worrisome, %d writes pending\n", dirty)); } @@ -2203,18 +2228,6 @@ return srp ? 1 : 0; } -static int sg_dio_in_use(Sg_fd * sfp) -{ - const Sg_request * srp; - unsigned long iflags; - - read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (srp = sfp->headrp; srp; srp = srp->nextrp) - if ((! srp->done) && srp->data.kiobp) break; - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return srp ? 1 : 0; -} - /* If retSzp==NULL want exact size or fail */ /* sg_low_malloc() should always be called from a process context allowing GFP_KERNEL to be used instead of GFP_ATOMIC */ @@ -2355,7 +2368,7 @@ case SG_USER_MEM: break; /* nothing to do */ default: - printk("sg_low_free: bad mem_src=%d, buff=0x%p, rqSz=%df\n", + printk("sg_low_free: bad mem_src=%d, buff=0x%p, rqSz=%d\n", mem_src, buff, size); break; } @@ -2451,11 +2464,17 @@ static struct proc_dir_entry * sg_proc_sgp = NULL; -static const char * sg_proc_sg_dirname = "sg"; -static const char * sg_proc_leaf_names[] = {"def_reserved_size", "debug", - "devices", "device_hdr", "device_strs", - "hosts", "host_hdr", "host_strs", "version"}; +static char sg_proc_sg_dirname[] = "sg"; +static const char * sg_proc_leaf_names[] = {"allow_dio", "def_reserved_size", + "debug", "devices", "device_hdr", "device_strs", + "hosts", "host_hdr", "host_strs", "version"}; +static int sg_proc_adio_read(char * buffer, char ** start, off_t offset, + int size, int * eof, void * data); +static int sg_proc_adio_info(char * buffer, int * len, off_t * begin, + off_t offset, int size); +static int sg_proc_adio_write(struct file * filp, const char * buffer, + unsigned long count, void * data); static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset, int size, int * eof, void * data); static int sg_proc_dressz_info(char * buffer, int * len, off_t * begin, @@ -2495,12 +2514,12 @@ static int sg_proc_version_info(char * buffer, int * len, off_t * begin, off_t offset, int size); static read_proc_t * sg_proc_leaf_reads[] = { - sg_proc_dressz_read, sg_proc_debug_read, + sg_proc_adio_read, sg_proc_dressz_read, sg_proc_debug_read, sg_proc_dev_read, sg_proc_devhdr_read, sg_proc_devstrs_read, sg_proc_host_read, sg_proc_hosthdr_read, sg_proc_hoststrs_read, sg_proc_version_read}; static write_proc_t * sg_proc_leaf_writes[] = { - sg_proc_dressz_write, 0, 0, 0, 0, 0, 0, 0, 0}; + sg_proc_adio_write, sg_proc_dressz_write, 0, 0, 0, 0, 0, 0, 0, 0}; #define PRINT_PROC(fmt,args...) \ do { \ @@ -2562,6 +2581,32 @@ remove_proc_entry(sg_proc_sg_dirname, proc_scsi); } +static int sg_proc_adio_read(char * buffer, char ** start, off_t offset, + int size, int * eof, void * data) +{ SG_PROC_READ_FN(sg_proc_adio_info); } + +static int sg_proc_adio_info(char * buffer, int * len, off_t * begin, + off_t offset, int size) +{ + PRINT_PROC("%d\n", sg_allow_dio); + return 1; +} + +static int sg_proc_adio_write(struct file * filp, const char * buffer, + unsigned long count, void * data) +{ + int num; + char buff[11]; + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + num = (count < 10) ? count : 10; + copy_from_user(buff, buffer, num); + buff[num] = '\0'; + sg_allow_dio = simple_strtol(buff, 0, 10) ? 1 : 0; + return count; +} + static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset, int size, int * eof, void * data) { SG_PROC_READ_FN(sg_proc_dressz_info); } @@ -2580,7 +2625,7 @@ unsigned long k = ULONG_MAX; char buff[11]; - if (! capable(CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) return -EACCES; num = (count < 10) ? count : 10; copy_from_user(buff, buffer, num); @@ -2620,8 +2665,9 @@ Sg_request * srp; struct scsi_device * scsidp; int dev, k, m, blen, usg; - - if (! (scsidp = sdp->device)) { + + scsidp = sdp->device; + if (NULL == scsidp) { PRINT_PROC("device %d detached ??\n", j); continue; } @@ -2629,10 +2675,14 @@ if (sg_get_nth_sfp(sdp, 0)) { PRINT_PROC(" >>> device=sg%d ", dev); - PRINT_PROC("scsi%d chan=%d id=%d lun=%d em=%d sg_tablesize=%d" - " excl=%d\n", scsidp->host->host_no, scsidp->channel, - scsidp->id, scsidp->lun, scsidp->host->hostt->emulated, - sdp->sg_tablesize, sdp->exclude); + if (sdp->detached) + PRINT_PROC("detached pending close "); + else + PRINT_PROC("scsi%d chan=%d id=%d lun=%d em=%d", + scsidp->host->host_no, scsidp->channel, + scsidp->id, scsidp->lun, scsidp->host->hostt->emulated); + PRINT_PROC(" sg_tablesize=%d excl=%d\n", sdp->sg_tablesize, + sdp->exclude); } for (k = 0; (fp = sg_get_nth_sfp(sdp, k)); ++k) { PRINT_PROC(" FD(%d): timeout=%dms bufflen=%d " @@ -2683,7 +2733,7 @@ max_dev = sg_last_dev(); for (j = 0; j < max_dev; ++j) { sdp = sg_get_dev(j); - if (sdp && (scsidp = sdp->device)) + if (sdp && (scsidp = sdp->device) && (! sdp->detached)) PRINT_PROC("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", scsidp->host->host_no, scsidp->channel, scsidp->id, scsidp->lun, (int)scsidp->type, (int)scsidp->access_count, @@ -2719,7 +2769,7 @@ max_dev = sg_last_dev(); for (j = 0; j < max_dev; ++j) { sdp = sg_get_dev(j); - if (sdp && (scsidp = sdp->device)) + if (sdp && (scsidp = sdp->device) && (! sdp->detached)) PRINT_PROC("%8.8s\t%16.16s\t%4.4s\n", scsidp->vendor, scsidp->model, scsidp->rev); else diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sr.c linux.ac/drivers/scsi/sr.c --- linux.vanilla/drivers/scsi/sr.c Sat May 26 16:53:13 2001 +++ linux.ac/drivers/scsi/sr.c Wed May 2 14:16:25 2001 @@ -88,6 +88,7 @@ static int *sr_sizes; static int *sr_blocksizes; +static int *sr_hardsizes; static int sr_open(struct cdrom_device_info *, int); void get_sectorsize(int); @@ -262,7 +263,7 @@ static int sr_scatter_pad(Scsi_Cmnd *SCpnt, int s_size) { struct scatterlist *sg, *old_sg = NULL; - int i, fsize, bsize, sg_ent; + int i, fsize, bsize, sg_ent, sg_count; char *front, *back; back = front = NULL; @@ -290,17 +291,24 @@ /* * extend or allocate new scatter-gather table */ - if (SCpnt->use_sg) + sg_count = SCpnt->use_sg; + if (sg_count) old_sg = (struct scatterlist *) SCpnt->request_buffer; else { - SCpnt->use_sg = 1; + sg_count = 1; sg_ent++; } - SCpnt->sglist_len = ((sg_ent * sizeof(struct scatterlist)) + 511) & ~511; - if ((sg = scsi_malloc(SCpnt->sglist_len)) == NULL) + i = ((sg_ent * sizeof(struct scatterlist)) + 511) & ~511; + if ((sg = scsi_malloc(i)) == NULL) goto no_mem; + /* + * no more failing memory allocs possible, we can safely assign + * SCpnt values now + */ + SCpnt->sglist_len = i; + SCpnt->use_sg = sg_count; memset(sg, 0, SCpnt->sglist_len); i = 0; @@ -343,12 +351,12 @@ static int sr_init_command(Scsi_Cmnd * SCpnt) { - int dev, devm, block, this_count, s_size; + int dev, devm, block=0, this_count, s_size; devm = MINOR(SCpnt->request.rq_dev); dev = DEVICE_NR(SCpnt->request.rq_dev); - SCSI_LOG_HLQUEUE(1, printk("Doing sr request, dev = %d\n", devm)); + SCSI_LOG_HLQUEUE(1, printk("Doing sr request, dev = %d, block = %d\n", devm, block)); if (dev >= sr_template.nr_dev || !scsi_CDs[dev].device || @@ -415,7 +423,8 @@ (SCpnt->request.cmd == WRITE) ? "writing" : "reading", this_count, SCpnt->request.nr_sectors)); - SCpnt->cmnd[1] = (SCpnt->lun << 5) & 0xe0; + SCpnt->cmnd[1] = (SCpnt->device->scsi_level <= SCSI_2) ? + ((SCpnt->lun << 5) & 0xe0) : 0; if (this_count > 0xffff) this_count = 0xffff; @@ -501,11 +510,7 @@ if (SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 0; - - printk("Detected scsi CD-ROM sr%d at scsi%d, channel %d, id %d, lun %d\n", - sr_template.dev_noticed++, - SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); - + sr_template.dev_noticed++; return 1; } @@ -534,6 +539,9 @@ sr_template.nr_dev++; if (sr_template.nr_dev > sr_template.dev_max) panic("scsi_devices corrupt (sr)"); + + printk("Attached scsi CD-ROM sr%d at scsi%d, channel %d, id %d, lun %d\n", + i, SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); return 0; } @@ -564,7 +572,8 @@ retries = 3; do { cmd[0] = READ_CAPACITY; - cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0; + cmd[1] = (scsi_CDs[i].device->scsi_level <= SCSI_2) ? + ((scsi_CDs[i].device->lun << 5) & 0xe0) : 0; memset((void *) &cmd[2], 0, 8); SRpnt->sr_request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy */ SRpnt->sr_cmd_len = 0; @@ -593,7 +602,7 @@ } else { #if 0 if (cdrom_get_last_written(MKDEV(MAJOR_NR, i), - (long *) &scsi_CDs[i].capacity)) + &scsi_CDs[i].capacity)) #endif scsi_CDs[i].capacity = 1 + ((buffer[0] << 24) | (buffer[1] << 16) | @@ -657,7 +666,8 @@ buffer = (unsigned char *) scsi_malloc(512); cmd[0] = MODE_SENSE; - cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0; + cmd[1] = (scsi_CDs[i].device->scsi_level <= SCSI_2) ? + ((scsi_CDs[i].device->lun << 5) & 0xe0) : 0; cmd[2] = 0x2a; cmd[4] = 128; cmd[3] = cmd[5] = 0; @@ -734,7 +744,8 @@ Scsi_Device *device = scsi_CDs[MINOR(cdi->dev)].device; /* set the LUN */ - cgc->cmd[1] |= device->lun << 5; + if (device->scsi_level <= SCSI_2) + cgc->cmd[1] |= device->lun << 5; cgc->stat = sr_do_ioctl(MINOR(cdi->dev), cgc->cmd, cgc->buffer, cgc->buflen, cgc->quiet, cgc->data_direction, cgc->sense); @@ -775,16 +786,21 @@ if (!sr_blocksizes) goto cleanup_sizes; + sr_hardsizes = kmalloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC); + if (!sr_hardsizes) + goto cleanup_blocksizes; /* * These are good guesses for the time being. - * Don't set sr_hardsizes here! That will prevent reading anything smaller. */ for (i = 0; i < sr_template.dev_max; i++) { sr_blocksizes[i] = 2048; + sr_hardsizes[i] = 2048; } blksize_size[MAJOR_NR] = sr_blocksizes; + hardsect_size[MAJOR_NR] = sr_hardsizes; return 0; - +cleanup_blocksizes: + kfree(sr_blocksizes); cleanup_sizes: kfree(sr_sizes); cleanup_cds: @@ -903,8 +919,11 @@ kfree(sr_blocksizes); sr_blocksizes = NULL; + kfree(sr_hardsizes); + sr_hardsizes = NULL; } blksize_size[MAJOR_NR] = NULL; + hardsect_size[MAJOR_NR] = NULL; blk_size[MAJOR_NR] = NULL; read_ahead[MAJOR_NR] = 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sr_ioctl.c linux.ac/drivers/scsi/sr_ioctl.c --- linux.vanilla/drivers/scsi/sr_ioctl.c Fri Dec 29 22:07:22 2000 +++ linux.ac/drivers/scsi/sr_ioctl.c Sun Apr 22 01:47:30 2001 @@ -191,7 +191,8 @@ u_char sr_cmd[10]; sr_cmd[0] = GPCMD_TEST_UNIT_READY; - sr_cmd[1] = ((scsi_CDs[minor].device->lun) << 5); + sr_cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? + ((scsi_CDs[minor].device->lun) << 5) : 0; sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0; return sr_do_ioctl(minor, sr_cmd, NULL, 0, 1, SCSI_DATA_NONE, NULL); } @@ -201,7 +202,8 @@ u_char sr_cmd[10]; sr_cmd[0] = GPCMD_START_STOP_UNIT; - sr_cmd[1] = ((scsi_CDs[MINOR(cdi->dev)].device->lun) << 5); + sr_cmd[1] = (scsi_CDs[MINOR(cdi->dev)].device->scsi_level <= SCSI_2) ? + ((scsi_CDs[MINOR(cdi->dev)].device->lun) << 5) : 0; sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0; sr_cmd[4] = (pos == 0) ? 0x03 /* close */ : 0x02 /* eject */ ; @@ -273,7 +275,8 @@ int result; sr_cmd[0] = GPCMD_READ_SUBCHANNEL; - sr_cmd[1] = ((scsi_CDs[MINOR(cdi->dev)].device->lun) << 5); + sr_cmd[1] = (scsi_CDs[MINOR(cdi->dev)].device->scsi_level <= SCSI_2) ? + ((scsi_CDs[MINOR(cdi->dev)].device->lun) << 5) : 0; sr_cmd[2] = 0x40; /* I do want the subchannel info */ sr_cmd[3] = 0x02; /* Give me medium catalog number info */ sr_cmd[4] = sr_cmd[5] = 0; @@ -307,7 +310,8 @@ memset(sr_cmd, 0, MAX_COMMAND_SIZE); sr_cmd[0] = GPCMD_SET_SPEED; /* SET CD SPEED */ - sr_cmd[1] = (scsi_CDs[MINOR(cdi->dev)].device->lun) << 5; + sr_cmd[1] = (scsi_CDs[MINOR(cdi->dev)].device->scsi_level <= SCSI_2) ? + ((scsi_CDs[MINOR(cdi->dev)].device->lun) << 5) : 0; sr_cmd[2] = (speed >> 8) & 0xff; /* MSB for speed (in kbytes/sec) */ sr_cmd[3] = speed & 0xff; /* LSB */ @@ -336,7 +340,8 @@ struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg; sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP; - sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5); + sr_cmd[1] = (scsi_CDs[target].device->scsi_level <= SCSI_2) ? + ((scsi_CDs[target].device->lun) << 5) : 0; sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0; sr_cmd[8] = 12; /* LSB of length */ @@ -353,8 +358,9 @@ struct cdrom_tocentry *tocentry = (struct cdrom_tocentry *) arg; sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP; - sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | - (tocentry->cdte_format == CDROM_MSF ? 0x02 : 0); + sr_cmd[1] = (scsi_CDs[target].device->scsi_level <= SCSI_2) ? + ((scsi_CDs[target].device->lun) << 5) : 0; + sr_cmd[1] |= (tocentry->cdte_format == CDROM_MSF) ? 0x02 : 0; sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0; sr_cmd[6] = tocentry->cdte_track; sr_cmd[8] = 12; /* LSB of length */ @@ -379,7 +385,8 @@ struct cdrom_ti* ti = (struct cdrom_ti*)arg; sr_cmd[0] = GPCMD_PLAYAUDIO_TI; - sr_cmd[1] = scsi_CDs[target].device->lun << 5; + sr_cmd[1] = (scsi_CDs[target].device->scsi_level <= SCSI_2) ? + (scsi_CDs[target].device->lun << 5) : 0; sr_cmd[4] = ti->cdti_trk0; sr_cmd[5] = ti->cdti_ind0; sr_cmd[7] = ti->cdti_trk1; @@ -429,7 +436,9 @@ memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = GPCMD_READ_CD; /* READ_CD */ - cmd[1] = (scsi_CDs[minor].device->lun << 5) | ((format & 7) << 2); + cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? + (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] |= ((format & 7) << 2); cmd[2] = (unsigned char) (lba >> 24) & 0xff; cmd[3] = (unsigned char) (lba >> 16) & 0xff; cmd[4] = (unsigned char) (lba >> 8) & 0xff; @@ -481,7 +490,8 @@ memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = GPCMD_READ_10; - cmd[1] = (scsi_CDs[minor].device->lun << 5); + cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? + (scsi_CDs[minor].device->lun << 5) : 0; cmd[2] = (unsigned char) (lba >> 24) & 0xff; cmd[3] = (unsigned char) (lba >> 16) & 0xff; cmd[4] = (unsigned char) (lba >> 8) & 0xff; @@ -530,6 +540,8 @@ target = MINOR(cdi->dev); switch (cmd) { + case BLKGETSIZE: + return put_user(scsi_CDs[target].capacity >> 1, (long *) arg); case BLKROSET: case BLKROGET: case BLKRASET: diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sr_vendor.c linux.ac/drivers/scsi/sr_vendor.c --- linux.vanilla/drivers/scsi/sr_vendor.c Fri Dec 29 22:07:22 2000 +++ linux.ac/drivers/scsi/sr_vendor.c Tue Apr 3 17:55:04 2001 @@ -124,7 +124,9 @@ #endif memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = MODE_SELECT; - cmd[1] = (scsi_CDs[minor].device->lun << 5) | (1 << 4); + cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? + (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] |= (1 << 4); cmd[4] = 12; modesel = (struct ccs_modesel_head *) buffer; memset(modesel, 0, sizeof(*modesel)); @@ -173,7 +175,8 @@ case VENDOR_SCSI3: memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = READ_TOC; - cmd[1] = (scsi_CDs[minor].device->lun << 5); + cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? + (scsi_CDs[minor].device->lun << 5) : 0; cmd[8] = 12; cmd[9] = 0x40; rc = sr_do_ioctl(minor, cmd, buffer, 12, 1, SCSI_DATA_READ, NULL); @@ -198,7 +201,9 @@ unsigned long min, sec, frame; memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = 0xde; - cmd[1] = (scsi_CDs[minor].device->lun << 5) | 0x03; + cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? + (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] |= 0x03; cmd[2] = 0xb0; rc = sr_do_ioctl(minor, cmd, buffer, 0x16, 1, SCSI_DATA_READ, NULL); if (rc != 0) @@ -223,7 +228,9 @@ * where starts the last session ?) */ memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = 0xc7; - cmd[1] = (scsi_CDs[minor].device->lun << 5) | 3; + cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? + (scsi_CDs[minor].device->lun << 5) : 0; + cmd[1] |= 0x03; rc = sr_do_ioctl(minor, cmd, buffer, 4, 1, SCSI_DATA_READ, NULL); if (rc == -EINVAL) { printk(KERN_INFO "sr%d: Hmm, seems the drive " @@ -246,7 +253,8 @@ case VENDOR_WRITER: memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = READ_TOC; - cmd[1] = (scsi_CDs[minor].device->lun << 5); + cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? + (scsi_CDs[minor].device->lun << 5) : 0; cmd[8] = 0x04; cmd[9] = 0x40; rc = sr_do_ioctl(minor, cmd, buffer, 0x04, 1, SCSI_DATA_READ, NULL); @@ -259,7 +267,8 @@ break; } cmd[0] = READ_TOC; /* Read TOC */ - cmd[1] = (scsi_CDs[minor].device->lun << 5); + cmd[1] = (scsi_CDs[minor].device->scsi_level <= SCSI_2) ? + (scsi_CDs[minor].device->lun << 5) : 0; cmd[6] = rc & 0x7f; /* number of last session */ cmd[8] = 0x0c; cmd[9] = 0x40; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/st.c linux.ac/drivers/scsi/st.c --- linux.vanilla/drivers/scsi/st.c Sat Dec 30 19:23:14 2000 +++ linux.ac/drivers/scsi/st.c Tue Apr 3 17:55:04 2001 @@ -359,7 +359,8 @@ } } - cmd[1] |= (SRpnt->sr_device->lun << 5) & 0xe0; + if (SRpnt->sr_device->scsi_level <= SCSI_2) + cmd[1] |= (SRpnt->sr_device->lun << 5) & 0xe0; init_MUTEX_LOCKED(&STp->sem); SRpnt->sr_use_sg = (bytes > (STp->buffer)->sg[0].length) ? (STp->buffer)->use_sg : 0; @@ -3495,7 +3496,7 @@ Scsi_Tape *tpnt; ST_mode *STm; ST_partstat *STps; - int i, mode, target_nbr; + int i, mode, target_nbr, dev_num; unsigned long flags = 0; char *stp; @@ -3573,6 +3574,7 @@ } memset(tpnt, 0, sizeof(Scsi_Tape)); scsi_tapes[i] = tpnt; + dev_num = i; for (mode = 0; mode < ST_NBR_MODES; ++mode) { char name[8]; @@ -3653,6 +3655,9 @@ st_template.nr_dev++; write_unlock_irqrestore(&st_dev_arr_lock, flags); + printk(KERN_WARNING + "Attached scsi tape st%d at scsi%d, channel %d, id %d, lun %d\n", + dev_num, SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); /* See if we need to allocate more static buffers */ target_nbr = st_template.nr_dev; @@ -3673,12 +3678,7 @@ { if (SDp->type != TYPE_TAPE || st_incompatible(SDp)) return 0; - - printk(KERN_WARNING - "Detected scsi tape st%d at scsi%d, channel %d, id %d, lun %d\n", - st_template.dev_noticed++, - SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); - + st_template.dev_noticed++; return 1; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sym53c8xx.c linux.ac/drivers/scsi/sym53c8xx.c --- linux.vanilla/drivers/scsi/sym53c8xx.c Mon Apr 30 15:13:25 2001 +++ linux.ac/drivers/scsi/sym53c8xx.c Thu May 24 00:04:37 2001 @@ -85,7 +85,7 @@ /* ** Name and version of the driver */ -#define SCSI_NCR_DRIVER_NAME "sym53c8xx-1.7.3a-20010304" +#define SCSI_NCR_DRIVER_NAME "sym53c8xx-1.7.3c-20010512" #define SCSI_NCR_DEBUG_FLAGS (0) @@ -584,6 +584,9 @@ #if LINUX_VERSION_CODE < LinuxVersionCode(2,4,0) #define pci_enable_device(pdev) (0) #endif +#if LINUX_VERSION_CODE < LinuxVersionCode(2,4,4) +#define scsi_set_pci_device(inst, pdev) (0) +#endif /*========================================================== ** @@ -3781,7 +3784,7 @@ SIR_MSG_RECEIVED, }/*-------------------------< MSG_WEIRD_SEEN >------------------*/,{ - SCR_LOAD_REL (scratcha1, 4), /* DUMMY READ */ + SCR_LOAD_REL (scratcha, 4), /* DUMMY READ */ 0, SCR_INT, SIR_MSG_WEIRD, @@ -6591,7 +6594,7 @@ ** **---------------------------------------------------- */ -#if 0 /* This stuff was only usefull for linux-1.2.13 */ +#if 0 /* This stuff was only useful for linux-1.2.13 */ if (lp && !lp->numtags && cmd->device && cmd->device->tagged_queue) { lp->numtags = tp->usrtags; ncr_setup_tags (np, cp->target, cp->lun); @@ -6991,22 +6994,26 @@ u_char istat; int i; + if (!(np->features & FE_ISTAT1) || !(INB (nc_istat1) & SRUN)) + goto do_chip_reset; + OUTB (nc_istat, CABRT); - for (i = 1000000 ; i ; --i) { + for (i = 100000 ; i ; --i) { istat = INB (nc_istat); if (istat & SIP) { INW (nc_sist); - continue; } - if (istat & DIP) { - OUTB (nc_istat, 0); - INB (nc_dstat); - break; + else if (istat & DIP) { + if (INB (nc_dstat) & ABRT); + break; } + UDELAY(5); } + OUTB (nc_istat, 0); if (!i) - printk("%s: unable to abort current chip operation.\n", - ncr_name(np)); + printk("%s: unable to abort current chip operation, " + "ISTAT=0x%02x.\n", ncr_name(np), istat); +do_chip_reset: ncr_chip_reset(np); } @@ -7419,10 +7426,10 @@ /* ** On standard INQUIRY response (EVPD and CmDt ** not set), setup logical unit according to - ** announced capabilities (we need the 1rst 7 bytes). + ** announced capabilities (we need the 1rst 8 bytes). */ if (cmd->cmnd[0] == 0x12 && !(cmd->cmnd[1] & 0x3) && - cmd->cmnd[4] >= 7 && !cmd->use_sg) { + cmd->request_bufflen - cp->resid > 7 && !cmd->use_sg) { sync_scsi_data(np, cmd); /* SYNC the data */ ncr_setup_lcb (np, cp->target, cp->lun, (char *) cmd->request_buffer); @@ -7959,7 +7966,7 @@ */ fak = (kpc - 1) / div_10M[div] + 1; -#if 0 /* This optimization does not seem very usefull */ +#if 0 /* This optimization does not seem very useful */ per = (fak * div_10M[div]) / clk; @@ -8685,7 +8692,7 @@ ** scntl3: (see the manual) ** ** current script command: -** dsp: script adress (relative to start of script). +** dsp: script address (relative to start of script). ** dbc: first word of script command. ** ** First 24 register of the chip: @@ -9535,7 +9542,7 @@ #ifdef SYM_DEBUG_PM_WITH_WSR PRINT_ADDR(cp); - printf ("MA interrupt with WSR set - " + printk ("MA interrupt with WSR set - " "pm->sg.addr=%x - pm->sg.size=%d\n", pm->sg.addr, pm->sg.size); #endif @@ -10167,14 +10174,16 @@ if (i >= MAX_START*2) i = 0; } - assert(k != -1); - if (k != 1) { + /* + ** If job removed, repair the start queue. + */ + if (k != -1) { np->squeue[k] = np->squeue[i]; /* Idle task */ np->squeueput = k; /* Start queue pointer */ - cp->host_status = HS_ABORTED; - cp->scsi_status = S_ILLEGAL; - ncr_complete(np, cp); } + cp->host_status = HS_ABORTED; + cp->scsi_status = S_ILLEGAL; + ncr_complete(np, cp); } break; /* @@ -10730,7 +10739,7 @@ ** Was Sie schon immer ueber transfermode negotiation wissen wollten ... ** ** We try to negotiate sync and wide transfer only after -** a successfull inquire command. We look at byte 7 of the +** a successful inquire command. We look at byte 7 of the ** inquire data to determine the capabilities of the target. ** ** When we try to negotiate, we append the negotiation message @@ -11570,7 +11579,7 @@ /*========================================================== ** ** -** Aquire a control block +** Acquire a control block ** ** **========================================================== @@ -12222,6 +12231,7 @@ static int __init ncr_snooptest (struct ncb* np) { u_int32 ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc; + u_char dstat; int i, err=0; #ifndef SCSI_NCR_IOMAPPED if (np->reg) { @@ -12229,6 +12239,12 @@ if (err) return (err); } #endif +restart_test: + /* + ** Enable Master Parity Checking as we intend + ** to enable it for normal operations. + */ + OUTB (nc_ctest4, (np->rv_ctest4 & MPEE)); /* ** init */ @@ -12251,6 +12267,27 @@ for (i=0; i=NCR_SNOOP_TIMEOUT) { + printk ("CACHE TEST FAILED: timeout.\n"); + return (0x20); + }; + /* + ** Check for fatal DMA errors. + */ + dstat = INB (nc_dstat); +#if 1 /* Band aiding for broken hardwares that fail PCI parity */ + if ((dstat & MDPE) && (np->rv_ctest4 & MPEE)) { + printk ("%s: PCI DATA PARITY ERROR DETECTED - " + "DISABLING MASTER DATA PARITY CHECKING.\n", + ncr_name(np)); + np->rv_ctest4 &= ~MPEE; + goto restart_test; + } +#endif + if (dstat & (MDPE|BF|IID)) { + printk ("CACHE TEST FAILED: DMA error (dstat=0x%02x).", dstat); + return (0x80); + } /* ** Save termination position. */ @@ -12261,14 +12298,6 @@ host_rd = scr_to_cpu(np->ncr_cache); ncr_rd = INL (nc_scratcha); ncr_bk = INL (nc_temp); - - /* - ** check for timeout - */ - if (i>=NCR_SNOOP_TIMEOUT) { - printk ("CACHE TEST FAILED: timeout.\n"); - return (0x20); - }; /* ** Check termination position. */ @@ -14380,7 +14409,7 @@ /* save current state of GPCNTL and GPREG */ old_gpreg = INB (nc_gpreg); old_gpcntl = INB (nc_gpcntl); - gpcntl = old_gpcntl & 0xfc; + gpcntl = old_gpcntl & 0x1c; /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */ OUTB (nc_gpreg, old_gpreg); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sym53c8xx_comm.h linux.ac/drivers/scsi/sym53c8xx_comm.h --- linux.vanilla/drivers/scsi/sym53c8xx_comm.h Tue Apr 3 17:32:22 2001 +++ linux.ac/drivers/scsi/sym53c8xx_comm.h Thu May 24 00:02:48 2001 @@ -403,6 +403,9 @@ #if LINUX_VERSION_CODE < LinuxVersionCode(2,4,0) #define pci_enable_device(pdev) (0) #endif +#if LINUX_VERSION_CODE < LinuxVersionCode(2,4,4) +#define scsi_set_pci_device(inst, pdev) (0) +#endif /*========================================================== ** @@ -1311,7 +1314,7 @@ /* save current state of GPCNTL and GPREG */ old_gpreg = INB (nc_gpreg); old_gpcntl = INB (nc_gpcntl); - gpcntl = old_gpcntl & 0xfc; + gpcntl = old_gpcntl & 0x1c; /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */ OUTB (nc_gpreg, old_gpreg); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/scsi/sym53c8xx_defs.h linux.ac/drivers/scsi/sym53c8xx_defs.h --- linux.vanilla/drivers/scsi/sym53c8xx_defs.h Tue Apr 3 17:32:22 2001 +++ linux.ac/drivers/scsi/sym53c8xx_defs.h Sat May 26 18:10:50 2001 @@ -168,16 +168,17 @@ #endif /* - * Use normal IO if configured. Forced for alpha and powerpc. - * Powerpc fails copying to on-chip RAM using memcpy_toio(). + * Use normal IO if configured. Forced for alpha. */ #if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED) #define SCSI_NCR_IOMAPPED #elif defined(__alpha__) #define SCSI_NCR_IOMAPPED #elif defined(__powerpc__) +#if LINUX_VERSION_CODE <= LinuxVersionCode(2,4,3) #define SCSI_NCR_IOMAPPED #define SCSI_NCR_PCI_MEM_NOT_SUPPORTED +#endif #elif defined(__sparc__) #undef SCSI_NCR_IOMAPPED #endif @@ -734,7 +735,7 @@ #define FE_PFEN (1<<12) /* Prefetch enable */ #define FE_LDSTR (1<<13) /* Load/Store supported */ #define FE_RAM (1<<14) /* On chip RAM present */ -#define FE_VARCLK (1<<15) /* SCSI lock may vary */ +#define FE_VARCLK (1<<15) /* SCSI clock may vary */ #define FE_RAM8K (1<<16) /* On chip RAM sized 8Kb */ #define FE_64BIT (1<<17) /* Have a 64-bit PCI interface */ #define FE_IO256 (1<<18) /* Requires full 256 bytes in PCI space */ @@ -744,6 +745,7 @@ #define FE_ULTRA3 (1<<22) /* Ultra-3 80Mtrans/sec */ #define FE_66MHZ (1<<23) /* 66MHz PCI Support */ #define FE_DAC (1<<24) /* Support DAC cycles (64 bit addressing) */ +#define FE_ISTAT1 (1<<25) /* Have ISTAT1, MBOX0, MBOX1 registers */ #define FE_CACHE_SET (FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP) #define FE_SCSI_SET (FE_WIDE|FE_ULTRA|FE_ULTRA2|FE_DBLR|FE_QUAD|F_CLK80) @@ -808,7 +810,7 @@ , \ {PCI_DEVICE_ID_NCR_53C896, 0xff, "896", 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_DAC|FE_IO256|FE_NOPM|FE_LEDC} \ + FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_ISTAT1} \ , \ {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| \ @@ -823,11 +825,11 @@ FE_RAM|FE_IO256} \ , \ {PCI_DEVICE_ID_LSI_53C1010, 0xff, "1010-33", 6, 62, 7, \ - FE_WIDE|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_WIDE|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_ISTAT1| \ FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_ULTRA3} \ , \ {PCI_DEVICE_ID_LSI_53C1010_66, 0xff, "1010-66", 6, 62, 7, \ - FE_WIDE|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ + FE_WIDE|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_ISTAT1| \ FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_ULTRA3| \ FE_66MHZ} \ } @@ -1179,9 +1181,13 @@ #define SIP 0x02 /* sta: scsi-interrupt */ #define DIP 0x01 /* sta: host/script interrupt */ -/*15*/ u_char nc_istat1; /* 896 only */ -/*16*/ u_char nc_mbox0; /* 896 only */ -/*17*/ u_char nc_mbox1; /* 896 only */ +/*15*/ u_char nc_istat1; /* 896 and later cores only */ + #define FLSH 0x04 /* sta: chip is flushing */ + #define SRUN 0x02 /* sta: scripts are running */ + #define SIRQD 0x01 /* r/w: disable INT pin */ + +/*16*/ u_char nc_mbox0; /* 896 and later cores only */ +/*17*/ u_char nc_mbox1; /* 896 and later cores only */ /*18*/ u_char nc_ctest0; /*19*/ u_char nc_ctest1; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/.maui_boot.h.boot linux.ac/drivers/sound/.maui_boot.h.boot --- linux.vanilla/drivers/sound/.maui_boot.h.boot Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/sound/.maui_boot.h.boot Tue Apr 3 19:05:36 2001 @@ -0,0 +1,3 @@ +ifeq (,$(strip $(CONFIG_MAUI_HAVE_BOOT) $(CONFIG_MAUI_BOOT_FILE))) +FILES_BOOT_UP_TO_DATE += maui_boot.h +endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/.pss_boot.h.boot linux.ac/drivers/sound/.pss_boot.h.boot --- linux.vanilla/drivers/sound/.pss_boot.h.boot Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/sound/.pss_boot.h.boot Tue Apr 3 19:04:53 2001 @@ -0,0 +1,3 @@ +ifeq (,$(strip $(CONFIG_PSS_HAVE_BOOT) $(CONFIG_PSS_BOOT_FILE))) +FILES_BOOT_UP_TO_DATE += pss_boot.h +endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/.trix_boot.h.boot linux.ac/drivers/sound/.trix_boot.h.boot --- linux.vanilla/drivers/sound/.trix_boot.h.boot Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/sound/.trix_boot.h.boot Tue Apr 3 19:04:58 2001 @@ -0,0 +1,3 @@ +ifeq (,$(strip $(CONFIG_TRIX_HAVE_BOOT) $(CONFIG_TRIX_BOOT_FILE))) +FILES_BOOT_UP_TO_DATE += trix_boot.h +endif diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/724hwmcode.h linux.ac/drivers/sound/724hwmcode.h --- linux.vanilla/drivers/sound/724hwmcode.h Sun Nov 12 02:33:13 2000 +++ linux.ac/drivers/sound/724hwmcode.h Tue Apr 3 17:55:04 2001 @@ -9,7 +9,7 @@ #ifndef _HWMCODE_ #define _HWMCODE_ -static unsigned long int DspInst[] __initdata = { +static unsigned long int DspInst[] = { 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, @@ -20,7 +20,7 @@ 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; -static unsigned long int CntrlInst[] __initdata = { +static unsigned long int CntrlInst[] = { 0x000007, 0x240007, 0x0C0007, 0x1C0007, 0x060007, 0x700002, 0x000020, 0x030040, 0x007104, 0x004286, 0x030040, 0x000F0D, @@ -799,7 +799,7 @@ // 04/09?@creat // 04/12 stop nise fix // 06/21?@WorkingOff timming -static unsigned long int CntrlInst1E[] __initdata = { +static unsigned long int CntrlInst1E[] = { 0x000007, 0x240007, 0x0C0007, 0x1C0007, 0x060007, 0x700002, 0x000020, 0x030040, 0x007104, 0x004286, 0x030040, 0x000F0D, diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/Config.in linux.ac/drivers/sound/Config.in --- linux.vanilla/drivers/sound/Config.in Mon Apr 30 15:13:25 2001 +++ linux.ac/drivers/sound/Config.in Sat May 26 00:32:18 2001 @@ -6,12 +6,27 @@ # Prompt user for primary drivers. -dep_tristate ' C-Media PCI (CMI8338/8378)' CONFIG_SOUND_CMPCI $CONFIG_SOUND $CONFIG_PCI +dep_tristate ' C-Media PCI (CMI8338/8738)' CONFIG_SOUND_CMPCI $CONFIG_SOUND $CONFIG_PCI if [ "$CONFIG_SOUND_CMPCI" = "y" -o "$CONFIG_SOUND_CMPCI" = "m" ]; then - bool ' Enable S/PDIF loop for CMI8738' CONFIG_SOUND_CMPCI_SPDIFLOOP - bool ' Enable 4 channel mode for CMI8738' CONFIG_SOUND_CMPCI_4CH - if [ "$CONFIG_SOUND_CMPCI_4CH" = "y" ]; then - bool ' Separate rear out jack' CONFIG_SOUND_CMPCI_REAR + bool ' Enable legacy FM' CONFIG_SOUND_CMPCI_FM + if [ "$CONFIG_SOUND_CMPCI_FM" = "y" ]; then + define_hex CONFIG_SOUND_CMPCI_FMIO 388 + hex ' FM I/O 388, 3C8, 3E0, 3E8' CONFIG_SOUND_CMPCI_FMIO 388 + fi + bool ' Enable legacy MPU-401' CONFIG_SOUND_CMPCI_MIDI + if [ "$CONFIG_SOUND_CMPCI_MIDI" = "y" ]; then + hex ' MPU-401 I/O 330, 320, 310, 300' CONFIG_SOUND_CMPCI_MPUIO 330 + fi + bool ' Enable joystick' CONFIG_SOUND_CMPCI_JOYSTICK + bool ' Support CMI8738 based audio cards' CONFIG_SOUND_CMPCI_CM8738 + if [ "$CONFIG_SOUND_CMPCI_CM8738" = "y" ]; then + bool ' Inverse S/PDIF in for CMI8738' CONFIG_SOUND_CMPCI_SPDIFINVERSE + bool ' Enable S/PDIF loop for CMI8738' CONFIG_SOUND_CMPCI_SPDIFLOOP + int ' Number of speakers 2, 4, 5, 6' CONFIG_SOUND_CMPCI_SPEAKERS 2 + if [ "$CONFIG_SOUND_CMPCI_SPEAKERS" != "2" ]; then + bool ' Use Line-in as Read-out' CONFIG_SOUND_CMPCI_LINE_REAR + bool ' Use Line-in as Bass' CONFIG_SOUND_CMPCI_LINE_BASS + fi fi fi dep_tristate ' Creative SBLive! (EMU10K1)' CONFIG_SOUND_EMU10K1 $CONFIG_SOUND $CONFIG_PCI diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ac97.c linux.ac/drivers/sound/ac97.c --- linux.vanilla/drivers/sound/ac97.c Thu Jan 6 23:01:56 2000 +++ linux.ac/drivers/sound/ac97.c Tue Apr 3 17:55:04 2001 @@ -407,19 +407,19 @@ /* Read or write request. */ ret = -EINVAL; if (_IOC_TYPE (cmd) == 'M') { - int dir = _IOC_DIR (cmd); + int dir = _SIOC_DIR (cmd); int channel = _IOC_NR (cmd); if (channel >= 0 && channel < SOUND_MIXER_NRDEVICES) { ret = 0; - if (dir & _IOC_WRITE) { + if (dir & _SIOC_WRITE) { int val; if (get_user (val, (int *) arg) == 0) ret = ac97_set_mixer (dev, channel, val); else ret = -EFAULT; } - if (ret >= 0 && (dir & _IOC_READ)) { + if (ret >= 0 && (dir & _SIOC_READ)) { if (dev->last_written_OSS_values[channel] == AC97_REGVAL_UNKNOWN) dev->last_written_OSS_values[channel] diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/cmpci.c linux.ac/drivers/sound/cmpci.c --- linux.vanilla/drivers/sound/cmpci.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/sound/cmpci.c Tue May 22 11:06:18 2001 @@ -3,10 +3,15 @@ /* * cmpci.c -- C-Media PCI audio driver. * - * Copyright (C) 1999 ChenLi Tien (cltien@home.com) + * Copyright (C) 1999 ChenLi Tien (cltien@cmedia.com.tw) + * C-media support (support@cmedia.com.tw) * - * Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.ethz.ch) + * Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.ethz.ch) * + * For update, visit: + * http://members.home.net/puresoft/cmedia.html + * http://www.cmedia.com.tw + * * 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 @@ -57,8 +62,6 @@ * reported by Johan Maes * 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK * read/write cannot be executed - * 20 09 99 0.13 merged the generic changes in sonicvibes since this - * diverged. * 18.08.99 1.5 Only deallocate DMA buffer when unloading. * 02.09.99 1.6 Enable SPDIF LOOP * Change the mixer read back @@ -66,47 +69,27 @@ * Add support for modem, S/PDIF loop and 4 channels. * (8738 only) * Fix bug cause x11amp cannot play. - * $Log: cmpci.c,v $ - * Revision 2.41 1999/10/27 02:00:05 cltien - * Now the fragsize for modem is activated by parameter. - * - * Revision 2.40 1999/10/26 23:38:26 cltien - * Remove debugging message in cm_write which may cause module counter not 0. - * - * Revision 2.39 1999/10/26 21:52:50 cltien - * I forgor too adjust mic recording volume, as it should be moved to 5MUTEMONO. - * Change the DYNAMIC macro to FIXEDDMA, which means static DMA buffer. - * - * Revision 2.38 1999/10/08 21:59:03 cltien - * Set FLINKON and reset FLINKOFF for modem. - * - * Revision 2.37 1999/09/28 02:57:04 cltien - * Add set_bus_master() to make sure bus master enabled. - * - * Revision 2.36 1999/09/22 14:15:03 cltien - * Use open_sem to avoid multiple access to open_mode. - * Use wakeup in IntrClose to activate process in waiting queue. - * - * Revision 2.35 1999/09/22 13:20:53 cltien - * Use open_mode to check if DAC in used. Also more check in IntrWrite and IntrClose. Now the modem can access DAC safely. - * - * Revision 2.34 1999/09/22 03:29:57 cltien - * Use module count to decide which one to access the dac. * + * Fixes: + * Arnaldo Carvalho de Melo + * 18/05/2001 - .bss nitpicks, fix a bug in set_dac_channels where it + * was calling prog_dmabuf with s->lock held, call missing + * unlock_kernel in cm_midi_release * */ - + /*****************************************************************************/ -#include +#define EXPORT_SYMTAB #include +#include #include #include #include #include #include #include -#include +#include #include #include #include @@ -139,6 +122,9 @@ #ifndef PCI_DEVICE_ID_CMEDIA_CM8738 #define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 #endif +#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B +#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 +#endif #define CM_MAGIC ((PCI_VENDOR_ID_CMEDIA<<16)|PCI_DEVICE_ID_CMEDIA_CM8338A) @@ -217,14 +203,14 @@ #define CM_CFMT_STEREO 0x01 #define CM_CFMT_16BIT 0x02 #define CM_CFMT_MASK 0x03 -#define CM_CFMT_DACSHIFT 0 -#define CM_CFMT_ADCSHIFT 2 +#define CM_CFMT_DACSHIFT 2 +#define CM_CFMT_ADCSHIFT 0 static const unsigned sample_size[] = { 1, 2, 2, 4 }; static const unsigned sample_shift[] = { 0, 1, 1, 2 }; -#define CM_CENABLE_RE 0x2 -#define CM_CENABLE_PE 0x1 +#define CM_ENABLE_CH1 0x2 +#define CM_ENABLE_CH0 0x1 /* MIDI buffer sizes */ @@ -238,6 +224,12 @@ #define FMODE_DMFM 0x10 +#define SND_DEV_DSP16 5 + +#define set_dac1_rate set_adc_rate +#define stop_dac1 stop_adc +#define get_dmadac1 get_dmaadc + /* --------------------------------------------------------------------- */ struct cm_state { @@ -255,6 +247,7 @@ /* hardware resources */ unsigned int iosb, iobase, iosynth, iomidi, iogame, irq; + unsigned short deviceid; /* mixer stuff */ struct { @@ -275,6 +268,7 @@ struct dmabuf { void *rawbuf; + unsigned rawphys; unsigned buforder; unsigned numfrag; unsigned fragshift; @@ -307,12 +301,49 @@ unsigned char ibuf[MIDIINBUF]; unsigned char obuf[MIDIOUTBUF]; } midi; + + /* misc stuff */ + int chip_version; + int max_channels; + int curr_channels; + int speakers; // number of speakers + int capability; // HW capability, various for chip versions + int status; // HW or SW state + + /* spdif frame counter */ + int spdif_counter; }; +/* flags used for capability */ +#define CAN_AC3_HW 0x00000001 // 037 or later +#define CAN_AC3_SW 0x00000002 // 033 or later +#define CAN_AC3 (CAN_AC3_HW | CAN_AC3_SW) +#define CAN_DUAL_DAC 0x00000004 // 033 or later +#define CAN_MULTI_CH_HW 0x00000008 // 039 or later +#define CAN_MULTI_CH (CAN_MULTI_CH_HW | CAN_DUAL_DAC) +#define CAN_LINE_AS_REAR 0x00000010 // 033 or later +#define CAN_LINE_AS_BASS 0x00000020 // 039 or later +#define CAN_MIC_AS_BASS 0x00000040 // 039 or later + +/* flags used for status */ +#define DO_AC3_HW 0x00000001 +#define DO_AC3_SW 0x00000002 +#define DO_AC3 (DO_AC3_HW | DO_AC3_SW) +#define DO_DUAL_DAC 0x00000004 +#define DO_MULTI_CH_HW 0x00000008 +#define DO_MULTI_CH (DO_MULTI_CH_HW | DO_DUAL_DAC) +#define DO_LINE_AS_REAR 0x00000010 // 033 or later +#define DO_LINE_AS_BASS 0x00000020 // 039 or later +#define DO_MIC_AS_BASS 0x00000040 // 039 or later +#define DO_SPDIF_OUT 0x00000100 +#define DO_SPDIF_IN 0x00000200 +#define DO_SPDIF_LOOP 0x00000400 + /* --------------------------------------------------------------------- */ -static struct cm_state *devs = NULL; -static unsigned long wavetable_mem = 0; +static struct cm_state *devs; +static struct cm_state *devaudio; +static unsigned long wavetable_mem; /* --------------------------------------------------------------------- */ @@ -361,35 +392,76 @@ /* --------------------------------------------------------------------- */ -static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count) +/* + * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver. + */ + +#undef DMABYTEIO + +static void maskb(unsigned int addr, unsigned int mask, unsigned int value) { - count--; - outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); - outw(count, s->iobase + CODEC_CMI_CH0_FRAME2); - outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) & ~1, s->iobase + CODEC_CMI_FUNCTRL0); -// outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 1, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + outb((inb(addr) & mask) | value, addr); +} + +static void maskw(unsigned int addr, unsigned int mask, unsigned int value) +{ + outw((inw(addr) & mask) | value, addr); +} + +static void maskl(unsigned int addr, unsigned int mask, unsigned int value) +{ + outl((inl(addr) & mask) | value, addr); +} + +static void set_dmadac1(struct cm_state *s, unsigned int addr, unsigned int count) +{ + if (addr) + outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); + outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~1, 0); } static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count) { - count--; + outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); + outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 1); +} + +static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count) +{ outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1); - outw(count, s->iobase + CODEC_CMI_CH1_FRAME2); - outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) | 2, s->iobase + CODEC_CMI_FUNCTRL0); -// outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 2, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 0); + if (s->status & DO_DUAL_DAC) + set_dmadac1(s, 0, count); +} + +static void set_countadc(struct cm_state *s, unsigned count) +{ + outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2); +} + +static void set_countdac(struct cm_state *s, unsigned count) +{ + outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2); + if (s->status & DO_DUAL_DAC) + set_countadc(s, count); } extern __inline__ unsigned get_dmadac(struct cm_state *s) { unsigned int curr_addr; - if (!s->dma_dac.dmasize || !(s->enable & CM_CENABLE_PE)) - return 0; - - curr_addr = inl(s->iobase + CODEC_CMI_CH0_FRAME1); - curr_addr -= virt_to_bus(s->dma_dac.rawbuf); +#if 1 + curr_addr = inw(s->iobase + CODEC_CMI_CH1_FRAME2) + 1; + curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; curr_addr = s->dma_dac.dmasize - curr_addr; +#else + curr_addr = inl(s->iobase + CODEC_CMI_CH1_FRAME1); curr_addr &= ~(sample_size[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]-1); + curr_addr -= s->dma_dac.rawphys; +#endif return curr_addr; } @@ -397,22 +469,22 @@ { unsigned int curr_addr; - if (!s->dma_adc.dmasize || !(s->enable & CM_CENABLE_RE)) - return 0; - - curr_addr = inl(s->iobase + CODEC_CMI_CH1_FRAME1); - curr_addr -= virt_to_bus(s->dma_adc.rawbuf); +#if 1 + curr_addr = inw(s->iobase + CODEC_CMI_CH0_FRAME2) + 1; + curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]; curr_addr = s->dma_adc.dmasize - curr_addr; +#else + curr_addr = inl(s->iobase + CODEC_CMI_CH0_FRAME1); curr_addr &= ~(sample_size[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]-1); + curr_addr -= s->dma_adc.rawphys; +#endif return curr_addr; } static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data) { outb(idx, s->iobase + CODEC_SB16_ADDR); - udelay(10); outb(data, s->iobase + CODEC_SB16_DATA); - udelay(10); } static unsigned char rdmixer(struct cm_state *s, unsigned char idx) @@ -420,9 +492,7 @@ unsigned char v; outb(idx, s->iobase + CODEC_SB16_ADDR); - udelay(10); v = inb(s->iobase + CODEC_SB16_DATA); - udelay(10); return v; } @@ -431,22 +501,17 @@ unsigned long flags; spin_lock_irqsave(&s->lock, flags); - if (mask) { + if (mask) s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT); - udelay(10); - } s->fmt = (s->fmt & mask) | data; outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT); spin_unlock_irqrestore(&s->lock, flags); - udelay(10); } static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data) { outb(idx, s->iobase + CODEC_SB16_ADDR); - udelay(10); outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA); - udelay(10); } static struct { @@ -463,96 +528,299 @@ { 22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2 }, { 32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6 }, { 44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3 }, - { 48000, (44100 + 48000) /2, 48000, 7 } + { 48000, (44100 + 48000) / 2, 48000, 7 } }; -static void set_dac_rate(struct cm_state *s, unsigned rate) +static void set_spdifout(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if (rate == 48000 || rate == 44100) { + // SPDIFI48K SPDF_ACc97 + maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~0x01008000, rate == 48000 ? 0x01008000 : 0); + // ENSPDOUT + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0, 0x80); + // SPDF_1 SPD2DAC + maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x240); + // CDPLAY + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1); + s->status |= DO_SPDIF_OUT; + } else { + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0x80, 0); + maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~0x240, 0); + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0); + s->status &= ~DO_SPDIF_OUT; + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* find parity for bit 4~30 */ +static unsigned parity(unsigned data) +{ + unsigned parity = 0; + int counter = 4; + + data >>= 4; // start from bit 4 + while (counter <= 30) { + if (data & 1) + parity++; + data >>= 1; + counter++; + } + return parity & 1; +} + +static void set_ac3(struct cm_state *s, unsigned rate) { unsigned long flags; - unsigned char freq = 4, val; + + spin_lock_irqsave(&s->lock, flags); + set_spdifout(s, rate); + /* enable AC3 */ + if (rate == 48000 || rate == 44100) { + // mute DAC + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 0x40); + // AC3EN for 037, 0x10 + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x10); + // AC3EN for 039, 0x04 + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x04); + if (s->capability & CAN_AC3_HW) { + // SPD24SEL for 037, 0x02 + // SPD24SEL for 039, 0x20, but cannot be set + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x02); + s->status |= DO_AC3_HW; + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~1, 0); + } else { + // SPD32SEL for 037 & 039, 0x20 + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0x20); + // set 176K sample rate to fix 033 HW bug + if (s->chip_version == 33) { + if (rate == 48000) + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0, 0x08); + else + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); + } + s->status |= DO_AC3_SW; + } + } else { + maskb(s->iobase + CODEC_CMI_MIXER1, ~0x40, 0); + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0x32, 0); + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x24, 0); + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); + if (s->chip_version == 33) + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 1); + s->status &= ~DO_AC3; + } + s->spdif_counter = 0; + spin_unlock_irqrestore(&s->lock, flags); +} + +static void trans_ac3(struct cm_state *s, void *dest, const char *source, int size) +{ + int i = size / 2; + unsigned long data; + unsigned long *dst = (unsigned long *) dest; + unsigned short *src = (unsigned short *)source; + + do { + data = (unsigned long) *src++; + data <<= 12; // ok for 16-bit data + if (s->spdif_counter == 2 || s->spdif_counter == 3) + data |= 0x40000000; // indicate AC-3 raw data + if (parity(data)) + data |= 0x80000000; // parity + if (s->spdif_counter == 0) + data |= 3; // preamble 'M' + else if (s->spdif_counter & 1) + data |= 5; // odd, 'W' + else + data |= 9; // even, 'M' + *dst++ = data; + s->spdif_counter++; + if (s->spdif_counter == 384) + s->spdif_counter = 0; + } while (--i); +} + +static void set_adc_rate(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + unsigned char freq = 4; int i; if (rate > 48000) rate = 48000; - if (rate < 5512) - rate = 5512; - for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) - { - if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) - { + if (rate < 8000) + rate = 8000; + for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { + if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { rate = rate_lookup[i].rate; freq = rate_lookup[i].freq; break; } } - s->ratedac = rate; + s->rateadc = rate; freq <<= 2; spin_lock_irqsave(&s->lock, flags); - val = inb(s->iobase + CODEC_CMI_FUNCTRL1 + 1) & ~0x1c; - outb(val | freq, s->iobase + CODEC_CMI_FUNCTRL1 + 1); + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0x1c, freq); spin_unlock_irqrestore(&s->lock, flags); } -static void set_adc_rate(struct cm_state *s, unsigned rate) +static void set_dac_rate(struct cm_state *s, unsigned rate) { unsigned long flags; - unsigned char freq = 4, val; + unsigned char freq = 4; int i; if (rate > 48000) rate = 48000; - if (rate < 5512) - rate = 5512; - for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) - { - if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) - { + if (rate < 8000) + rate = 8000; + for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { + if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { rate = rate_lookup[i].rate; freq = rate_lookup[i].freq; break; } } - s->rateadc = rate; + s->ratedac = rate; freq <<= 5; spin_lock_irqsave(&s->lock, flags); - val = inb(s->iobase + CODEC_CMI_FUNCTRL1 + 1) & ~0xe0; - outb(val | freq, s->iobase + CODEC_CMI_FUNCTRL1 + 1); + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0xe0, freq); spin_unlock_irqrestore(&s->lock, flags); + if (s->curr_channels <= 2) + set_spdifout(s, rate); + if (s->status & DO_DUAL_DAC) + set_dac1_rate(s, rate); } /* --------------------------------------------------------------------- */ +static inline void reset_adc(struct cm_state *s) +{ + /* reset bus master */ + outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); +} + +static inline void reset_dac(struct cm_state *s) +{ + /* reset bus master */ + outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + if (s->status & DO_DUAL_DAC) + reset_adc(s); +} + +static inline void pause_adc(struct cm_state *s) +{ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 4); +} + +static inline void pause_dac(struct cm_state *s) +{ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 8); + if (s->status & DO_DUAL_DAC) + pause_adc(s); +} + +extern inline void disable_adc(struct cm_state *s) +{ + /* disable channel */ + s->enable &= ~CM_ENABLE_CH0; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + reset_adc(s); +} + +extern inline void disable_dac(struct cm_state *s) +{ + /* disable channel */ + s->enable &= ~CM_ENABLE_CH1; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + reset_dac(s); + if (s->status & DO_DUAL_DAC) + disable_adc(s); +} + +extern inline void enable_adc(struct cm_state *s) +{ + if (!(s->enable & CM_ENABLE_CH0)) { + /* enable channel */ + s->enable |= CM_ENABLE_CH0; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + } + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~4, 0); +} + +extern inline void enable_dac(struct cm_state *s) +{ + if (!(s->enable & CM_ENABLE_CH1)) { + /* enable channel */ + s->enable |= CM_ENABLE_CH1; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + } + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~8, 0); + if (s->status & DO_DUAL_DAC) + enable_adc(s); +} extern inline void stop_adc(struct cm_state *s) { unsigned long flags; spin_lock_irqsave(&s->lock, flags); - /* disable channel */ - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - s->enable &= ~CM_CENABLE_RE; - /* disable interrupt */ - outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - /* reset */ - outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - udelay(10); - outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + if (s->enable & CM_ENABLE_CH0) { + /* disable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~1, 0); + disable_adc(s); + } spin_unlock_irqrestore(&s->lock, flags); -} +} extern inline void stop_dac(struct cm_state *s) { unsigned long flags; spin_lock_irqsave(&s->lock, flags); - /* disable channel */ - s->enable &= ~CM_CENABLE_PE; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - /* disable interrupt */ - outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - /* reset */ - outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); - udelay(10); - outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + if (s->enable & CM_ENABLE_CH1) { + /* disable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~2, 0); + disable_dac(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (s->status & DO_DUAL_DAC) + stop_dac1(s); +} + +static void start_adc(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + /* enable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1); + enable_adc(s); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_adc.mapped || s->dma_adc.count > 0) && s->dma_adc.ready) { + /* enable interrupt */ +// maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 1); + enable_dac(s); + } spin_unlock_irqrestore(&s->lock, flags); } @@ -562,26 +830,80 @@ spin_lock_irqsave(&s->lock, flags); if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { - outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - s->enable |= CM_CENABLE_PE; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + /* enable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, 2); + enable_dac(s); } spin_unlock_irqrestore(&s->lock, flags); + if (s->status & DO_DUAL_DAC) + start_dac1(s); } -static void start_adc(struct cm_state *s) +static int prog_dmabuf(struct cm_state *s, unsigned rec); + +static int set_dac_channels(struct cm_state *s, int channels) { unsigned long flags; spin_lock_irqsave(&s->lock, flags); - if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - s->enable |= CM_CENABLE_RE; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + if ((channels > 2) && (channels <= s->max_channels) + && (((s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK) == (CM_CFMT_STEREO | CM_CFMT_16BIT))) { + set_spdifout(s, 0); + if (s->capability & CAN_MULTI_CH_HW) { + // NXCHG + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0, 0x80); + // CHB3D or CHB3D5C + maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, channels > 4 ? 0x80 : 0x20); + // CHB3D6C + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, channels == 6 ? 0x80 : 0); + // ENCENTER + maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~0x80, channels == 6 ? 0x80 : 0); + s->status |= DO_MULTI_CH_HW; + } else if (s->capability & CAN_DUAL_DAC) { + unsigned char fmtm = ~0, fmts = 0; + ssize_t ret; + + // ENDBDAC, turn on double DAC mode + // XCHGDAC, CH0 -> back, CH1->front + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 0xC0); + s->status |= DO_DUAL_DAC; + // prepare secondary buffer + spin_unlock_irqrestore(&s->lock, flags); + ret = prog_dmabuf(s, 1); + if (ret) return ret; + spin_lock_irqsave(&s->lock, flags); + // copy the hw state + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT); + // the HW only support 16-bit stereo + fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; + fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; + fmts |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + fmts |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + set_fmt(s, fmtm, fmts); + set_dac1_rate(s, s->ratedac); + } + // N4SPK3D, disable 4 speaker mode (analog duplicate) + if (s->speakers > 2) + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0x04, 0); + s->curr_channels = channels; + } else { + if (s->status & DO_MULTI_CH_HW) { + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x80, 0); + maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, 0); + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, 0); + } else if (s->status & DO_DUAL_DAC) { + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x80, 0); + } + // N4SPK3D, enable 4 speaker mode (analog duplicate) + if (s->speakers > 2) + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0, 0x04); + s->status &= ~DO_MULTI_CH; + s->curr_channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; } spin_unlock_irqrestore(&s->lock, flags); -} + return s->curr_channels; +} /* --------------------------------------------------------------------- */ @@ -591,7 +913,7 @@ static void dealloc_dmabuf(struct dmabuf *db) { struct page *pstart, *pend; - + if (db->rawbuf) { /* undo marking the pages as reserved */ pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); @@ -604,7 +926,7 @@ } -/* Ch0 is used for playback, Ch1 is used for recording */ +/* Ch1 is used for playback, Ch0 is used for recording */ static int prog_dmabuf(struct cm_state *s, unsigned rec) { @@ -620,13 +942,12 @@ spin_lock_irqsave(&s->lock, flags); fmt = s->fmt; if (rec) { - s->enable &= ~CM_CENABLE_RE; + stop_adc(s); fmt >>= CM_CFMT_ADCSHIFT; } else { - s->enable &= ~CM_CENABLE_PE; + stop_dac(s); fmt >>= CM_CFMT_DACSHIFT; } - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); spin_unlock_irqrestore(&s->lock, flags); fmt &= CM_CFMT_MASK; db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; @@ -638,12 +959,13 @@ if (!db->rawbuf) return -ENOMEM; db->buforder = order; - if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) - printk(KERN_DEBUG "cmpci: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", - virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); - if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) - printk(KERN_DEBUG "cmpci: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", - virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); + db->rawphys = virt_to_bus(db->rawbuf); + if ((db->rawphys ^ (db->rawphys + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) + printk(KERN_DEBUG "cm: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", + (long) db->rawphys, PAGE_SIZE << db->buforder); + if ((db->rawphys + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) + printk(KERN_DEBUG "cm: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", + (long) db->rawphys, PAGE_SIZE << db->buforder); /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) @@ -670,30 +992,22 @@ if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) db->numfrag = db->ossmaxfrags; /* to make fragsize >= 4096 */ -#if 0 - if(s->modem) - { - while (db->fragsize < 4096 && db->numfrag >= 4) - { - db->fragsize *= 2; - db->fragshift++; - db->numfrag /= 2; - } - } -#endif db->fragsamples = db->fragsize >> sample_shift[fmt]; db->dmasize = db->numfrag << db->fragshift; db->dmasamples = db->dmasize >> sample_shift[fmt]; memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize); spin_lock_irqsave(&s->lock, flags); if (rec) { - set_dmaadc(s, virt_to_bus(db->rawbuf), db->dmasize >> sample_shift[fmt]); + if (s->status & DO_DUAL_DAC) + set_dmadac1(s, db->rawphys, db->dmasize >> sample_shift[fmt]); + else + set_dmaadc(s, db->rawphys, db->dmasize >> sample_shift[fmt]); /* program sample counts */ - outw(db->fragsamples-1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2); + set_countdac(s, db->fragsamples); } else { - set_dmadac(s, virt_to_bus(db->rawbuf), db->dmasize >> sample_shift[fmt]); + set_dmadac(s, db->rawphys, db->dmasize >> sample_shift[fmt]); /* program sample counts */ - outw(db->fragsamples-1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2); + set_countdac(s, db->fragsamples); } spin_unlock_irqrestore(&s->lock, flags); db->ready = 1; @@ -704,6 +1018,7 @@ { unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80; unsigned char *buf = s->dma_dac.rawbuf; + unsigned char *buf1 = s->dma_adc.rawbuf; unsigned bsize = s->dma_dac.dmasize; unsigned bptr = s->dma_dac.swptr; unsigned len = s->dma_dac.fragsize; @@ -711,11 +1026,14 @@ if (bptr + len > bsize) { unsigned x = bsize - bptr; memset(buf + bptr, c, x); + if (s->status & DO_DUAL_DAC) + memset(buf1 + bptr, c, x); bptr = 0; len -= x; } memset(buf + bptr, c, len); - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + if (s->status & DO_DUAL_DAC) + memset(buf1 + bptr, c, len); } /* call with spinlock held! */ @@ -726,7 +1044,29 @@ /* update ADC pointer */ if (s->dma_adc.ready) { - hwptr = (s->dma_adc.dmasize - get_dmaadc(s)) % s->dma_adc.dmasize; + if (s->status & DO_DUAL_DAC) { + hwptr = get_dmaadc(s) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + if (s->dma_adc.mapped) { + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + s->dma_adc.count -= diff; + if (s->dma_adc.count <= 0) { + pause_adc(s); + s->dma_adc.error++; + } else if (s->dma_adc.count <= (signed)s->dma_adc.fragsize && !s->dma_adc.endcleared) { + clear_advance(s); + s->dma_adc.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) + wake_up(&s->dma_adc.wait); + } + } else { + hwptr = get_dmaadc(s) % s->dma_adc.dmasize; diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; s->dma_adc.hwptr = hwptr; s->dma_adc.total_bytes += diff; @@ -735,15 +1075,15 @@ wake_up(&s->dma_adc.wait); if (!s->dma_adc.mapped) { if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - s->enable &= ~CM_CENABLE_RE; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + pause_adc(s); s->dma_adc.error++; } } + } } /* update DAC pointer */ if (s->dma_dac.ready) { - hwptr = (s->dma_dac.dmasize - get_dmadac(s)) % s->dma_dac.dmasize; + hwptr = get_dmadac(s) % s->dma_dac.dmasize; diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; s->dma_dac.hwptr = hwptr; s->dma_dac.total_bytes += diff; @@ -754,8 +1094,7 @@ } else { s->dma_dac.count -= diff; if (s->dma_dac.count <= 0) { - s->enable &= ~CM_CENABLE_PE; - outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + pause_dac(s); s->dma_dac.error++; } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { clear_advance(s); @@ -767,6 +1106,7 @@ } } +#ifdef CONFIG_SOUND_CMPCI_MIDI /* hold spinlock for the following! */ static void cm_handle_midi(struct cm_state *s) { @@ -796,11 +1136,13 @@ if (wake) wake_up(&s->midi.owait); } +#endif static void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct cm_state *s = (struct cm_state *)dev_id; unsigned int intsrc, intstat; + unsigned char mask = 0; /* fastpath out, to ease interrupt sharing */ intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS); @@ -810,22 +1152,19 @@ intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* acknowledge interrupt */ if (intsrc & CM_INT_CH0) - { - outb(intstat & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - udelay(10); - outb(intstat | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - } + mask |= 1; if (intsrc & CM_INT_CH1) - { - outb(intstat & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - udelay(10); - outb(intstat | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2); - } + mask |= 2; + outb(intstat & ~mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); + outb(intstat | mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); cm_update_ptr(s); +#ifdef CONFIG_SOUND_CMPCI_MIDI cm_handle_midi(s); +#endif spin_unlock(&s->lock); } +#ifdef CONFIG_SOUND_CMPCI_MIDI static void cm_midi_timer(unsigned long data) { struct cm_state *s = (struct cm_state *)data; @@ -837,10 +1176,11 @@ s->midi.timer.expires = jiffies+1; add_timer(&s->midi.timer); } +#endif /* --------------------------------------------------------------------- */ -static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n"; +static const char invalid_magic[] = KERN_CRIT "cm: invalid magic value\n"; #ifdef CONFIG_SOUND_CMPCI /* support multiple chips */ #define VALIDATE_STATE(s) @@ -983,9 +1323,9 @@ } if (cmd == OSS_GETVERSION) return put_user(SOUND_VERSION, (int *)arg); - if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) return -EINVAL; - if (_SIOC_DIR(cmd) == _SIOC_READ) { + if (_IOC_DIR(cmd) == _IOC_READ) { switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ return put_user(mixer_recmask(s), (int *)arg); @@ -1033,7 +1373,7 @@ #endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ } } - if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) return -EINVAL; s->mix.modcnt++; switch (_IOC_NR(cmd)) { @@ -1100,7 +1440,7 @@ rl = (l < 4 ? 0 : (l - 5) / 3) & 31; rr = (rl >> 2) & 7; wrmixer(s, mixtable[i].left, rl<<3); - outb((inb(s->iobase + CODEC_CMI_MIXER2) & ~0x0e) | rr<<1, s->iobase + CODEC_CMI_MIXER2); + maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); break; case MT_5MUTEMONO: @@ -1108,7 +1448,7 @@ rl = l < 4 ? 0 : (l - 5) / 3; rr = rl >> 2; wrmixer(s, mixtable[i].left, rl<<3); - outb((inb(s->iobase + CODEC_CMI_MIXER2) & ~0x0e) | rr<<1, s->iobase + CODEC_CMI_MIXER2); + maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); break; case MT_5MUTE: @@ -1117,7 +1457,7 @@ wrmixer(s, mixtable[i].left, rl<<3); wrmixer(s, mixtable[i].right, rr<<3); break; - + case MT_6MUTE: if (l < 6) rl = 0x00; @@ -1187,19 +1527,20 @@ release: cm_release_mixdev, }; + /* --------------------------------------------------------------------- */ static int drain_dac(struct cm_state *s, int nonblock) { - DECLARE_WAITQUEUE(wait, current); + DECLARE_WAITQUEUE(wait, current); unsigned long flags; int count, tmo; if (s->dma_dac.mapped || !s->dma_dac.ready) return 0; + current->state = TASK_INTERRUPTIBLE; add_wait_queue(&s->dma_dac.wait, &wait); for (;;) { - set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->dma_dac.count; spin_unlock_irqrestore(&s->lock, flags); @@ -1212,10 +1553,10 @@ current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->ratedac; + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "cmpci: dma timed out??\n"); + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "cm: dma timed out??\n"); } remove_wait_queue(&s->dma_dac.wait, &wait); current->state = TASK_RUNNING; @@ -1263,14 +1604,14 @@ if (file->f_flags & O_NONBLOCK) return ret ? ret : -EAGAIN; if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { - printk(KERN_DEBUG "cmpci: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + printk(KERN_DEBUG "cm: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, s->dma_adc.hwptr, s->dma_adc.swptr); stop_adc(s); spin_lock_irqsave(&s->lock, flags); - set_dmaadc(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.dmasamples); + set_dmaadc(s, s->dma_adc.rawphys, s->dma_adc.dmasamples); /* program sample counts */ - outw(s->dma_adc.fragsamples-1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2); + set_countadc(s, s->dma_adc.fragsamples); s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; spin_unlock_irqrestore(&s->lock, flags); } @@ -1310,62 +1651,113 @@ return ret; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + if (s->status & DO_DUAL_DAC) { + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + } ret = 0; #if 0 spin_lock_irqsave(&s->lock, flags); cm_update_ptr(s); spin_unlock_irqrestore(&s->lock, flags); -#endif +#endif while (count > 0) { spin_lock_irqsave(&s->lock, flags); if (s->dma_dac.count < 0) { s->dma_dac.count = 0; s->dma_dac.swptr = s->dma_dac.hwptr; } + if (s->status & DO_DUAL_DAC) { + s->dma_adc.swptr = s->dma_dac.swptr; + s->dma_adc.count = s->dma_dac.count; + s->dma_adc.endcleared = s->dma_dac.endcleared; + } swptr = s->dma_dac.swptr; cnt = s->dma_dac.dmasize-swptr; - if (s->dma_dac.count + cnt > s->dma_dac.dmasize) - cnt = s->dma_dac.dmasize - s->dma_dac.count; + if (s->status & DO_AC3_SW) { + if (s->dma_dac.count + 2 * cnt > s->dma_dac.dmasize) + cnt = (s->dma_dac.dmasize - s->dma_dac.count) / 2; + } else { + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + } spin_unlock_irqrestore(&s->lock, flags); if (cnt > count) cnt = count; + if ((s->status & DO_DUAL_DAC) && (cnt > count / 2)) + cnt = count / 2; if (cnt <= 0) { start_dac(s); if (file->f_flags & O_NONBLOCK) return ret ? ret : -EAGAIN; if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { - printk(KERN_DEBUG "cmpci: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + printk(KERN_DEBUG "cm: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, s->dma_dac.hwptr, s->dma_dac.swptr); stop_dac(s); spin_lock_irqsave(&s->lock, flags); - set_dmadac(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.dmasamples); + set_dmadac(s, s->dma_dac.rawphys, s->dma_dac.dmasamples); /* program sample counts */ - outw(s->dma_dac.fragsamples-1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2); + set_countdac(s, s->dma_dac.fragsamples); s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + if (s->status & DO_DUAL_DAC) { + set_dmadac1(s, s->dma_adc.rawphys, s->dma_adc.dmasamples); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + } spin_unlock_irqrestore(&s->lock, flags); } if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; continue; } - if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) - return ret ? ret : -EFAULT; - swptr = (swptr + cnt) % s->dma_dac.dmasize; + if (s->status & DO_AC3_SW) { + // clip exceeded data, caught by 033 and 037 + if (swptr + 2 * cnt > s->dma_dac.dmasize) + cnt = (s->dma_dac.dmasize - swptr) / 2; + trans_ac3(s, s->dma_dac.rawbuf + swptr, buffer, cnt); + swptr = (swptr + 2 * cnt) % s->dma_dac.dmasize; + } else if (s->status & DO_DUAL_DAC) { + int i; + unsigned long *src, *dst0, *dst1; + + src = (unsigned long *) buffer; + dst0 = (unsigned long *) (s->dma_dac.rawbuf + swptr); + dst1 = (unsigned long *) (s->dma_adc.rawbuf + swptr); + // copy left/right sample at one time + for (i = 0; i <= cnt / 4; i++) { + *dst0++ = *src++; + *dst1++ = *src++; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + } else { + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac.dmasize; + } spin_lock_irqsave(&s->lock, flags); s->dma_dac.swptr = swptr; s->dma_dac.count += cnt; + if (s->status & DO_AC3_SW) + s->dma_dac.count += cnt; s->dma_dac.endcleared = 0; spin_unlock_irqrestore(&s->lock, flags); count -= cnt; buffer += cnt; ret += cnt; + if (s->status & DO_DUAL_DAC) { + count -= cnt; + buffer += cnt; + ret += cnt; + } start_dac(s); } return ret; } -/* No kernel lock - fine (we have our own spinlock) */ static unsigned int cm_poll(struct file *file, struct poll_table_struct *wait) { struct cm_state *s = (struct cm_state *)file->private_data; @@ -1373,17 +1765,10 @@ unsigned int mask = 0; VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) { - if (!s->dma_dac.ready && prog_dmabuf(s, 0)) - return 0; + if (file->f_mode & FMODE_WRITE) poll_wait(file, &s->dma_dac.wait, wait); - } - if (file->f_mode & FMODE_READ) { - if (!s->dma_adc.ready && prog_dmabuf(s, 1)) - return 0; + if (file->f_mode & FMODE_READ) poll_wait(file, &s->dma_adc.wait, wait); - } - spin_lock_irqsave(&s->lock, flags); cm_update_ptr(s); if (file->f_mode & FMODE_READ) { @@ -1413,14 +1798,14 @@ VALIDATE_STATE(s); lock_kernel(); if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(s, 1)) != 0) + if ((ret = prog_dmabuf(s, 0)) != 0) goto out; db = &s->dma_dac; } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(s, 0)) != 0) + if ((ret = prog_dmabuf(s, 1)) != 0) goto out; db = &s->dma_adc; - } else + } else goto out; ret = -EINVAL; if (vma->vm_pgoff != 0) @@ -1428,7 +1813,7 @@ size = vma->vm_end - vma->vm_start; if (size > (PAGE_SIZE << db->buforder)) goto out; - ret = -EAGAIN; + ret = -EINVAL; if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) goto out; db->mapped = 1; @@ -1463,13 +1848,15 @@ return 0; case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_BIND, (int *)arg); case SNDCTL_DSP_RESET: if (file->f_mode & FMODE_WRITE) { stop_dac(s); synchronize_irq(); s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + if (s->status & DO_DUAL_DAC) + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; } if (file->f_mode & FMODE_READ) { stop_adc(s); @@ -1479,7 +1866,7 @@ return 0; case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) + if (get_user(val, (int *)arg)) return -EFAULT; if (val >= 0) { if (file->f_mode & FMODE_READ) { @@ -1490,13 +1877,15 @@ if (file->f_mode & FMODE_WRITE) { stop_dac(s); s->dma_dac.ready = 0; + if (s->status & DO_DUAL_DAC) + s->dma_adc.ready = 0; set_dac_rate(s, val); } } return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); - + case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) + if (get_user(val, (int *)arg)) return -EFAULT; fmtd = 0; fmtm = ~0; @@ -1515,12 +1904,19 @@ fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; else fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } } set_fmt(s, fmtm, fmtd); return 0; case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) + if (get_user(val, (int *)arg)) return -EFAULT; if (val != 0) { fmtd = 0; @@ -1540,14 +1936,26 @@ fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; else fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } } set_fmt(s, fmtm, fmtd); - } + if ((s->capability & CAN_MULTI_CH) + && (file->f_mode & FMODE_WRITE)) { + val = set_dac_channels(s, val); + return put_user(val, (int *)arg); + } + } return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg); case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + return put_user(AFMT_S16_LE|AFMT_U8|AFMT_AC3, (int *)arg); case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ if (get_user(val, (int *)arg)) @@ -1566,27 +1974,47 @@ if (file->f_mode & FMODE_WRITE) { stop_dac(s); s->dma_dac.ready = 0; - if (val == AFMT_S16_LE) + if (val == AFMT_S16_LE || val == AFMT_AC3) fmtd |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; else fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_DACSHIFT); + if (val == AFMT_AC3) { + fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + set_ac3(s, s->ratedac); + } else + set_ac3(s, 0); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } } set_fmt(s, fmtm, fmtd); } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) + if (s->status & DO_AC3) return put_user(AFMT_AC3, (int *)arg); + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg); - + case SNDCTL_DSP_POST: return 0; case SNDCTL_DSP_GETTRIGGER: val = 0; - if (file->f_mode & FMODE_READ && s->enable & CM_CENABLE_RE) + if (s->status & DO_DUAL_DAC) { + if (file->f_mode & FMODE_WRITE && + (s->enable & CM_ENABLE_CH1) && + (s->enable & CM_ENABLE_CH0)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + } + if (file->f_mode & FMODE_READ && s->enable & CM_ENABLE_CH0) val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->enable & CM_CENABLE_PE) + if (file->f_mode & FMODE_WRITE && s->enable & CM_ENABLE_CH1) val |= PCM_ENABLE_OUTPUT; return put_user(val, (int *)arg); - + case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) return -EFAULT; @@ -1602,6 +2030,10 @@ if (val & PCM_ENABLE_OUTPUT) { if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) return ret; + if (s->status & DO_DUAL_DAC) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + } start_dac(s); } else stop_dac(s); @@ -1611,22 +2043,22 @@ case SNDCTL_DSP_GETOSPACE: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; + if (!(s->enable & CM_ENABLE_CH1) && (val = prog_dmabuf(s, 0)) != 0) + return val; spin_lock_irqsave(&s->lock, flags); cm_update_ptr(s); abinfo.fragsize = s->dma_dac.fragsize; abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; spin_unlock_irqrestore(&s->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 (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; + if (!(s->enable & CM_ENABLE_CH0) && (val = prog_dmabuf(s, 1)) != 0) + return val; spin_lock_irqsave(&s->lock, flags); cm_update_ptr(s); abinfo.fragsize = s->dma_adc.fragsize; @@ -1643,8 +2075,6 @@ case SNDCTL_DSP_GETODELAY: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; spin_lock_irqsave(&s->lock, flags); cm_update_ptr(s); val = s->dma_dac.count; @@ -1654,8 +2084,6 @@ case SNDCTL_DSP_GETIPTR: if (!(file->f_mode & FMODE_READ)) return -EINVAL; - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) - return ret; spin_lock_irqsave(&s->lock, flags); cm_update_ptr(s); cinfo.bytes = s->dma_adc.total_bytes; @@ -1669,8 +2097,6 @@ case SNDCTL_DSP_GETOPTR: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) - return ret; spin_lock_irqsave(&s->lock, flags); cm_update_ptr(s); cinfo.bytes = s->dma_dac.total_bytes; @@ -1678,6 +2104,10 @@ cinfo.ptr = s->dma_dac.hwptr; if (s->dma_dac.mapped) s->dma_dac.count &= s->dma_dac.fragsize-1; + if (s->status & DO_DUAL_DAC) { + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + } spin_unlock_irqrestore(&s->lock, flags); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); @@ -1685,6 +2115,11 @@ if (file->f_mode & FMODE_WRITE) { if ((val = prog_dmabuf(s, 0))) return val; + if (s->status & DO_DUAL_DAC) { + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(2 * s->dma_dac.fragsize, (int *)arg); + } return put_user(s->dma_dac.fragsize, (int *)arg); } if ((val = prog_dmabuf(s, 1))) @@ -1692,7 +2127,7 @@ return put_user(s->dma_adc.fragsize, (int *)arg); case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) + if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { s->dma_adc.ossfragshift = val & 0xffff; @@ -1713,6 +2148,10 @@ s->dma_dac.ossfragshift = 15; if (s->dma_dac.ossmaxfrags < 4) s->dma_dac.ossmaxfrags = 4; + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ossfragshift = s->dma_dac.ossfragshift; + s->dma_adc.ossmaxfrags = s->dma_dac.ossmaxfrags; + } } return 0; @@ -1720,14 +2159,17 @@ if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) return -EINVAL; - if (get_user(val, (int *)arg)) + if (get_user(val, (int *)arg)) return -EFAULT; if (val != 1 && val != 2 && val != 4) return -EINVAL; if (file->f_mode & FMODE_READ) s->dma_adc.subdivision = val; - if (file->f_mode & FMODE_WRITE) + if (file->f_mode & FMODE_WRITE) { s->dma_dac.subdivision = val; + if (s->status & DO_DUAL_DAC) + s->dma_adc.subdivision = val; + } return 0; case SOUND_PCM_READ_RATE: @@ -1742,7 +2184,63 @@ case SOUND_PCM_READ_FILTER: return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); - case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_GETCHANNELMASK: + return put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE|DSP_BIND_SPDIF, (int *)arg); + + case SNDCTL_DSP_BIND_CHANNEL: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val == DSP_BIND_QUERY) { + val = DSP_BIND_FRONT; + if (s->status & DO_SPDIF_OUT) + val |= DSP_BIND_SPDIF; + else { + if (s->curr_channels == 4) + val |= DSP_BIND_SURR; + if (s->curr_channels > 4) + val |= DSP_BIND_CENTER_LFE; + } + } else { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val & DSP_BIND_SPDIF) { + set_spdifout(s, s->ratedac); + set_dac_channels(s, s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1); + if (!(s->status & DO_SPDIF_OUT)) + val &= ~DSP_BIND_SPDIF; + } else { + int channels; + int mask; + + mask = val & (DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE); + switch (mask) { + case DSP_BIND_FRONT: + channels = 2; + break; + case DSP_BIND_FRONT|DSP_BIND_SURR: + channels = 4; + break; + case DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE: + channels = 6; + break; + default: + channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; + break; + } + set_dac_channels(s, channels); + } + } + } + return put_user(val, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: return -EINVAL; @@ -1788,6 +2286,12 @@ fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; set_dac_rate(s, 8000); + // clear previous multichannel, spdif, ac3 state + set_spdifout(s, 0); + if (s->deviceid == PCI_DEVICE_ID_CMEDIA_CM8738) { + set_ac3(s, 0); + set_dac_channels(s, 1); + } } set_fmt(s, fmtm, fmts); s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); @@ -1806,11 +2310,23 @@ down(&s->open_sem); if (file->f_mode & FMODE_WRITE) { stop_dac(s); +#ifndef FIXEDDMA dealloc_dmabuf(&s->dma_dac); + if (s->status & DO_DUAL_DAC) + dealloc_dmabuf(&s->dma_adc); +#endif + if (s->status & DO_MULTI_CH) + set_dac_channels(s, 0); + if (s->status & DO_AC3) + set_ac3(s, 0); + if (s->status & DO_SPDIF_OUT) + set_spdifout(s, 0); } if (file->f_mode & FMODE_READ) { stop_adc(s); +#ifndef FIXEDDMA dealloc_dmabuf(&s->dma_adc); +#endif } s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); up(&s->open_sem); @@ -1831,6 +2347,7 @@ release: cm_release, }; +#ifdef CONFIG_SOUND_CMPCI_MIDI /* --------------------------------------------------------------------- */ static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) @@ -1847,12 +2364,9 @@ return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; - if (count == 0) - return 0; ret = 0; add_wait_queue(&s->midi.iwait, &wait); while (count > 0) { - set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); ptr = s->midi.ird; cnt = MIDIINBUF - ptr; @@ -1862,14 +2376,15 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) + if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; } + __set_current_state(TASK_INTERRUPTIBLE); schedule(); - if (signal_pending(current)) + if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; @@ -1877,7 +2392,8 @@ } continue; } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + { if (!ret) ret = -EFAULT; break; @@ -1916,7 +2432,6 @@ ret = 0; add_wait_queue(&s->midi.owait, &wait); while (count > 0) { - set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); ptr = s->midi.owr; cnt = MIDIOUTBUF - ptr; @@ -1928,11 +2443,13 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) { + if (file->f_flags & O_NONBLOCK) + { if (!ret) ret = -EAGAIN; break; } + __set_current_state(TASK_INTERRUPTIBLE); schedule(); if (signal_pending(current)) { if (!ret) @@ -1941,7 +2458,8 @@ } continue; } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + { if (!ret) ret = -EFAULT; break; @@ -2017,7 +2535,7 @@ s->midi.ird = s->midi.iwr = s->midi.icnt = 0; s->midi.ord = s->midi.owr = s->midi.ocnt = 0; /* enable MPU-401 */ - outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) | 4, s->iobase + CODEC_CMI_FUNCTRL1); + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 4); outb(0xff, s->iomidi+1); /* reset command */ if (!(inb(s->iomidi+1) & 0x80)) inb(s->iomidi); @@ -2040,23 +2558,24 @@ spin_unlock_irqrestore(&s->lock, flags); s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); up(&s->open_sem); + MOD_INC_USE_COUNT; return 0; } static int cm_midi_release(struct inode *inode, struct file *file) { struct cm_state *s = (struct cm_state *)file->private_data; - DECLARE_WAITQUEUE(wait, current); + DECLARE_WAITQUEUE(wait, current); unsigned long flags; unsigned count, tmo; VALIDATE_STATE(s); - lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&s->midi.owait, &wait); for (;;) { - set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); count = s->midi.ocnt; spin_unlock_irqrestore(&s->lock, flags); @@ -2067,11 +2586,12 @@ if (file->f_flags & O_NONBLOCK) { remove_wait_queue(&s->midi.owait, &wait); set_current_state(TASK_RUNNING); + unlock_kernel(); return -EBUSY; } tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "cmpci: midi timed out??\n"); + printk(KERN_DEBUG "cm: midi timed out??\n"); } remove_wait_queue(&s->midi.owait, &wait); set_current_state(TASK_RUNNING); @@ -2085,7 +2605,7 @@ if (!(inb(s->iomidi+1) & 0x80)) inb(s->iomidi); /* disable MPU-401 */ - outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) & ~4, s->iobase + CODEC_CMI_FUNCTRL1); + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~4, 0); } spin_unlock_irqrestore(&s->lock, flags); up(&s->open_sem); @@ -2103,9 +2623,11 @@ open: cm_midi_open, release: cm_midi_release, }; +#endif /* --------------------------------------------------------------------- */ +#ifdef CONFIG_SOUND_CMPCI_FM static int cm_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { static const unsigned char op_offset[18] = { @@ -2197,10 +2719,8 @@ outb(5, s->iosynth+2); outb(arg & 1, s->iosynth+3); return 0; - - default: - return -EINVAL; } + return -EINVAL; } static int cm_dmfm_open(struct inode *inode, struct file *file) @@ -2236,6 +2756,7 @@ outb(1, s->iosynth+3); /* enable OPL3 */ s->open_mode |= FMODE_DMFM; up(&s->open_sem); + MOD_INC_USE_COUNT; return 0; } @@ -2267,6 +2788,7 @@ open: cm_dmfm_open, release: cm_dmfm_release, }; +#endif /* CONFIG_SOUND_CMPCI_FM */ /* --------------------------------------------------------------------- */ @@ -2285,46 +2807,125 @@ int mixch; int vol; } initvol[] __initdata = { - { SOUND_MIXER_WRITE_CD, 0x4040 }, - { SOUND_MIXER_WRITE_LINE, 0x4040 }, - { SOUND_MIXER_WRITE_MIC, 0x4040 }, - { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, - { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, - { SOUND_MIXER_WRITE_PCM, 0x4040 } + { SOUND_MIXER_WRITE_CD, 0x4f4f }, + { SOUND_MIXER_WRITE_LINE, 0x4f4f }, + { SOUND_MIXER_WRITE_MIC, 0x4f4f }, + { SOUND_MIXER_WRITE_SYNTH, 0x4f4f }, + { SOUND_MIXER_WRITE_VOLUME, 0x4f4f }, + { SOUND_MIXER_WRITE_PCM, 0x4f4f } }; -#ifdef MODULE -static int spdif_loop = 0; -static int four_ch = 0; -static int rear_out = 0; -MODULE_PARM(spdif_loop, "i"); -MODULE_PARM(four_ch, "i"); -MODULE_PARM(rear_out, "i"); +/* check chip version and capability */ +static int query_chip(struct cm_state *s) +{ + int ChipVersion = -1; + unsigned char RegValue; + + // check reg 0Ch, bit 24-31 + RegValue = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 3); + if (RegValue == 0) { + // check reg 08h, bit 24-28 + RegValue = inb(s->iobase + CODEC_CMI_CHFORMAT + 3); + RegValue &= 0x1f; + if (RegValue == 0) { + ChipVersion = 33; + s->max_channels = 4; + s->capability |= CAN_AC3_SW; + s->capability |= CAN_DUAL_DAC; + } else { + ChipVersion = 37; + s->max_channels = 4; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + } + } else { + // check reg 0Ch, bit 26 + if (RegValue & (1 << (26-24))) { + ChipVersion = 39; + if (RegValue & (1 << (24-24))) + s->max_channels = 6; + else + s->max_channels = 4; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + s->capability |= CAN_MULTI_CH_HW; + } else { + ChipVersion = 55; // 4 or 6 channels + s->max_channels = 6; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + s->capability |= CAN_MULTI_CH_HW; + } + } + // still limited to number of speakers + if (s->max_channels > s->speakers) + s->max_channels = s->speakers; + return ChipVersion; +} + +#ifdef CONFIG_SOUND_CMPCI_MIDI +static int mpu_io = CONFIG_SOUND_CMPCI_MPUIO; +#else +static int mpu_io; +#endif +#ifdef CONFIG_SOUND_CMPCI_FM +static int fm_io = CONFIG_SOUND_CMPCI_FMIO; #else +static int fm_io; +#endif +#ifdef CONFIG_SOUND_CMPCI_SPDIFINVERSE +static int spdif_inverse = 1; +#else +static int spdif_inverse; +#endif #ifdef CONFIG_SOUND_CMPCI_SPDIFLOOP -static int spdif_loop = 1; +static int spdif_loop = 1; #else -static int spdif_loop = 0; +static int spdif_loop; #endif -#ifdef CONFIG_SOUND_CMPCI_4CH -static int four_ch = 1; +#ifdef CONFIG_SOUND_CMPCI_SPEAKERS +static int speakers = CONFIG_SOUND_CMPCI_SPEAKERS; #else -static int four_ch = 0; +static int speakers = 2; #endif -#ifdef CONFIG_SOUND_CMPCI_REAR -static int rear_out = 1; +#ifdef CONFIG_SOUND_CMPCI_LINE_REAR +static int use_line_as_rear = 1; #else -static int rear_out = 0; +static int use_line_as_rear; #endif +#ifdef CONFIG_SOUND_CMPCI_LINE_BASS +static int use_line_as_bass = 1; +#else +static int use_line_as_bass; +#endif +#ifdef CONFIG_SOUND_CMPCI_JOYSTICK +static int joystick = 1; +#else +static int joystick; #endif +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(fm_io, "i"); +MODULE_PARM(spdif_inverse, "i"); +MODULE_PARM(spdif_loop, "i"); +MODULE_PARM(speakers, "i"); +MODULE_PARM(use_line_as_rear, "i"); +MODULE_PARM(use_line_as_bass, "i"); +MODULE_PARM(joystick, "i"); +MODULE_PARM_DESC(mpu_io, "(0x330, 0x320, 0x310, 0x300) Base of MPU-401, 0 to disable"); +MODULE_PARM_DESC(fm_io, "(0x388, 0x3C8, 0x3E0) Base of OPL3, 0 to disable"); +MODULE_PARM_DESC(spdif_inverse, "(1/0) Invert S/PDIF-in signal"); +MODULE_PARM_DESC(spdif_loop, "(1/0) Route S/PDIF-in to S/PDIF-out directly"); +MODULE_PARM_DESC(speakers, "(2-6) Number of speakers you connect"); +MODULE_PARM_DESC(use_line_as_rear, "(1/0) Use line-in jack as rear-out"); +MODULE_PARM_DESC(use_line_as_bass, "(1/0) Use line-in jack as bass/center"); +MODULE_PARM_DESC(joystick, "(1/0) Enable joystick interface, still need joystick driver"); -static int __init init_cmpci(void) +void initialize_chip(struct pci_dev *pcidev) { struct cm_state *s; - struct pci_dev *pcidev = NULL; mm_segment_t fs; - int i, val, index = 0; - + int i, val; + unsigned char reg_mask = 0; struct { unsigned short deviceid; char *devicename; @@ -2333,33 +2934,21 @@ { PCI_DEVICE_ID_CMEDIA_CM8338A, "CM8338A" }, { PCI_DEVICE_ID_CMEDIA_CM8338B, "CM8338B" }, { PCI_DEVICE_ID_CMEDIA_CM8738, "CM8738" }, + { PCI_DEVICE_ID_CMEDIA_CM8738B, "CM8738B" }, }; char *devicename = "unknown"; - -#ifdef CONFIG_PCI - if (!pci_present()) /* No PCI bus in this machine! */ -#endif - return -ENODEV; - printk(KERN_INFO "cmpci: version v2.41-nomodem time " __TIME__ " " __DATE__ "\n"); -#if 0 - if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) - printk(KERN_INFO "cmpci: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); -#endif - while (index < NR_DEVICE && pcidev == NULL && ( - (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, pcidev)) || - (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, pcidev)) || - (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, pcidev)))) { + { if (pci_enable_device(pcidev)) - continue; + return; if (pcidev->irq == 0) - continue; - if (!(s = kmalloc(sizeof(struct cm_state), GFP_KERNEL))) { - printk(KERN_WARNING "cmpci: out of memory\n"); - continue; + return; + s = kmalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + printk(KERN_WARNING "cm: out of memory\n"); + return; } /* search device name */ - for (i = 0; i < sizeof(devicetable) / sizeof(devicetable[0]); i++) - { + for (i = 0; i < sizeof(devicetable) / sizeof(devicetable[0]); i++) { if (devicetable[i].deviceid == pcidev->device) { devicename = devicetable[i].devicename; @@ -2376,35 +2965,96 @@ spin_lock_init(&s->lock); s->magic = CM_MAGIC; s->iobase = pci_resource_start(pcidev, 0); - s->iosynth = 0x388; - s->iomidi = 0x330; - spin_lock_init(&s->lock); + s->iosynth = fm_io; + s->iomidi = mpu_io; + s->status = 0; + /* range check */ + if (speakers < 2) + speakers = 2; + else if (speakers > 6) + speakers = 6; + s->speakers = speakers; if (s->iobase == 0) - continue; + return; s->irq = pcidev->irq; if (!request_region(s->iobase, CM_EXTENT_CODEC, "cmpci")) { - printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1); + printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1); goto err_region5; } - if (!request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi")) { - printk(KERN_WARNING "cmpci: io ports %#x-%#x in use, midi disabled.\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1); +#ifdef CONFIG_SOUND_CMPCI_MIDI + /* disable MPU-401 */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x04, 0); + if (s->iomidi) { + if (!request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi")) { + printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1); s->iomidi = 0; + } else { + /* set IO based at 0x330 */ + switch (s->iomidi) { + case 0x330: + reg_mask = 0; + break; + case 0x320: + reg_mask = 0x20; + break; + case 0x310: + reg_mask = 0x40; + break; + case 0x300: + reg_mask = 0x60; + break; + default: + s->iomidi = 0; + break; + } + outb((inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60) | reg_mask, s->iobase + CODEC_CMI_LEGACY_CTRL + 3); + /* enable MPU-401 */ + if (s->iomidi) { + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); + } + } } - else - { - /* set IO based at 0x330 */ - outb(inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60, s->iobase + CODEC_CMI_LEGACY_CTRL + 3); - } - if (!request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM")) { - printk(KERN_WARNING "cmpci: io ports %#x-%#x in use, synth disabled.\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1); +#endif +#ifdef CONFIG_SOUND_CMPCI_FM + /* disable FM */ + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0); + if (s->iosynth) { + if (!request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM")) { + printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1); s->iosynth = 0; + } else { + /* set IO based at 0x388 */ + switch (s->iosynth) { + case 0x388: + reg_mask = 0; + break; + case 0x3C8: + reg_mask = 0x01; + break; + case 0x3E0: + reg_mask = 0x02; + break; + case 0x3E8: + reg_mask = 0x03; + break; + default: + s->iosynth = 0; + break; + } + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x03, reg_mask); + /* enable FM */ + if (s->iosynth) { + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 8); + } + } } +#endif + /* enable joystick */ + if (joystick) + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x02); else - { - /* enable FM */ - outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 2) | 8, s->iobase + CODEC_CMI_MISC_CTRL); - } + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x02, 0); /* initialize codec registers */ outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ @@ -2413,21 +3063,25 @@ /* request irq */ if (request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s)) { - printk(KERN_ERR "cmpci: irq %u in use\n", s->irq); + printk(KERN_ERR "cm: irq %u in use\n", s->irq); goto err_irq; } - printk(KERN_INFO "cmpci: found %s adapter at io %#06x irq %u\n", + printk(KERN_INFO "cm: found %s adapter at io %#06x irq %u\n", devicename, s->iobase, s->irq); /* register devices */ if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0) goto err_dev1; if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0) goto err_dev2; - if (s->iomidi && (s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0) +#ifdef CONFIG_SOUND_CMPCI_MIDI + if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0) goto err_dev3; - if (s->iosynth && (s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0) +#endif +#ifdef CONFIG_SOUND_CMPCI_FM + if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0) goto err_dev4; - pci_set_master(pcidev); +#endif + pci_set_master(pcidev); /* enable bus mastering */ /* initialize the chips */ fs = get_fs(); set_fs(KERNEL_DS); @@ -2440,59 +3094,78 @@ val = initvol[i].vol; mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); } - set_fs(fs); - if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738) - { + /* use channel 0 for record, channel 1 for play */ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 1); + s->deviceid = pcidev->device; + if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738) { + /* chip version and hw capability check */ + s->chip_version = query_chip(s); + printk(KERN_INFO "chip version = 0%d\n", s->chip_version); + /* seet SPDIF-in inverse before enable SPDIF loop */ + if (spdif_inverse) { + /* turn on spdif-in inverse */ + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 1); + printk(KERN_INFO "cm: Inverse SPDIF-in\n"); + } else { + /* turn off spdif-ininverse */ + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~1, 0); + } + /* enable SPDIF loop */ - if (spdif_loop) - { + if (spdif_loop) { + s->status |= DO_SPDIF_LOOP; /* turn on spdif-in to spdif-out */ - outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) | 0x80, s->iobase + CODEC_CMI_FUNCTRL1); - printk(KERN_INFO "cmpci: Enable SPDIF loop\n"); + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x80); + printk(KERN_INFO "cm: Enable SPDIF loop\n"); + } else { + s->status &= ~DO_SPDIF_LOOP; + /* turn off spdif-in to spdif-out */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x80, 0); } - else - outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) & ~0x80, s->iobase + CODEC_CMI_FUNCTRL1); - /* enable 4 channels mode */ - if (four_ch) - { - /* 4 channel mode (analog duplicate) */ - outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 3) | 0x04, s->iobase + CODEC_CMI_MISC_CTRL + 3); - printk(KERN_INFO "cmpci: Enable 4 channels mode\n"); - /* has separate rear-out jack ? */ - if (rear_out) - { - /* has separate rear out jack */ - outb(inb(s->iobase + CODEC_CMI_MIXER1) & ~0x20, s->iobase + CODEC_CMI_MIXER1); - } - else - { - outb(inb(s->iobase + CODEC_CMI_MIXER1) | 0x20, s->iobase + CODEC_CMI_MIXER1); - printk(KERN_INFO "cmpci: line-in routed as rear-out\n"); - } + if (use_line_as_rear) { + s->capability |= CAN_LINE_AS_REAR; + s->status |= DO_LINE_AS_REAR; + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, 0x20); + } else + maskb(s->iobase + CODEC_CMI_MIXER1, ~0x20, 0); + if (s->chip_version >= 39) { + if (use_line_as_bass) { + s->capability |= CAN_LINE_AS_BASS; + s->status |= DO_LINE_AS_BASS; + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0, 0x60); + } else + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x60, 0); } - else - outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 3) & ~0x04, s->iobase + CODEC_CMI_MISC_CTRL + 3); + } else { + /* 8338 will fall here */ + s->max_channels = 2; } /* queue it for later freeing */ s->next = devs; devs = s; - index++; - continue; + return; +#ifdef CONFIG_SOUND_CMPCI_FM + unregister_sound_special(s->dev_dmfm); err_dev4: +#endif +#ifdef CONFIG_SOUND_CMPCI_MIDI unregister_sound_midi(s->dev_midi); err_dev3: +#endif unregister_sound_mixer(s->dev_mixer); err_dev2: unregister_sound_dsp(s->dev_audio); err_dev1: - printk(KERN_ERR "cmpci: cannot register misc device\n"); + printk(KERN_ERR "cm: cannot register misc device\n"); free_irq(s->irq, s); err_irq: - if(s->iosynth) - release_region(s->iosynth, CM_EXTENT_SYNTH); - if(s->iomidi) - release_region(s->iomidi, CM_EXTENT_MIDI); +#ifdef CONFIG_SOUND_CMPCI_FM + if (s->iosynth) release_region(s->iosynth, CM_EXTENT_SYNTH); +#endif +#ifdef CONFIG_SOUND_CMPCI_MIDI + if (s->iomidi) release_region(s->iomidi, CM_EXTENT_MIDI); +#endif release_region(s->iobase, CM_EXTENT_CODEC); err_region5: kfree(s); @@ -2500,15 +3173,47 @@ if (!devs) { if (wavetable_mem) free_pages(wavetable_mem, 20-PAGE_SHIFT); + return; + } + return; +} + +static int __init init_cmpci(void) +{ + struct pci_dev *pcidev = NULL; + int index = 0; + +#ifdef CONFIG_PCI + if (!pci_present()) /* No PCI bus in this machine! */ +#endif return -ENODEV; + printk(KERN_INFO "cm: version $Revision: 5.64 $ time " __TIME__ " " __DATE__ "\n"); +#if 0 + if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) + printk(KERN_INFO "cm: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); +#endif + while (index < NR_DEVICE && ( + (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, pcidev)))) { + initialize_chip(pcidev); + index++; + } + while (index < NR_DEVICE && ( + (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, pcidev)))) { + initialize_chip(pcidev); + index++; + } + while (index < NR_DEVICE && ( + (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, pcidev)))) { + initialize_chip(pcidev); + index++; } return 0; } /* --------------------------------------------------------------------- */ -MODULE_AUTHOR("ChenLi Tien, cltien@home.com"); -MODULE_DESCRIPTION("CMPCI Audio Driver"); +MODULE_AUTHOR("ChenLi Tien, cltien@cmedia.com.tw"); +MODULE_DESCRIPTION("CM8x38 Audio Driver"); static void __exit cleanup_cmpci(void) { @@ -2520,28 +3225,34 @@ synchronize_irq(); outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ free_irq(s->irq, s); +#ifdef FIXEDDMA + dealloc_dmabuf(&s->dma_dac); + dealloc_dmabuf(&s->dma_adc); +#endif /* reset mixer */ wrmixer(s, DSP_MIX_DATARESETIDX, 0); release_region(s->iobase, CM_EXTENT_CODEC); - if(s->iomidi) - { - release_region(s->iomidi, CM_EXTENT_MIDI); - unregister_sound_midi(s->dev_midi); - } - if(s->iosynth) - { - release_region(s->iosynth, CM_EXTENT_SYNTH); - unregister_sound_special(s->dev_dmfm); - } +#ifdef CONFIG_SOUND_CMPCI_MIDI + if (s->iomidi) release_region(s->iomidi, CM_EXTENT_MIDI); +#endif +#ifdef CONFIG_SOUND_CMPCI_FM + if (s->iosynth) release_region(s->iosynth, CM_EXTENT_SYNTH); +#endif unregister_sound_dsp(s->dev_audio); unregister_sound_mixer(s->dev_mixer); +#ifdef CONFIG_SOUND_CMPCI_MIDI + unregister_sound_midi(s->dev_midi); +#endif +#ifdef CONFIG_SOUND_CMPCI_FM + unregister_sound_special(s->dev_dmfm); +#endif kfree(s); } if (wavetable_mem) free_pages(wavetable_mem, 20-PAGE_SHIFT); - printk(KERN_INFO "cmpci: unloading\n"); + printk(KERN_INFO "cm: unloading\n"); } module_init(init_cmpci); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/cs4232.c linux.ac/drivers/sound/cs4232.c --- linux.vanilla/drivers/sound/cs4232.c Sun Nov 12 02:33:13 2000 +++ linux.ac/drivers/sound/cs4232.c Tue Apr 3 17:55:05 2001 @@ -338,7 +338,7 @@ { #ifdef CONFIG_SOUND_WAVEFRONT_MODULE if(synthio == -1) - printk(KERN_WARNING "cs4232: set synthio and synthirq to use the wavefront facilities.\n"); + printk(KERN_INFO "cs4232: set synthio and synthirq to use the wavefront facilities.\n"); else { synth_base = synthio; synth_irq = synthirq; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/cs4281/cs4281m.c linux.ac/drivers/sound/cs4281/cs4281m.c --- linux.vanilla/drivers/sound/cs4281/cs4281m.c Sat May 26 16:53:13 2001 +++ linux.ac/drivers/sound/cs4281/cs4281m.c Thu May 24 23:14:22 2001 @@ -4463,9 +4463,9 @@ unregister_sound_midi(s->dev_midi); iounmap(s->pBA1); iounmap(s->pBA0); - kfree(s); pci_set_drvdata(pci_dev,NULL); list_del(&s->list); + kfree(s); CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO "cs4281: cs4281_remove()-: remove successful\n")); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/esssolo1.c linux.ac/drivers/sound/esssolo1.c --- linux.vanilla/drivers/sound/esssolo1.c Sat May 26 16:53:13 2001 +++ linux.ac/drivers/sound/esssolo1.c Wed May 23 23:41:30 2001 @@ -79,6 +79,9 @@ * for abs. Bug report by Andrew Morton * 15.05.2001 pci_enable_device moved, return values in probe cleaned * up. Marcus Meissner + * 22.05.2001 0.19 more cleanups, changed PM to PCI 2.4 style, got rid + * of global list of devices, using pci device data. + * Marcus Meissner */ /*****************************************************************************/ @@ -94,7 +97,6 @@ #include #include #include -#include #include #include #include @@ -161,15 +163,14 @@ #define FMODE_DMFM 0x10 +static struct pci_driver solo1_driver; + /* --------------------------------------------------------------------- */ struct solo1_state { /* magic */ unsigned int magic; - /* list of esssolo1 devices */ - struct list_head devs; - /* the corresponding pci_dev structure */ struct pci_dev *dev; @@ -244,10 +245,6 @@ /* --------------------------------------------------------------------- */ -static LIST_HEAD(devs); - -/* --------------------------------------------------------------------- */ - extern inline void write_seq(struct solo1_state *s, unsigned char data) { int i; @@ -939,16 +936,22 @@ static int solo1_open_mixdev(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); - struct list_head *list; - struct solo1_state *s; + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct solo1_state, devs); + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; + drvr = pci_dev_driver (pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; if (s->dev_mixer == minor) break; } + if (!s) + return -ENODEV; VALIDATE_STATE(s); file->private_data = s; return 0; @@ -1611,16 +1614,23 @@ { int minor = MINOR(inode->i_rdev); DECLARE_WAITQUEUE(wait, current); - struct list_head *list; - struct solo1_state *s; + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct solo1_state, devs); + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; + + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; if (!((s->dev_audio ^ minor) & ~0xf)) break; } + if (!s) + return -ENODEV; VALIDATE_STATE(s); file->private_data = s; /* wait for device to become free */ @@ -1894,16 +1904,23 @@ int minor = MINOR(inode->i_rdev); DECLARE_WAITQUEUE(wait, current); unsigned long flags; - struct list_head *list; - struct solo1_state *s; + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; + + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct solo1_state, devs); + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; if (s->dev_midi == minor) break; } + if (!s) + return -ENODEV; VALIDATE_STATE(s); file->private_data = s; /* wait for device to become free */ @@ -2112,16 +2129,23 @@ { int minor = MINOR(inode->i_rdev); DECLARE_WAITQUEUE(wait, current); - struct list_head *list; - struct solo1_state *s; + struct solo1_state *s = NULL; + struct pci_dev *pci_dev; + + pci_for_each_dev(pci_dev) { + struct pci_driver *drvr; - for (list = devs.next; ; list = list->next) { - if (list == &devs) - return -ENODEV; - s = list_entry(list, struct solo1_state, devs); + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; if (s->dev_dmfm == minor) break; } + if (!s) + return -ENODEV; VALIDATE_STATE(s); file->private_data = s; /* wait for device to become free */ @@ -2256,33 +2280,31 @@ return 0; } -static int solo1_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - struct solo1_state *s = (struct solo1_state*) dev->data; - if (s) { - switch(rqst) { - case PM_RESUME: - setup_solo1(s); - break; +static void +solo1_suspend(struct pci_dev *pci_dev) { + struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + return; + outb(0, s->iobase+6); + /* DMA master clear */ + outb(0, s->ddmabase+0xd); + /* reset sequencer and FIFO */ + outb(3, s->sbbase+6); + /* turn off DDMA controller address space */ + pci_write_config_word(s->dev, 0x60, 0); +} - case PM_SUSPEND: - outb(0, s->iobase+6); - /* DMA master clear */ - outb(0, s->ddmabase+0xd); - /* reset sequencer and FIFO */ - outb(3, s->sbbase+6); - /* turn off DDMA controller address space */ - pci_write_config_word(s->dev, 0x60, 0); - break; - } - } - return 0; +static void +solo1_resume(struct pci_dev *pci_dev) { + struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + return; + setup_solo1(s); } static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) { struct solo1_state *s; - struct pm_dev *pmdev; int ret; if ((ret=pci_enable_device(pcidev))) @@ -2379,13 +2401,6 @@ gameport_register_port(&s->gameport); /* store it in the driver field */ pci_set_drvdata(pcidev, s); - /* put it into driver list */ - list_add_tail(&s->devs, &devs); - - pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), solo1_pm_callback); - if (pmdev) - pmdev->data = s; - return 0; err: @@ -2420,7 +2435,6 @@ if (!s) return; - list_del(&s->devs); /* stop DMA controller */ outb(0, s->iobase+6); outb(0, s->ddmabase+0xd); /* DMA master clear */ @@ -2455,7 +2469,9 @@ name: "ESS Solo1", id_table: id_table, probe: solo1_probe, - remove: solo1_remove + remove: solo1_remove, + suspend: solo1_suspend, + resume: solo1_resume }; @@ -2463,7 +2479,7 @@ { if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "solo1: version v0.18 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "solo1: version v0.19 time " __TIME__ " " __DATE__ "\n"); if (!pci_register_driver(&solo1_driver)) { pci_unregister_driver(&solo1_driver); return -ENODEV; @@ -2480,7 +2496,6 @@ { printk(KERN_INFO "solo1: unloading\n"); pci_unregister_driver(&solo1_driver); - pm_unregister_all(solo1_pm_callback); } /* --------------------------------------------------------------------- */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/maestro.c linux.ac/drivers/sound/maestro.c --- linux.vanilla/drivers/sound/maestro.c Tue Apr 3 17:32:22 2001 +++ linux.ac/drivers/sound/maestro.c Thu May 24 23:56:24 2001 @@ -115,6 +115,12 @@ * themselves, but we'll see. * * History + * v0.15 - May 21 2001 - Marcus Meissner + * Ported to Linux 2.4 PCI API. Some clean ups, global devs list + * removed (now using pci device driver data). + * PM needs to be polished still. Bumped version. + * (still kind of v0.14) May 13 2001 - Ben Pfaff + * Add support for 978 docking and basic hardware volume control * (still kind of v0.14) Nov 23 - Alan Cox * Add clocking= for people with seriously warped hardware * (still v0.14) Nov 10 2000 - Bartlomiej Zolnierkiewicz @@ -189,7 +195,7 @@ * fix bob frequency * endianness * do smart things with ac97 2.0 bits. - * docking and dual codecs and 978? + * dual codecs * leave 54->61 open * * it also would be fun to have a mode that would not use pci dma at all @@ -204,28 +210,6 @@ #include #include #include - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) - - #define DECLARE_WAITQUEUE(QUEUE,INIT) struct wait_queue QUEUE = {INIT, NULL} - #define wait_queue_head_t struct wait_queue * - #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->base_address[0] & PCI_BASE_ADDRESS_IO_MASK) - #define SILLY_INIT_SEM(SEM) SEM=MUTEX; - #define init_waitqueue_head init_waitqueue - #define SILLY_MAKE_INIT(FUNC) __initfunc(FUNC) - #define SILLY_OFFSET(VMA) ((VMA)->vm_offset) - - -#else - - #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->resource[0].start) - #define SILLY_INIT_SEM(SEM) init_MUTEX(&SEM) - #define SILLY_MAKE_INIT(FUNC) __init FUNC - #define SILLY_OFFSET(VMA) ((VMA)->vm_pgoff) - - -#endif - #include #include #include @@ -249,6 +233,8 @@ #include "maestro.h" +static struct pci_driver maestro_pci_driver; + /* --------------------------------------------------------------------- */ #define M_DEBUG 1 @@ -269,8 +255,17 @@ static int clocking=48000; +MODULE_AUTHOR("Zach Brown , Alan Cox "); +MODULE_DESCRIPTION("ESS Maestro Driver"); +#ifdef M_DEBUG +MODULE_PARM(debug,"i"); +#endif +MODULE_PARM(dsps_order,"i"); +MODULE_PARM(use_pm,"i"); +MODULE_PARM(clocking, "i"); + /* --------------------------------------------------------------------- */ -#define DRIVER_VERSION "0.14" +#define DRIVER_VERSION "0.15" #ifndef PCI_VENDOR_ESS #define PCI_VENDOR_ESS 0x125D @@ -352,6 +347,11 @@ [ACPI_D3] = ACPI_NONE }; +static char version[] __devinitdata = +KERN_INFO "maestro: version " DRIVER_VERSION " time " __TIME__ " " __DATE__ "\n"; + + + static const unsigned sample_size[] = { 1, 2, 2, 4 }; static const unsigned sample_shift[] = { 0, 1, 1, 2 }; @@ -483,8 +483,12 @@ int bob_freq; char dsps_open; + + int dock_mute_vol; }; +static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ); + static unsigned ld2(unsigned int x) { @@ -516,8 +520,6 @@ static void check_suspend(struct ess_card *card); -static struct ess_card *devs = NULL; - /* --------------------------------------------------------------------- */ @@ -983,6 +985,17 @@ outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68); outw(0x0209, ioaddr + 0x60); } + + /* Turn on the 978 docking chip. + First frob the "master output enable" bit, + then set most of the playback volume control registers to max. */ + outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); + outb(0xff, ioaddr+0xc3); + outb(0xff, ioaddr+0xc4); + outb(0xff, ioaddr+0xc6); + outb(0xff, ioaddr+0xc8); + outb(0x3f, ioaddr+0xcf); + outb(0x3f, ioaddr+0xd0); } /* * Indirect register access. Not all registers are readable so we @@ -1895,19 +1908,52 @@ outw(inw(c->iobase+4)&1, c->iobase+4); /* M_printk("maestro int: %x\n",event);*/ - if(event&(1<<6)) { - /* XXX if we have a hw volume control int enable - all the ints? doesn't make sense.. */ - event = inw(c->iobase+0x18); - outb(0xFF, c->iobase+0x1A); - } - else - { - /* else ack 'em all, i imagine */ - outb(0xFF, c->iobase+0x1A); + int x; + enum {UP_EVT, DOWN_EVT, MUTE_EVT} vol_evt; + int volume; + + /* Figure out which volume control button was pushed, + based on differences from the default register + values. */ + x = inb(c->iobase+0x1c); + if (x&1) vol_evt = MUTE_EVT; + else if (((x>>1)&7) > 4) vol_evt = UP_EVT; + else vol_evt = DOWN_EVT; + + /* Reset the volume control registers. */ + outb(0x88, c->iobase+0x1c); + outb(0x88, c->iobase+0x1d); + outb(0x88, c->iobase+0x1e); + outb(0x88, c->iobase+0x1f); + + /* Deal with the button press in a hammer-handed + manner by adjusting the master mixer volume. */ + volume = c->mix.mixer_state[0] & 0xff; + if (vol_evt == UP_EVT) { + volume += 10; + if (volume > 100) + volume = 100; + } + else if (vol_evt == DOWN_EVT) { + volume -= 10; + if (volume < 0) + volume = 0; + } else { + /* vol_evt == MUTE_EVT */ + if (volume == 0) + volume = c->dock_mute_vol; + else { + c->dock_mute_vol = volume; + volume = 0; + } + } + set_mixer (c, 0, (volume << 8) | volume); } + + /* Ack all the interrupts. */ + outb(0xFF, c->iobase+0x1A); /* * Update the pointers for all APU's we are running. @@ -2083,17 +2129,25 @@ } /* --------------------------------------------------------------------- */ - static int ess_open_mixdev(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); - struct ess_card *card = devs; - - while (card && card->dev_mixer != minor) - card = card->next; + struct ess_card *card = NULL; + struct pci_dev *pdev; + struct pci_driver *drvr; + + pci_for_each_dev(pdev) { + drvr = pci_dev_driver (pdev); + if (drvr == &maestro_pci_driver) { + card = (struct ess_card*)pci_get_drvdata (pdev); + if (!card) + continue; + if (card->dev_mixer == minor) + break; + } + } if (!card) return -ENODEV; - file->private_data = card; return 0; } @@ -2455,7 +2509,7 @@ #endif goto out; ret = -EINVAL; - if (SILLY_OFFSET(vma) != 0) + if (vma->vm_pgoff != 0) goto out; size = vma->vm_end - vma->vm_start; if (size > (PAGE_SIZE << db->buforder)) @@ -2919,33 +2973,40 @@ ess_open(struct inode *inode, struct file *file) { int minor = MINOR(inode->i_rdev); - struct ess_card *c = devs; - struct ess_state *s = NULL, *sp; - int i; + struct ess_state *s = NULL; unsigned char fmtm = ~0, fmts = 0; - + struct pci_dev *pdev; /* * Scan the cards and find the channel. We only * do this at open time so it is ok */ - - while (c!=NULL) - { - for(i=0;ichannels[i]; - if(sp->dev_audio < 0) - continue; - if((sp->dev_audio ^ minor) & ~0xf) + + pci_for_each_dev(pdev) { + struct ess_card *c; + struct pci_driver *drvr; + + drvr = pci_dev_driver (pdev); + if (drvr == &maestro_pci_driver) { + int i; + struct ess_state *sp; + + c = (struct ess_card*)pci_get_drvdata (pdev); + if (!c) continue; - s=sp; + for(i=0;ichannels[i]; + if(sp->dev_audio < 0) + continue; + if((sp->dev_audio ^ minor) & ~0xf) + continue; + s=sp; + } } - c=c->next; } - if (!s) return -ENODEV; - + VALIDATE_STATE(s); file->private_data = s; /* wait for device to become free */ @@ -3087,8 +3148,8 @@ /* XXX how do we know which to use? */ w&=~(1<<14); /* External clock */ - w&=~(1<<7); /* HWV off */ - w&=~(1<<6); /* Debounce off */ + w|= (1<<7); /* Hardware volume control on */ + w|= (1<<6); /* Debounce off: easier to push the HWV buttons. */ w&=~(1<<5); /* GPIO 4:5 */ w|= (1<<4); /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ w&=~(1<<2); /* MIDI fix off (undoc) */ @@ -3106,6 +3167,12 @@ pci_write_config_word(pcidev, 0x40, w); + /* Set up 978 docking control chip. */ + pci_read_config_word(pcidev, 0x58, &w); + w|=1<<2; /* Enable 978. */ + w|=1<<3; /* Turn on 978 hardware volume control. */ + w&=~(1<<11); /* Turn on 978 mixer volume control. */ + pci_write_config_word(pcidev, 0x58, w); sound_reset(iobase); @@ -3170,7 +3237,7 @@ outw(w, iobase+0x18); w=inw(iobase+0x18); - w&=~(1<<6); /* Harpo off */ + w&=~(1<<6); /* Hardware volume control interrupt off... for now. */ outw(w, iobase+0x18); w=inw(iobase+0x18); @@ -3193,6 +3260,13 @@ w|=(1<<0); /* SB IRQ on */ outw(w, iobase+0x18); + /* Set hardware volume control registers to midpoints. + We can tell which button was pushed based on how they change. */ + outb(0x88, iobase+0x1c); + outb(0x88, iobase+0x1d); + outb(0x88, iobase+0x1e); + outb(0x88, iobase+0x1f); + /* it appears some maestros (dell 7500) only work if these are set, regardless of wether we use the assp or not. */ @@ -3308,32 +3382,44 @@ } static int __init -maestro_install(struct pci_dev *pcidev, int card_type) +maestro_probe(struct pci_dev *pcidev,const struct pci_device_id *pdid) { + int card_type = pdid->driver_data; u32 n; int iobase; - int i; + int i, ret; struct ess_card *card; struct ess_state *ess; struct pm_dev *pmdev; int num = 0; +/* when built into the kernel, we only print version if device is found */ +#ifndef MODULE + static int printed_version; + if (!printed_version++) + printk(version); +#endif + /* don't pick up weird modem maestros */ if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) - return 0; + return -ENODEV; + + + if ((ret=pci_enable_device(pcidev))) + return ret; - iobase = SILLY_PCI_BASE_ADDRESS(pcidev); + iobase = pci_resource_start(pcidev,0); + if (!iobase || !(pci_resource_flags(pcidev, 0 ) & IORESOURCE_IO)) + return -ENODEV; + + if(pcidev->irq == 0) + return -ENODEV; /* stake our claim on the iospace */ if( request_region(iobase, 256, card_names[card_type]) == NULL ) { printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase); - return 0; - } - - /* this was tripping up some machines */ - if(pcidev->irq == 0) { - printk(KERN_WARNING "maestro: pci subsystem reports irq 0, this might not be correct.\n"); + return -EBUSY; } /* just to be sure */ @@ -3343,7 +3429,8 @@ if(card == NULL) { printk(KERN_WARNING "maestro: out of memory\n"); - return 0; + release_region(iobase, 256); + return -ENOMEM; } memset(card, 0, sizeof(*card)); @@ -3354,18 +3441,14 @@ if (pmdev) pmdev->data = card; - if (register_reboot_notifier(&maestro_nb)) { - printk(KERN_WARNING "maestro: reboot notifier registration failed; may not reboot properly.\n"); - } - card->iobase = iobase; card->card_type = card_type; card->irq = pcidev->irq; - card->next = devs; card->magic = ESS_CARD_MAGIC; spin_lock_init(&card->lock); init_waitqueue_head(&card->suspend_queue); - devs = card; + + card->dock_mute_vol = 50; /* init our groups of 6 apus */ for(i=0;idma_dac.wait); init_waitqueue_head(&s->open_wait); spin_lock_init(&s->lock); - SILLY_INIT_SEM(s->open_sem); + init_MUTEX(&s->open_sem); s->magic = ESS_STATE_MAGIC; s->apu[0] = 6*i; @@ -3407,19 +3490,6 @@ ess = &card->channels[0]; - if (pci_enable_device(pcidev)) { - printk (KERN_ERR "maestro: pci_enable_device() failed\n"); - for (i = 0; i < NR_DSPS; i++) { - struct ess_state *s = &card->channels[i]; - if (s->dev_audio != -1) - unregister_sound_dsp(s->dev_audio); - } - release_region(card->iobase, 256); - unregister_reboot_notifier(&maestro_nb); - kfree(card); - return 0; - } - /* * Ok card ready. Begin setup proper */ @@ -3469,7 +3539,7 @@ mixer_push_state(card); } - if(request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card)) + if((ret=request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card))) { printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq); unregister_sound_mixer(card->dev_mixer); @@ -3482,26 +3552,69 @@ release_region(card->iobase, 256); unregister_reboot_notifier(&maestro_nb); kfree(card); - return 0; + return ret; } + + /* Turn on hardware volume control interrupt. + This has to come after we grab the IRQ above, + or a crash will result on installation if a button has been pressed, + because in that case we'll get an immediate interrupt. */ + n = inw(iobase+0x18); + n|=(1<<6); + outw(n, iobase+0x18); + + pci_set_drvdata(pcidev,card); /* now go to sleep 'till something interesting happens */ maestro_power(card,ACPI_D2); printk(KERN_INFO "maestro: %d channels configured.\n", num); - return 1; + return 0; } -int __init init_maestro(void) -{ - struct pci_dev *pcidev = NULL; - int foundone = 0; - - if (!pci_present()) /* No PCI bus in this machine! */ - return -ENODEV; - printk(KERN_INFO "maestro: version " DRIVER_VERSION " time " __TIME__ " " __DATE__ "\n"); +static void maestro_remove(struct pci_dev *pcidev) { + struct ess_card *card = pci_get_drvdata(pcidev); + int i; + + /* XXX maybe should force stop bob, but should be all + stopped by _release by now */ + free_irq(card->irq, card); + unregister_sound_mixer(card->dev_mixer); + for(i=0;ichannels[i]; + if(ess->dev_audio != -1) + unregister_sound_dsp(ess->dev_audio); + } + /* Goodbye, Mr. Bond. */ + maestro_power(card,ACPI_D3); + release_region(card->iobase, 256); + kfree(card); + pci_set_drvdata(pcidev,NULL); +} + +static struct pci_device_id maestro_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1968, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2}, + {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1978, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2E}, + {PCI_VENDOR_ESS_OLD, PCI_DEVICE_ID_ESS_ESS0100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, maestro_pci_tbl); - pcidev = NULL; +static struct pci_driver maestro_pci_driver = { + name:"maestro", + id_table:maestro_pci_tbl, + probe:maestro_probe, + remove:maestro_remove, +}; +int __init init_maestro(void) +{ + pci_module_init(&maestro_pci_driver); + if (register_reboot_notifier(&maestro_nb)) + printk(KERN_WARNING "maestro: reboot notifier registration failed; may not reboot properly.\n"); +#ifdef MODULE + printk(version); +#endif if (dsps_order < 0) { dsps_order = 1; printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); @@ -3510,97 +3623,29 @@ dsps_order = MAX_DSP_ORDER; printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); } - - /* - * Find the ESS Maestro 2. - */ - - while( (pcidev = pci_find_device(PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1968, pcidev))!=NULL ) { - if (maestro_install(pcidev, TYPE_MAESTRO2)) - foundone=1; - } - - /* - * Find the ESS Maestro 2E - */ - - while( (pcidev = pci_find_device(PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1978, pcidev))!=NULL) { - if (maestro_install(pcidev, TYPE_MAESTRO2E)) - foundone=1; - } - - /* - * ESS Maestro 1 - */ - - while((pcidev = pci_find_device(PCI_VENDOR_ESS_OLD, PCI_DEVICE_ID_ESS_ESS0100, pcidev))!=NULL) { - if (maestro_install(pcidev, TYPE_MAESTRO)) - foundone=1; - } - if( ! foundone ) { - printk("maestro: no devices found.\n"); - return -ENODEV; - } return 0; } -static void nuke_maestros(void) -{ - struct ess_card *card; - - /* we do these unconditionally, which is probably wrong */ - pm_unregister_all(maestro_pm_callback); - unregister_reboot_notifier(&maestro_nb); - - while ((card = devs)) { - int i; - devs = devs->next; - - /* XXX maybe should force stop bob, but should be all - stopped by _release by now */ - free_irq(card->irq, card); - unregister_sound_mixer(card->dev_mixer); - for(i=0;ichannels[i]; - if(ess->dev_audio != -1) - unregister_sound_dsp(ess->dev_audio); - } - /* Goodbye, Mr. Bond. */ - maestro_power(card,ACPI_D3); - release_region(card->iobase, 256); - kfree(card); - } - devs = NULL; -} - static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf) { /* this notifier is called when the kernel is really shut down. */ M_printk("maestro: shutting down\n"); - nuke_maestros(); + /* this will remove all card instances too */ + pci_unregister_driver(&maestro_pci_driver); + /* XXX dunno about power management */ return NOTIFY_OK; } /* --------------------------------------------------------------------- */ -#ifdef MODULE -MODULE_AUTHOR("Zach Brown , Alan Cox "); -MODULE_DESCRIPTION("ESS Maestro Driver"); -#ifdef M_DEBUG -MODULE_PARM(debug,"i"); -#endif -MODULE_PARM(dsps_order,"i"); -MODULE_PARM(use_pm,"i"); -MODULE_PARM(clocking, "i"); -void cleanup_module(void) { +void cleanup_maestro(void) { M_printk("maestro: unloading\n"); - nuke_maestros(); + pci_unregister_driver(&maestro_pci_driver); + pm_unregister_all(maestro_pm_callback); + unregister_reboot_notifier(&maestro_nb); } -#endif - /* --------------------------------------------------------------------- */ void @@ -3759,3 +3804,4 @@ } module_init(init_maestro); +module_exit(cleanup_maestro); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/sonicvibes.c linux.ac/drivers/sound/sonicvibes.c --- linux.vanilla/drivers/sound/sonicvibes.c Mon Apr 30 15:13:27 2001 +++ linux.ac/drivers/sound/sonicvibes.c Tue May 22 19:05:12 2001 @@ -92,6 +92,8 @@ * Tjeerd Mulder * 31.01.2001 0.29 Register/Unregister gameport * Fix SETTRIGGER non OSS API conformity + * 18.05.2001 0.30 PCI probing and error values cleaned up by Marcus + * Meissner * */ @@ -2504,21 +2506,24 @@ static const char __initdata sv_ddma_name[] = "S3 Inc. SonicVibes DDMA Controller"; struct sv_state *s; mm_segment_t fs; - int i, val; + int i, val, ret; char *ddmaname; unsigned ddmanamelen; + if ((ret=pci_enable_device(pcidev))) + return ret; + if (!RSRCISIOREGION(pcidev, RESOURCE_SB) || !RSRCISIOREGION(pcidev, RESOURCE_ENH) || !RSRCISIOREGION(pcidev, RESOURCE_SYNTH) || !RSRCISIOREGION(pcidev, RESOURCE_MIDI) || !RSRCISIOREGION(pcidev, RESOURCE_GAME)) - return -1; + return -ENODEV; if (pcidev->irq == 0) - return -1; + return -ENODEV; if (pci_set_dma_mask(pcidev, 0x00ffffff)) { printk(KERN_WARNING "sonicvibes: architecture does not support 24bit PCI busmaster DMA\n"); - return -1; + return -ENODEV; } /* try to allocate a DDMA resource if not already available */ if (!RSRCISIOREGION(pcidev, RESOURCE_DDMA)) { @@ -2534,12 +2539,12 @@ pcidev->resource[RESOURCE_DDMA].name = NULL; kfree(ddmaname); printk(KERN_ERR "sv: cannot allocate DDMA controller io ports\n"); - return -1; + return -EBUSY; } } if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) { printk(KERN_WARNING "sv: out of memory\n"); - return -1; + return -ENOMEM; } memset(s, 0, sizeof(struct sv_state)); init_waitqueue_head(&s->dma_adc.wait); @@ -2567,7 +2572,8 @@ /* hack */ pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12); /* wavetable base address */ - + + ret = -EBUSY; if (!request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM")) { printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1); goto err_region5; @@ -2594,8 +2600,6 @@ printk(KERN_ERR "sv: gameport io ports in use\n"); s->gameport.io = s->gameport.size = 0; } - if (pci_enable_device(pcidev)) - goto err_irq; /* initialize codec registers */ outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */ udelay(50); @@ -2619,21 +2623,29 @@ wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff); wrindir(s, SV_CIADCOUTPUT, 0); /* request irq */ - if (request_irq(s->irq, sv_interrupt, SA_SHIRQ, "S3 SonicVibes", s)) { + if ((ret=request_irq(s->irq,sv_interrupt,SA_SHIRQ,"S3 SonicVibes",s))) { printk(KERN_ERR "sv: irq %u in use\n", s->irq); goto err_irq; } printk(KERN_INFO "sv: found adapter at io %#lx irq %u dmaa %#06x dmac %#06x revision %u\n", s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION)); /* register devices */ - if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0) + if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0) { + ret = s->dev_audio; goto err_dev1; - if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0) + } + if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; goto err_dev2; - if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0) + } + if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0) { + ret = s->dev_midi; goto err_dev3; - if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) + } + if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) { + ret = s->dev_dmfm; goto err_dev4; + } pci_set_master(pcidev); /* enable bus mastering */ /* initialize the chips */ fs = get_fs(); @@ -2679,7 +2691,7 @@ release_region(s->ioenh, SV_EXTENT_ENH); err_region5: kfree(s); - return -1; + return ret; } static void __devinit sv_remove(struct pci_dev *dev) @@ -2731,7 +2743,7 @@ { if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "sv: version v0.29 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "sv: version v0.30 time " __TIME__ " " __DATE__ "\n"); #if 0 if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/trident.c linux.ac/drivers/sound/trident.c --- linux.vanilla/drivers/sound/trident.c Sat May 26 16:53:13 2001 +++ linux.ac/drivers/sound/trident.c Tue May 22 10:38:45 2001 @@ -104,6 +104,11 @@ * Mmap support * "Channel Binding" ioctl extension (done) * new pci device driver interface for 2.4 kernel (done) + * + * Lock order (high->low) + * lock - hardware lock + * open_sem - guard opens + * sem - guard dmabuf, write re-entry etc */ #include @@ -243,6 +248,9 @@ int multi_channels_adjust_count; unsigned chans_num; unsigned fmt_flag:1; + /* Guard against mmap/write/read races */ + struct semaphore sem; + }; /* hardware channels */ @@ -296,7 +304,7 @@ /* The trident has a certain amount of cross channel interaction so we use a single per card lock */ spinlock_t lock; - + /* PCI device stuff */ struct pci_dev * pci_dev; u16 pci_id; @@ -359,9 +367,10 @@ static void ali_enable_special_channel(struct trident_state *stat); static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card); static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card); -static int ali_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data); static void ali_restore_regs(struct trident_card *card); static void ali_save_regs(struct trident_card *card); +static void trident_suspend(struct pci_dev *dev); +static void trident_resume(struct pci_dev *dev); static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel); static int ali_setup_multi_channels(struct trident_card *card, int chan_nums); static unsigned int ali_get_spdif_in_rate(struct trident_card *card); @@ -1416,7 +1425,7 @@ { struct trident_state *state = (struct trident_state *)file->private_data; struct dmabuf *dmabuf = &state->dmabuf; - ssize_t ret; + ssize_t ret = 0; unsigned long flags; unsigned swptr; int cnt; @@ -1428,13 +1437,15 @@ VALIDATE_STATE(state); if (ppos != &file->f_pos) return -ESPIPE; + if (dmabuf->mapped) return -ENXIO; - if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) - return ret; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; - ret = 0; + + down(&state->sem); + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + goto out; while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); @@ -1459,8 +1470,10 @@ start_adc(state); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; - return ret; + goto out; } + + up(&state->sem); /* No matter how much space left in the buffer, we have to wait until CSO == ESO/2 or CSO == ESO when address engine interrupts */ tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); @@ -1484,14 +1497,21 @@ } if (signal_pending(current)) { ret = ret ? ret : -ERESTARTSYS; - return ret; + goto out; + } + down(&state->sem); + if(dmabuf->mapped) + { + if(!ret) + ret = -ENXIO; + goto out; } continue; } if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { if (!ret) ret = -EFAULT; - return ret; + goto out; } swptr = (swptr + cnt) % dmabuf->dmasize; @@ -1506,11 +1526,14 @@ ret += cnt; start_adc(state); } +out: + up(&state->sem); return ret; } /* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to the soundcard. it is drained by the dma machine and filled by this loop. */ + static ssize_t trident_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct trident_state *state = (struct trident_state *)file->private_data; @@ -1528,12 +1551,27 @@ VALIDATE_STATE(state); if (ppos != &file->f_pos) return -ESPIPE; + + /* + * Guard against an mmap or ioctl while writing + */ + + down(&state->sem); + if (dmabuf->mapped) - return -ENXIO; + { + ret = -ENXIO; + goto out; + } if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) - return ret; + goto out; + if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; + { + ret= -EFAULT; + goto out; + } + ret = 0; while (count > 0) { @@ -1559,7 +1597,7 @@ start_dac(state); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; - return ret; + goto out; } /* No matter how much data left in the buffer, we have to wait until CSO == ESO/2 or CSO == ESO when address engine interrupts */ @@ -1567,6 +1605,8 @@ tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); tmo >>= sample_shift[dmabuf->fmt]; unlock_set_fmt(state); + up(&state->sem); + /* There are two situations when sleep_on_timeout returns, one is when the interrupt is serviced correctly and the process is waked up by ISR ON TIME. Another is when timeout is expired, which means that @@ -1586,7 +1626,14 @@ } if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; - return ret; + goto out; + } + down(&state->sem); + if(dmabuf->mapped) + { + if(!ret) + ret = -ENXIO; + goto out; } continue; } @@ -1606,14 +1653,14 @@ ret += copy_count; if (!ret) ret = -EFAULT; unlock_set_fmt(state); - return ret; + goto out; } } else { if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { if (!ret) ret = -EFAULT; unlock_set_fmt(state); - return ret; + goto out; } state_cnt = cnt; } @@ -1632,9 +1679,12 @@ ret += cnt; start_dac(state); } +out: + up(&state->sem); return ret; } + /* No kernel lock - we have our own spinlock */ static unsigned int trident_poll(struct file *file, struct poll_table_struct *wait) { @@ -1645,17 +1695,32 @@ VALIDATE_STATE(state); + /* + * Guard against a parallel poll and write causing multiple + * prog_dmabuf events + */ + + down(&state->sem); + if (file->f_mode & FMODE_WRITE) { if (!dmabuf->ready && prog_dmabuf(state, 0)) + { + up(&state->sem); return 0; + } poll_wait(file, &dmabuf->wait, wait); } if (file->f_mode & FMODE_READ) { if (!dmabuf->ready && prog_dmabuf(state, 1)) + { + up(&state->sem); return 0; + } poll_wait(file, &dmabuf->wait, wait); } + up(&state->sem); + spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); if (file->f_mode & FMODE_READ) { @@ -1685,6 +1750,14 @@ VALIDATE_STATE(state); lock_kernel(); + + /* + * Lock against poll read write or mmap creating buffers. Also lock + * a read or write against an mmap. + */ + + down(&state->sem); + if (vma->vm_flags & VM_WRITE) { if ((ret = prog_dmabuf(state, 0)) != 0) goto out; @@ -1707,6 +1780,7 @@ dmabuf->mapped = 1; ret = 0; out: + up(&state->sem); unlock_kernel(); return ret; } @@ -1718,7 +1792,7 @@ unsigned long flags; audio_buf_info abinfo; count_info cinfo; - int val, mapped, ret; + int val, mapped, ret = 0; struct trident_card *card = state->card; @@ -1733,8 +1807,9 @@ switch (cmd) { case OSS_GETVERSION: - return put_user(SOUND_VERSION, (int *)arg); - + ret = put_user(SOUND_VERSION, (int *)arg); + break; + case SNDCTL_DSP_RESET: /* FIXME: spin_lock ? */ if (file->f_mode & FMODE_WRITE) { @@ -1751,16 +1826,19 @@ dmabuf->swptr = dmabuf->hwptr = 0; dmabuf->count = dmabuf->total_bytes = 0; } - return 0; + break; case SNDCTL_DSP_SYNC: if (file->f_mode & FMODE_WRITE) - return drain_dac(state, file->f_flags & O_NONBLOCK); - return 0; + ret = drain_dac(state, file->f_flags & O_NONBLOCK); + break; case SNDCTL_DSP_SPEED: /* set smaple rate */ if (get_user(val, (int *)arg)) - return -EFAULT; + { + ret = -EFAULT; + break; + } if (val >= 0) { if (file->f_mode & FMODE_WRITE) { stop_dac(state); @@ -1777,11 +1855,15 @@ spin_unlock_irqrestore(&state->card->lock, flags); } } - return put_user(dmabuf->rate, (int *)arg); + ret = put_user(dmabuf->rate, (int *)arg); + break; case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ if (get_user(val, (int *)arg)) - return -EFAULT; + { + ret = -EFAULT; + break; + } lock_set_fmt(state); if (file->f_mode & FMODE_WRITE) { stop_dac(state); @@ -1800,26 +1882,34 @@ dmabuf->fmt &= ~TRIDENT_FMT_STEREO; } unlock_set_fmt(state); - return 0; + break; case SNDCTL_DSP_GETBLKSIZE: if (file->f_mode & FMODE_WRITE) { if ((val = prog_dmabuf(state, 0))) - return val; - return put_user(dmabuf->fragsize, (int *)arg); + ret = val; + else + ret = put_user(dmabuf->fragsize, (int *)arg); + break; } if (file->f_mode & FMODE_READ) { if ((val = prog_dmabuf(state, 1))) - return val; - return put_user(dmabuf->fragsize, (int *)arg); + ret = val; + else + ret = put_user(dmabuf->fragsize, (int *)arg); + break; } case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ - return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); + ret = put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); + break; case SNDCTL_DSP_SETFMT: /* Select sample format */ if (get_user(val, (int *)arg)) - return -EFAULT; + { + ret = -EFAULT; + break; + } lock_set_fmt(state); if (val != AFMT_QUERY) { if (file->f_mode & FMODE_WRITE) { @@ -1840,12 +1930,16 @@ } } unlock_set_fmt(state); - return put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? + ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + break; case SNDCTL_DSP_CHANNELS: if (get_user(val, (int *)arg)) - return -EFAULT; + { + ret = -EFAULT; + break; + } if (val != 0) { lock_set_fmt(state); if (file->f_mode & FMODE_WRITE) { @@ -1866,21 +1960,22 @@ if( card->rec_channel_use_count > 0 ) { - printk("Err: Record is working on the card!\n"); - return -EBUSY; + printk(KERN_ERR "trident: Record is working on the card!\n"); + ret = -EBUSY; + break; } ret = ali_setup_multi_channels(state->card, 6); if (ret < 0) { unlock_set_fmt(state); - return ret; + break; } down(&state->card->open_sem); ret = ali_allocate_other_states_resources(state, 6); if (ret < 0) { up(&state->card->open_sem); unlock_set_fmt(state); - return ret; + break; } state->card->multi_channel_use_count ++; up(&state->card->open_sem); @@ -1905,25 +2000,38 @@ } unlock_set_fmt(state); } - return put_user(val, (int *)arg); + ret = put_user(val, (int *)arg); + break; case SNDCTL_DSP_POST: - /* FIXME: the same as RESET ?? */ - return 0; + /* Cause the working fragment to be output */ + break; case SNDCTL_DSP_SUBDIVIDE: if (dmabuf->subdivision) - return -EINVAL; + { + ret = -EINVAL; + break; + } if (get_user(val, (int *)arg)) - return -EFAULT; + { + ret = -EFAULT; + break; + } if (val != 1 && val != 2 && val != 4) - return -EINVAL; + { + ret = -EINVAL; + break; + } dmabuf->subdivision = val; - return 0; + break; case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, (int *)arg)) - return -EFAULT; + { + ret = -EFAULT; + break; + } dmabuf->ossfragshift = val & 0xffff; dmabuf->ossmaxfrags = (val >> 16) & 0xffff; @@ -1934,13 +2042,19 @@ if (dmabuf->ossmaxfrags < 4) dmabuf->ossmaxfrags = 4; - return 0; + break; case SNDCTL_DSP_GETOSPACE: if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; + { + ret = -EINVAL; + break; + } if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - return val; + { + ret = val; + break; + } spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); abinfo.fragsize = dmabuf->fragsize; @@ -1948,13 +2062,20 @@ abinfo.fragstotal = dmabuf->numfrag; abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; spin_unlock_irqrestore(&state->card->lock, flags); - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + break; case SNDCTL_DSP_GETISPACE: if (!(file->f_mode & FMODE_READ)) - return -EINVAL; + { + ret = -EINVAL; + break; + } if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) - return val; + { + ret = val; + break; + } spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); abinfo.fragsize = dmabuf->fragsize; @@ -1962,15 +2083,17 @@ abinfo.fragstotal = dmabuf->numfrag; abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; spin_unlock_irqrestore(&state->card->lock, flags); - return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + break; case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; - return 0; + break; case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND, + ret = put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND, (int *)arg); + break; case SNDCTL_DSP_GETTRIGGER: val = 0; @@ -1978,15 +2101,19 @@ val |= PCM_ENABLE_INPUT; if ((file->f_mode & FMODE_WRITE) && dmabuf->enable) val |= PCM_ENABLE_OUTPUT; - return put_user(val, (int *)arg); + ret = put_user(val, (int *)arg); + break; case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) - return -EFAULT; + { + ret = -EFAULT; + break; + } if (file->f_mode & FMODE_READ) { if (val & PCM_ENABLE_INPUT) { if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) - return ret; + break; start_adc(state); } else stop_adc(state); @@ -1994,18 +2121,24 @@ if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) - return ret; + break; start_dac(state); } else stop_dac(state); } - return 0; + break; case SNDCTL_DSP_GETIPTR: if (!(file->f_mode & FMODE_READ)) - return -EINVAL; + { + ret = -EINVAL; + break; + } if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) - return val; + { + ret = val; + break; + } spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); cinfo.bytes = dmabuf->total_bytes; @@ -2014,13 +2147,21 @@ if (dmabuf->mapped) dmabuf->count &= dmabuf->fragsize-1; spin_unlock_irqrestore(&state->card->lock, flags); - return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + break; case SNDCTL_DSP_GETOPTR: if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; + { + ret = -EINVAL; + break; + } if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - return val; + { + ret = val; + break; + } + spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); cinfo.bytes = dmabuf->total_bytes; @@ -2029,43 +2170,62 @@ if (dmabuf->mapped) dmabuf->count &= dmabuf->fragsize-1; spin_unlock_irqrestore(&state->card->lock, flags); - return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo))?-EFAULT:0; + break; case SNDCTL_DSP_SETDUPLEX: - return -EINVAL; + ret = -EINVAL; + break; case SNDCTL_DSP_GETODELAY: if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; + { + ret = -EINVAL; + break; + } if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) - return val; + { + ret = val; + break; + } spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); val = dmabuf->count; spin_unlock_irqrestore(&state->card->lock, flags); - return put_user(val, (int *)arg); + ret = put_user(val, (int *)arg); + break; case SOUND_PCM_READ_RATE: - return put_user(dmabuf->rate, (int *)arg); + ret = put_user(dmabuf->rate, (int *)arg); + break; case SOUND_PCM_READ_CHANNELS: - return put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1, + ret = put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1, (int *)arg); + break; case SOUND_PCM_READ_BITS: - return put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? + ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + break; case SNDCTL_DSP_GETCHANNELMASK: - return put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE, + ret = put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE, (int *)arg); + break; case SNDCTL_DSP_BIND_CHANNEL: if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) - return -EINVAL; + { + ret = -EINVAL; + break; + } if (get_user(val, (int *)arg)) - return -EFAULT; + { + ret = -EFAULT; + break; + } if (val == DSP_BIND_QUERY) { val = dmabuf->channel->attribute | 0x3c00; val = attr2mask[val >> 8]; @@ -2077,17 +2237,20 @@ dmabuf->channel->attribute = (CHANNEL_SPC_PB|SRC_ENABLE); dmabuf->channel->attribute |= mask2attr[ffs(val)]; } - return put_user(val, (int *)arg); + ret = put_user(val, (int *)arg); + break; case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: - return -EINVAL; + default: + ret = -EINVAL; + break; } - return -EINVAL; + return ret; } static int trident_open(struct inode *inode, struct file *file) @@ -2908,21 +3071,22 @@ restore_flags(flags); } -static int ali_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +static void trident_suspend(struct pci_dev *dev) { - struct trident_card *card = (struct trident_card *)dev->data; - - if (card) { - switch (rqst) { - case PM_SUSPEND: - ali_save_regs(card); - break; - case PM_RESUME: - ali_restore_regs(card); - break; - } + struct trident_card *card = (struct trident_card *) dev; + + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + ali_save_regs(card); + } +} + +static void trident_resume(struct pci_dev *dev) +{ + struct trident_card *card = (struct trident_card *) dev; + + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { + ali_restore_regs(card); } - return 0; } static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card) @@ -3383,14 +3547,6 @@ card_names[pci_id->driver_data], card->iobase, card->irq); if(card->pci_id == PCI_DEVICE_ID_ALI_5451) { - /* ALi Power Management */ - struct pm_dev *pmdev; - - pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pci_dev), - ali_pm_callback); - if (pmdev) - pmdev->data = card; - /* ALi channel Management */ card->alloc_pcm_channel = ali_alloc_pcm_channel; card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel; @@ -3479,7 +3635,6 @@ #ifdef CONFIG_PROC_FS remove_proc_entry("ALi5451", NULL); #endif - pm_unregister_all(ali_pm_callback); } /* Kill interrupts, and SP/DIF */ @@ -3512,6 +3667,8 @@ id_table: trident_pci_tbl, probe: trident_probe, remove: trident_remove, + suspend: trident_suspend, + resume: trident_resume }; static int __init trident_init_module (void) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ymfpci.c linux.ac/drivers/sound/ymfpci.c --- linux.vanilla/drivers/sound/ymfpci.c Sat May 26 16:53:14 2001 +++ linux.ac/drivers/sound/ymfpci.c Sat May 26 00:20:50 2001 @@ -23,10 +23,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * TODO: - * - Use P44Slot for 44.1 playback. + * - Use P44Slot for 44.1 playback (beware of idle buzzing in P44Slot). * - 96KHz playback for DVD - use pitch of 2.0. - * - uLaw for Sun apps. - * : Alan says firmly "no software format conversion in kernel". * - Retain DMA buffer on close, do not wait the end of frame. * - Cleanup * ? underused structure members @@ -66,14 +64,19 @@ #endif #include "ymfpci.h" -static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd); +static int ymf_playback_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); static void ymf_capture_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); -static void ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice); +static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice); static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank); static int ymf_playback_prepare(struct ymf_state *state); static int ymf_capture_prepare(struct ymf_state *state); static struct ymf_state *ymf_state_alloc(ymfpci_t *unit); +static void ymfpci_aclink_reset(struct pci_dev * pci); +static void ymfpci_disable_dsp(ymfpci_t *unit); +static void ymfpci_download_image(ymfpci_t *codec); +static void ymf_memload(ymfpci_t *unit); + static LIST_HEAD(ymf_devs); /* @@ -330,7 +333,7 @@ spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->hwptr = dmabuf->swptr = 0; dmabuf->total_bytes = 0; - dmabuf->count = dmabuf->error = 0; + dmabuf->count = 0; spin_unlock_irqrestore(&state->unit->reg_lock, flags); /* allocate DMA buffer if not allocated yet */ @@ -810,8 +813,6 @@ end >>= 1; if (w_16) end >>= 1; -/* P3 */ // printk("ymf_pcm_init_voice: %d: Rate %d Format 0x%08x Delta 0x%x End 0x%x\n", -// voice->number, rate, format, delta, end); for (nbank = 0; nbank < 2; nbank++) { bank = &voice->bank[nbank]; bank->format = format; @@ -907,7 +908,7 @@ if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) { /* Somebody started 32 mpg123's in parallel? */ - /* P3 */ printk("ymfpci%d: cannot allocate voice\n", + printk(KERN_INFO "ymfpci%d: cannot allocate voice\n", state->unit->dev_audio); return err; } @@ -1178,6 +1179,7 @@ { struct ymf_state *state = (struct ymf_state *)file->private_data; struct ymf_dmabuf *dmabuf = &state->rpcm.dmabuf; + struct ymf_unit *unit = state->unit; DECLARE_WAITQUEUE(waita, current); ssize_t ret; unsigned long flags; @@ -1190,18 +1192,26 @@ return -ENXIO; if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; ret = 0; add_wait_queue(&dmabuf->wait, &waita); while (count > 0) { - spin_lock_irqsave(&state->unit->reg_lock, flags); + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } swptr = dmabuf->swptr; cnt = dmabuf->dmasize - swptr; if (dmabuf->count < cnt) cnt = dmabuf->count; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); + spin_unlock_irqrestore(&unit->reg_lock, flags); if (cnt > count) cnt = count; @@ -1240,7 +1250,7 @@ } spin_unlock_irqrestore(&state->unit->reg_lock, flags); if (signal_pending(current)) { - ret = ret ? ret : -ERESTARTSYS; + if (!ret) ret = -ERESTARTSYS; break; } continue; @@ -1253,19 +1263,24 @@ swptr = (swptr + cnt) % dmabuf->dmasize; - spin_lock_irqsave(&state->unit->reg_lock, flags); + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + continue; + } + dmabuf->swptr = swptr; dmabuf->count -= cnt; - // spin_unlock_irqrestore(&state->unit->reg_lock, flags); + // spin_unlock_irqrestore(&unit->reg_lock, flags); count -= cnt; buffer += cnt; ret += cnt; - // spin_lock_irqsave(&state->unit->reg_lock, flags); + // spin_lock_irqsave(&unit->reg_lock, flags); if (!state->rpcm.running) { - ymf_capture_trigger(state->unit, &state->rpcm, 1); + ymf_capture_trigger(unit, &state->rpcm, 1); } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); + spin_unlock_irqrestore(&unit->reg_lock, flags); } set_current_state(TASK_RUNNING); remove_wait_queue(&dmabuf->wait, &waita); @@ -1278,6 +1293,7 @@ { struct ymf_state *state = (struct ymf_state *)file->private_data; struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf; + struct ymf_unit *unit = state->unit; DECLARE_WAITQUEUE(waita, current); ssize_t ret; unsigned long flags; @@ -1294,8 +1310,6 @@ return -ENXIO; if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; ret = 0; /* @@ -1310,7 +1324,17 @@ add_wait_queue(&dmabuf->wait, &waita); while (count > 0) { - spin_lock_irqsave(&state->unit->reg_lock, flags); + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } if (dmabuf->count < 0) { printk(KERN_ERR "ymf_write: count %d, was legal in cs46xx\n", @@ -1342,7 +1366,7 @@ cnt = dmabuf->dmasize - swptr; if (dmabuf->count + cnt > dmabuf->dmasize - redzone) cnt = (dmabuf->dmasize - redzone) - dmabuf->count; - spin_unlock_irqrestore(&state->unit->reg_lock, flags); + spin_unlock_irqrestore(&unit->reg_lock, flags); if (cnt > count) cnt = count; @@ -1353,11 +1377,11 @@ * buffer is full, start the dma machine and * wait for data to be played */ - spin_lock_irqsave(&state->unit->reg_lock, flags); + spin_lock_irqsave(&unit->reg_lock, flags); if (!state->wpcm.running) { - ymf_playback_trigger(state->unit, &state->wpcm, 1); + ymf_playback_trigger(unit, &state->wpcm, 1); } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); + spin_unlock_irqrestore(&unit->reg_lock, flags); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; @@ -1379,7 +1403,11 @@ swptr -= dmabuf->dmasize; } - spin_lock_irqsave(&state->unit->reg_lock, flags); + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + continue; + } dmabuf->swptr = swptr; dmabuf->count += cnt; @@ -1393,10 +1421,10 @@ delay = state->format.rate / 20; /* 50ms */ delay <<= state->format.shift; if (dmabuf->count >= delay && !state->wpcm.running) { - ymf_playback_trigger(state->unit, &state->wpcm, 1); + ymf_playback_trigger(unit, &state->wpcm, 1); } - spin_unlock_irqrestore(&state->unit->reg_lock, flags); + spin_unlock_irqrestore(&unit->reg_lock, flags); count -= cnt; buffer += cnt; @@ -1621,7 +1649,6 @@ case SNDCTL_DSP_CHANNELS: if (get_user(val, (int *)arg)) return -EFAULT; - /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_CHANNELS 0x%x\n", val); */ if (val != 0) { if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); @@ -2016,6 +2043,77 @@ }; /* + */ + +static void ymf_suspend(struct pci_dev *pcidev) +{ + struct ymf_unit *unit = pci_get_drvdata(pcidev); + unsigned long flags; + struct ymf_dmabuf *dmabuf; + struct list_head *p; + struct ymf_state *state; + + spin_lock_irqsave(&unit->reg_lock, flags); + + unit->suspended = 1; + + list_for_each(p, &unit->states) { + state = list_entry(p, struct ymf_state, chain); + + dmabuf = &state->wpcm.dmabuf; + dmabuf->hwptr = dmabuf->swptr = 0; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + + dmabuf = &state->rpcm.dmabuf; + dmabuf->hwptr = dmabuf->swptr = 0; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + } + + ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0); + ymfpci_disable_dsp(unit); + + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +static void ymf_resume(struct pci_dev *pcidev) +{ + struct ymf_unit *unit = pci_get_drvdata(pcidev); + unsigned long flags; + struct list_head *p; + struct ymf_state *state; + + ymfpci_aclink_reset(unit->pci); + ymfpci_codec_ready(unit, 0, 1); /* prints diag if not ready. */ + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + /* XXX At this time the legacy registers are probably deprogrammed. */ +#endif + + ymfpci_download_image(unit); + udelay(100); + + ymf_memload(unit); + + spin_lock_irqsave(&unit->reg_lock, flags); + + if (unit->start_count) { + ymfpci_writel(unit, YDSXGR_MODE, 3); + unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; + } + + unit->suspended = 0; + list_for_each(p, &unit->states) { + state = list_entry(p, struct ymf_state, chain); + wake_up(&state->wpcm.dmabuf.wait); + wake_up(&state->rpcm.dmabuf.wait); + } + + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +/* * initialization routines */ @@ -2238,28 +2336,6 @@ ptr += (codec->bank_size_effect + 0x00ff) & ~0x00ff; codec->work_base = ptr; - ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, virt_to_bus(codec->bank_base_playback)); - ymfpci_writel(codec, YDSXGR_RECCTRLBASE, virt_to_bus(codec->bank_base_capture)); - ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, virt_to_bus(codec->bank_base_effect)); - ymfpci_writel(codec, YDSXGR_WORKBASE, virt_to_bus(codec->work_base)); - ymfpci_writel(codec, YDSXGR_WORKSIZE, codec->work_size >> 2); - - /* S/PDIF output initialization */ - ymfpci_writew(codec, YDSXGR_SPDIFOUTCTRL, 0); - ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS, - SND_PCM_AES0_CON_EMPHASIS_NONE | - (SND_PCM_AES1_CON_ORIGINAL << 8) | - (SND_PCM_AES1_CON_PCM_CODER << 8)); - - /* S/PDIF input initialization */ - ymfpci_writew(codec, YDSXGR_SPDIFINCTRL, 0); - - /* move this volume setup to mixer */ - ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); - ymfpci_writel(codec, YDSXGR_BUF441OUTVOL, 0); - ymfpci_writel(codec, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); - ymfpci_writel(codec, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); - return 0; } @@ -2273,6 +2349,32 @@ kfree(codec->work_ptr); } +static void ymf_memload(ymfpci_t *unit) +{ + + ymfpci_writel(unit, YDSXGR_PLAYCTRLBASE, virt_to_bus(unit->bank_base_playback)); + ymfpci_writel(unit, YDSXGR_RECCTRLBASE, virt_to_bus(unit->bank_base_capture)); + ymfpci_writel(unit, YDSXGR_EFFCTRLBASE, virt_to_bus(unit->bank_base_effect)); + ymfpci_writel(unit, YDSXGR_WORKBASE, virt_to_bus(unit->work_base)); + ymfpci_writel(unit, YDSXGR_WORKSIZE, unit->work_size >> 2); + + /* S/PDIF output initialization */ + ymfpci_writew(unit, YDSXGR_SPDIFOUTCTRL, 0); + ymfpci_writew(unit, YDSXGR_SPDIFOUTSTATUS, + SND_PCM_AES0_CON_EMPHASIS_NONE | + (SND_PCM_AES1_CON_ORIGINAL << 8) | + (SND_PCM_AES1_CON_PCM_CODER << 8)); + + /* S/PDIF input initialization */ + ymfpci_writew(unit, YDSXGR_SPDIFINCTRL, 0); + + /* move this volume setup to mixer */ + ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); + ymfpci_writel(unit, YDSXGR_BUF441OUTVOL, 0); + ymfpci_writel(unit, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); + ymfpci_writel(unit, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); +} + static int ymf_ac97_init(ymfpci_t *unit, int num_ac97) { struct ac97_codec *codec; @@ -2383,8 +2485,7 @@ if (ymfpci_memalloc(codec) < 0) goto out_disable_dsp; - - /* ymfpci_proc_init(unit, codec); */ + ymf_memload(codec); if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) { printk(KERN_ERR "ymfpci: unable to request IRQ %d\n", @@ -2480,6 +2581,8 @@ id_table: ymf_id_tbl, probe: ymf_probe_one, remove: ymf_remove_one, + suspend: ymf_suspend, + resume: ymf_resume }; static int __init ymf_init_module(void) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/sound/ymfpci.h linux.ac/drivers/sound/ymfpci.h --- linux.vanilla/drivers/sound/ymfpci.h Sat May 26 16:53:14 2001 +++ linux.ac/drivers/sound/ymfpci.h Sat May 26 00:20:50 2001 @@ -257,6 +257,7 @@ ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2]; int start_count; + int suspended; u32 active_bank; struct ymf_voice voices[64]; @@ -282,8 +283,6 @@ struct list_head ymf_devs; struct list_head states; /* List of states for this unit */ - /* For the moment we do not traverse list of states so it is - * entirely useless. Will be used (PM) or killed. XXX */ }; struct ymf_dmabuf { @@ -300,7 +299,6 @@ int count; /* fill count */ unsigned total_bytes; /* total bytes dmaed by hardware */ - unsigned error; /* number of over/underruns */ wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ /* redundant, but makes calculations easier */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/telephony/ixj.c linux.ac/drivers/telephony/ixj.c --- linux.vanilla/drivers/telephony/ixj.c Sat May 26 16:53:14 2001 +++ linux.ac/drivers/telephony/ixj.c Thu May 24 23:35:24 2001 @@ -4480,9 +4480,15 @@ if (lcp == NULL) return -ENOMEM; if (copy_from_user(lcp, (char *) cp, sizeof(IXJ_FILTER_CADENCE))) + { + kfree(lcp); return -EFAULT; + } if (lcp->filter >= 4) + { + kfree(lcp); return -1; + } j->cadence_f[lcp->filter].state = 0; j->cadence_f[lcp->filter].enable = lcp->enable; j->filter_en[lcp->filter] = j->cadence_f[lcp->filter].en_filter = lcp->en_filter; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/CDCEther.c linux.ac/drivers/usb/CDCEther.c --- linux.vanilla/drivers/usb/CDCEther.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/CDCEther.c Sun Apr 22 01:58:23 2001 @@ -0,0 +1,1269 @@ +// Portions of this file taken from +// Petko Manolov - Petkan (petkan@dce.bg) +// from his driver pegasus.c + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "CDCEther.h" + +static const char *version = __FILE__ ": v0.98.2 28 March 2001 Brad Hards and another"; + +// We will attempt to probe anything that is in the +// communication device class... +// We will sort through them later. +static struct usb_device_id CDCEther_ids[] = { + { USB_DEVICE_INFO(2, 0, 0) }, + { } +}; + +////////////////////////////////////////////////////////////////////////////// +// Callback routines from USB device ///////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +static void read_bulk_callback( struct urb *urb ) +{ + ether_dev_t *ether_dev = urb->context; + struct net_device *net; + int count = urb->actual_length, res; + struct sk_buff *skb; + + // Sanity check + if ( !ether_dev || !(ether_dev->flags & CDC_ETHER_RUNNING) ) { + dbg("BULK IN callback but driver is not active!"); + return; + } + + net = ether_dev->net; + if ( !netif_device_present(net) ) { + // Somebody killed our network interface... + return; + } + + if ( ether_dev->flags & CDC_ETHER_RX_BUSY ) { + // Are we already trying to receive a frame??? + ether_dev->stats.rx_errors++; + dbg("ether_dev Rx busy"); + return; + } + + // We are busy, leave us alone! + ether_dev->flags |= CDC_ETHER_RX_BUSY; + + switch ( urb->status ) { + case USB_ST_NOERROR: + break; + case USB_ST_NORESPONSE: + dbg( "no repsonse in BULK IN" ); + ether_dev->flags &= ~CDC_ETHER_RX_BUSY; + break; + default: + dbg( "%s: RX status %d", net->name, urb->status ); + goto goon; + } + + // Check to make sure we got some data... + if ( !count ) { + // We got no data!!! + goto goon; + } + + // Tell the kernel we want some memory + if ( !(skb = dev_alloc_skb(count)) ) { + // We got no receive buffer. + goto goon; + } + + // Here's where it came from + skb->dev = net; + + // Now we copy it over + eth_copy_and_sum(skb, ether_dev->rx_buff, count, 0); + + // Not sure + skb_put(skb, count); + // Not sure here either + skb->protocol = eth_type_trans(skb, net); + + // Ship it off to the kernel + netif_rx(skb); + + // update out statistics + ether_dev->stats.rx_packets++; + ether_dev->stats.rx_bytes += count; + +goon: + // Prep the USB to wait for another frame + FILL_BULK_URB( ðer_dev->rx_urb, ether_dev->usb, + usb_rcvbulkpipe(ether_dev->usb, ether_dev->data_ep_in), + ether_dev->rx_buff, ether_dev->wMaxSegmentSize, + read_bulk_callback, ether_dev ); + + // Give this to the USB subsystem so it can tell us + // when more data arrives. + if ( (res = usb_submit_urb(ðer_dev->rx_urb)) ) { + warn( __FUNCTION__ " failed submint rx_urb %d", res); + } + + // We are no longer busy, show us the frames!!! + ether_dev->flags &= ~CDC_ETHER_RX_BUSY; +} + +static void write_bulk_callback( struct urb *urb ) +{ + ether_dev_t *ether_dev = urb->context; + + // Sanity check + if ( !ether_dev || !(ether_dev->flags & CDC_ETHER_RUNNING) ) { + // We are insane!!! + err( "write_bulk_callback: device not running" ); + return; + } + + // Do we still have a valid kernel network device? + if ( !netif_device_present(ether_dev->net) ) { + // Someone killed our network interface. + err( "write_bulk_callback: net device not present" ); + return; + } + + // Hmm... What on Earth could have happened??? + if ( urb->status ) { + info("%s: TX status %d", ether_dev->net->name, urb->status); + } + + // Update the network interface and tell it we are + // ready for another frame + ether_dev->net->trans_start = jiffies; + netif_wake_queue( ether_dev->net ); +} + +//static void intr_callback( struct urb *urb ) +//{ +// ether_dev_t *ether_dev = urb->context; +// struct net_device *net; +// __u8 *d; +// +// if ( !ether_dev ) +// return; +// +// switch ( urb->status ) { +// case USB_ST_NOERROR: +// break; +// case USB_ST_URB_KILLED: +// return; +// default: +// info("intr status %d", urb->status); +// } +// +// d = urb->transfer_buffer; +// net = ether_dev->net; +// if ( d[0] & 0xfc ) { +// ether_dev->stats.tx_errors++; +// if ( d[0] & TX_UNDERRUN ) +// ether_dev->stats.tx_fifo_errors++; +// if ( d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT) ) +// ether_dev->stats.tx_aborted_errors++; +// if ( d[0] & LATE_COL ) +// ether_dev->stats.tx_window_errors++; +// if ( d[0] & (NO_CARRIER | LOSS_CARRIER) ) +// ether_dev->stats.tx_carrier_errors++; +// } +//} + +////////////////////////////////////////////////////////////////////////////// +// Routines for turning net traffic on and off on the USB side /////////////// +////////////////////////////////////////////////////////////////////////////// + +static inline int enable_net_traffic( ether_dev_t *ether_dev ) +{ + struct usb_device *usb = ether_dev->usb; + + // Here would be the time to set the data interface to the configuration where + // it has two endpoints that use a protocol we can understand. + + if (usb_set_interface( usb, + ether_dev->data_bInterfaceNumber, + ether_dev->data_bAlternateSetting_with_traffic ) ) { + err("usb_set_interface() failed" ); + err("Attempted to set interface %d", ether_dev->data_bInterfaceNumber); + err("To alternate setting %d", ether_dev->data_bAlternateSetting_with_traffic); + return -1; + } + return 0; +} + +static inline void disable_net_traffic( ether_dev_t *ether_dev ) +{ + // The thing to do is to set the data interface to the alternate setting that has + // no endpoints. This is what the spec suggests. + + if (ether_dev->data_interface_altset_num_without_traffic >= 0 ) { + if (usb_set_interface( ether_dev->usb, + ether_dev->data_bInterfaceNumber, + ether_dev->data_bAlternateSetting_without_traffic ) ) { + err("usb_set_interface() failed"); + } + } else { + // Some devices just may not support this... + warn("No way to disable net traffic"); + } +} + +////////////////////////////////////////////////////////////////////////////// +// Callback routines for kernel Ethernet Device ////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +static void CDCEther_tx_timeout( struct net_device *net ) +{ + ether_dev_t *ether_dev = net->priv; + + // Sanity check + if ( !ether_dev ) { + // Seems to be a case of insanity here + return; + } + + // Tell syslog we are hosed. + warn("%s: Tx timed out.", net->name); + + // Tear the waiting frame off the list + ether_dev->tx_urb.transfer_flags |= USB_ASYNC_UNLINK; + usb_unlink_urb( ðer_dev->tx_urb ); + + // Update statistics + ether_dev->stats.tx_errors++; +} + +static int CDCEther_start_xmit( struct sk_buff *skb, struct net_device *net ) +{ + ether_dev_t *ether_dev = net->priv; + int count; + int res; + + // If we are told to transmit an ethernet frame that fits EXACTLY + // into an integer number of USB packets, we force it to send one + // more byte so the device will get a runt USB packet signalling the + // end of the ethernet frame + if ( (skb->len) ^ (ether_dev->data_ep_out_size) ) { + // It was not an exact multiple + // no need to add anything extra + count = skb->len; + } else { + // Add one to make it NOT an exact multiple + count = skb->len + 1; + } + + // Tell the kernel, "No more frames 'til we are done + // with this one.' + netif_stop_queue( net ); + + // Copy it from kernel memory to OUR memory + memcpy(ether_dev->tx_buff, skb->data, skb->len); + + // Fill in the URB for shipping it out. + FILL_BULK_URB( ðer_dev->tx_urb, ether_dev->usb, + usb_sndbulkpipe(ether_dev->usb, ether_dev->data_ep_out), + ether_dev->tx_buff, ether_dev->wMaxSegmentSize, + write_bulk_callback, ether_dev ); + + // Tell the URB how much it will be transporting today + ether_dev->tx_urb.transfer_buffer_length = count; + + // Send the URB on its merry way. + if ((res = usb_submit_urb(ðer_dev->tx_urb))) { + // Hmm... It didn't go. Tell someone... + warn("failed tx_urb %d", res); + // update some stats... + ether_dev->stats.tx_errors++; + // and tell the kernel to give us another. + // Maybe we'll get it right next time. + netif_start_queue( net ); + } else { + // Okay, it went out. + // Update statistics + ether_dev->stats.tx_packets++; + ether_dev->stats.tx_bytes += skb->len; + // And tell the kernel when the last transmit occurred. + net->trans_start = jiffies; + } + + // We are done with the kernel's memory + dev_kfree_skb(skb); + + // We are done here. + return 0; +} + +static struct net_device_stats *CDCEther_netdev_stats( struct net_device *net ) +{ + // Easy enough! + return &((ether_dev_t *)net->priv)->stats; +} + +static int CDCEther_open(struct net_device *net) +{ + ether_dev_t *ether_dev = (ether_dev_t *)net->priv; + int res; + + // We are finally getting used! + MOD_INC_USE_COUNT; + + // Turn on the USB and let the packets flow!!! + if ( (res = enable_net_traffic( ether_dev )) ) { + err( __FUNCTION__ "can't enable_net_traffic() - %d", res ); + MOD_DEC_USE_COUNT; + return -EIO; + } + + // Prep a receive URB + FILL_BULK_URB( ðer_dev->rx_urb, ether_dev->usb, + usb_rcvbulkpipe(ether_dev->usb, ether_dev->data_ep_in), + ether_dev->rx_buff, ether_dev->wMaxSegmentSize, + read_bulk_callback, ether_dev ); + + // Put it out there so the device can send us stuff + if ( (res = usb_submit_urb(ðer_dev->rx_urb)) ) + { + // Hmm... Okay... + warn( __FUNCTION__ " failed rx_urb %d", res ); + } + + // Tell the kernel we are ready to start receiving from it + netif_start_queue( net ); + + // We are up and running. + ether_dev->flags |= CDC_ETHER_RUNNING; + + // Let's get ready to move frames!!! + return 0; +} + +static int CDCEther_close( struct net_device *net ) +{ + ether_dev_t *ether_dev = net->priv; + + // We are no longer running. + ether_dev->flags &= ~CDC_ETHER_RUNNING; + + // Tell the kernel to stop sending us stuff + netif_stop_queue( net ); + + // If we are not already unplugged, turn off USB + // traffic + if ( !(ether_dev->flags & CDC_ETHER_UNPLUG) ) { + disable_net_traffic( ether_dev ); + } + + // We don't need the URBs anymore. + usb_unlink_urb( ðer_dev->rx_urb ); + usb_unlink_urb( ðer_dev->tx_urb ); + usb_unlink_urb( ðer_dev->intr_urb ); + + // We are not being used now. + MOD_DEC_USE_COUNT; + + // That's it. I'm done. + return 0; +} + +static int CDCEther_ioctl( struct net_device *net, struct ifreq *rq, int cmd ) +{ + //__u16 *data = (__u16 *)&rq->ifr_data; + //ether_dev_t *ether_dev = net->priv; + + // No support here yet. + // Do we need support??? + switch(cmd) { + case SIOCDEVPRIVATE: + return -EOPNOTSUPP; + case SIOCDEVPRIVATE+1: + return -EOPNOTSUPP; + case SIOCDEVPRIVATE+2: + //return 0; + return -EOPNOTSUPP; + default: + return -EOPNOTSUPP; + } +} + +static void CDCEther_set_multicast( struct net_device *net ) +{ + ether_dev_t *ether_dev = net->priv; + + // Tell the kernel to stop sending us frames while we get this + // all set up. + netif_stop_queue(net); + + // Do what we are told. + if (net->flags & IFF_PROMISC) { + // TODO - Turn on promiscuous mode + info( "%s: Promiscuous mode enabled", net->name); + } else if (net->flags & IFF_ALLMULTI){ + // TODO - Here we need to tell the device to block ALL multicast traffic. + info("%s: set allmulti", net->name); + } else if (net->mc_count > ether_dev->wNumberMCFilters) { + // TODO - Here we need to set multicast filters, but + // There are more than our limit... Hmm... + info("%s: set too many MC filters", net->name); + } else { + // TODO - Here we are supposed to set SOME of the multicast filters. + // I must learn how to do this... + //info("%s: set Rx mode", net->name); + } + + // Tell the kernel to start giving frames to us again. + netif_wake_queue(net); +} + +////////////////////////////////////////////////////////////////////////////// +// Routines used to parse out the Functional Descriptors ///////////////////// +////////////////////////////////////////////////////////////////////////////// + +static int parse_header_functional_descriptor( int *bFunctionLength, + int bDescriptorType, + int bDescriptorSubtype, + unsigned char *data, + ether_dev_t *ether_dev, + int *requirements ) +{ + // Check to make sure we haven't seen one of these already. + if ( (~*requirements) & REQ_HDR_FUNC_DESCR ) { + err( "Multiple Header Functional Descriptors found." ); + return -1; + } + + // Is it the right size??? + if (*bFunctionLength != 5) { + info( "Invalid length in Header Functional Descriptor" ); + // This is a hack to get around a particular device (NO NAMES) + // It has this function length set to the length of the + // whole class-specific descriptor + *bFunctionLength = 5; + } + + // Nothing extremely useful here. + // We'll keep it for posterity + ether_dev->bcdCDC = data[0] + (data[1] << 8); + + // We've seen one of these + *requirements &= ~REQ_HDR_FUNC_DESCR; + + // It's all good. + return 0; +} + +static int parse_union_functional_descriptor( int *bFunctionLength, + int bDescriptorType, + int bDescriptorSubtype, + unsigned char *data, + ether_dev_t *ether_dev, + int *requirements ) +{ + // Check to make sure we haven't seen one of these already. + if ( (~*requirements) & REQ_UNION_FUNC_DESCR ) { + err( "Multiple Union Functional Descriptors found." ); + return -1; + } + + // Is it the right size? + if (*bFunctionLength != 5) { + // It is NOT the size we expected. + err( "Unsupported length in Union Functional Descriptor" ); + return -1; + } + + // Sanity check of sorts + if (ether_dev->comm_interface != data[0]) { + // This tells us that we are chasing the wrong comm + // interface or we are crazy or something else wierd. + err( "Union Functional Descriptor tells us to use a different Communication Interface" ); + return -1; + } + + // We'll need this in a few microseconds! + ether_dev->data_interface = data[1]; + + // We've seen one of these now. + *requirements &= ~REQ_UNION_FUNC_DESCR; + + // Done + return 0; +} + +static int parse_ethernet_functional_descriptor( int *bFunctionLength, + int bDescriptorType, + int bDescriptorSubtype, + unsigned char *data, + ether_dev_t *ether_dev, + int *requirements ) +{ + // Check to make sure we haven't seen one of these already. + if ( (~*requirements) & REQ_ETH_FUNC_DESCR ) { + err( "Multiple Ethernet Functional Descriptors found." ); + return -1; + } + + // Is it the right size? + if (*bFunctionLength != 13) { + err( "Invalid length in Ethernet Networking Functional Descriptor" ); + return -1; + } + + // Lots of goodies from this one. They are all important. + ether_dev->iMACAddress = data[0]; + ether_dev->bmEthernetStatistics = data[1] + (data[2] << 8) + (data[3] << 16) + (data[4] << 24); + ether_dev->wMaxSegmentSize = data[5] + (data[6] << 8); + ether_dev->wNumberMCFilters = (data[7] + (data[8] << 8)) & 0x00007FFF; + ether_dev->bNumberPowerFilters = data[9]; + + // We've seen one of these now. + *requirements &= ~REQ_ETH_FUNC_DESCR; + + // That's all she wrote. + return 0; +} + +static int parse_protocol_unit_functional_descriptor( int *bFunctionLength, + int bDescriptorType, + int bDescriptorSubtype, + unsigned char *data, + ether_dev_t *ether_dev, + int *requirements ) +{ + // There should only be one type if we are sane + if (bDescriptorType != CS_INTERFACE) { + info( "Invalid bDescriptorType found." ); + return -1; + } + + // The Subtype tells the tale. + switch (bDescriptorSubtype){ + case 0x00: // Header Functional Descriptor + return parse_header_functional_descriptor( bFunctionLength, + bDescriptorType, + bDescriptorSubtype, + data, + ether_dev, + requirements ); + break; + case 0x06: // Union Functional Descriptor + return parse_union_functional_descriptor( bFunctionLength, + bDescriptorType, + bDescriptorSubtype, + data, + ether_dev, + requirements ); + break; + case 0x0F: // Ethernet Networking Functional Descriptor + return parse_ethernet_functional_descriptor( bFunctionLength, + bDescriptorType, + bDescriptorSubtype, + data, + ether_dev, + requirements ); + break; + default: // We don't support this at this time... + // However that doesn't necessarily indicate an error. + return 0; + } + // How did we get here??? + return -1; +} + +static int parse_ethernet_class_information( unsigned char *data, int length, ether_dev_t *ether_dev ) +{ + int loc = 0; + int rc; + int bFunctionLength; + int bDescriptorType; + int bDescriptorSubtype; + int requirements = REQUIREMENTS_TOTAL; + + // As long as there is something here, we will try to parse it + while (loc < length) { + // Length + bFunctionLength = data[loc]; + loc++; + + // Type + bDescriptorType = data[loc]; + loc++; + + // Subtype + bDescriptorSubtype = data[loc]; + loc++; + + // ship this off to be processed elsewhere. + rc = parse_protocol_unit_functional_descriptor( &bFunctionLength, + bDescriptorType, + bDescriptorSubtype, + &data[loc], + ether_dev, + &requirements ); + // Did it process okay? + if (rc) { + // Something was hosed somewhere. + // No need to continue; + return -1; + } + // We have already taken three bytes. + loc += (bFunctionLength - 3); + } + // Check to see if we got everything we need. + if (requirements) { + // We missed some of the requirements... + err( "Not all required functional descriptors present 0x%08X", requirements ); + return -1; + } + // We got everything. + return 0; +} + +////////////////////////////////////////////////////////////////////////////// +// Routine to check for the existence of the Functional Descriptors ////////// +////////////////////////////////////////////////////////////////////////////// + +static int find_and_parse_ethernet_class_information( struct usb_device *device, ether_dev_t *ether_dev ) +{ + struct usb_config_descriptor *conf = NULL; + struct usb_interface *comm_intf_group = NULL; + struct usb_interface_descriptor *comm_intf = NULL; + int rc = -1; + // The assumption here is that find_ethernet_comm_interface + // and find_valid_configuration + // have already filled in the information about where to find + // the a valid commication interface. + + conf = &( device->config[ether_dev->configuration_num] ); + comm_intf_group = &( conf->interface[ether_dev->comm_interface] ); + comm_intf = &( comm_intf_group->altsetting[ether_dev->comm_interface_altset_num] ); + // Let's check and see if it has the extra information we need... + + if (comm_intf->extralen > 0) { + // This is where the information is SUPPOSED to be. + rc = parse_ethernet_class_information( comm_intf->extra, comm_intf->extralen, ether_dev ); + } else if (conf->extralen > 0) { + // This is a hack. The spec says it should be at the interface + // location checked above. However I have seen it here also. + // This is the same device that requires the functional descriptor hack above + warn( "Ethernet information found at device configuration. This is broken." ); + rc = parse_ethernet_class_information( comm_intf->extra, comm_intf->extralen, ether_dev ); + } else { + // I don't know where else to look. + warn( "No ethernet information found." ); + rc = -1; + } + return rc; +} + +////////////////////////////////////////////////////////////////////////////// +// Routines to verify the data interface ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +static int get_data_interface_endpoints( struct usb_device *device, ether_dev_t *ether_dev ) +{ + struct usb_config_descriptor *conf = NULL; + struct usb_interface *data_intf_group = NULL; + struct usb_interface_descriptor *data_intf = NULL; + + // Walk through and get to the data interface we are checking. + conf = &( device->config[ether_dev->configuration_num] ); + data_intf_group = &( conf->interface[ether_dev->data_interface] ); + data_intf = &( data_intf_group->altsetting[ether_dev->data_interface_altset_num_with_traffic] ); + + // Start out assuming we won't find anything we can use + ether_dev->data_ep_in = 0; + ether_dev->data_ep_out = 0; + + // If these are not BULK endpoints, we don't want them + if ( data_intf->endpoint[0].bmAttributes != 0x02 ) { + return -1; + } if ( data_intf->endpoint[1].bmAttributes != 0x02 ) { + return -1; + } + + // Check the first endpoint to see if it is IN or OUT + if ( data_intf->endpoint[0].bEndpointAddress & 0x80 ) { + // This endpoint is IN + ether_dev->data_ep_in = data_intf->endpoint[0].bEndpointAddress & 0x7F; + } else { + // This endpoint is OUT + ether_dev->data_ep_out = data_intf->endpoint[0].bEndpointAddress & 0x7F; + ether_dev->data_ep_out_size = data_intf->endpoint[0].wMaxPacketSize; + } + + // Check the second endpoint to see if it is IN or OUT + if ( data_intf->endpoint[1].bEndpointAddress & 0x80 ) { + // This endpoint is IN + ether_dev->data_ep_in = data_intf->endpoint[1].bEndpointAddress & 0x7F; + } else { + // This endpoint is OUT + ether_dev->data_ep_out = data_intf->endpoint[1].bEndpointAddress & 0x7F; + ether_dev->data_ep_out_size = data_intf->endpoint[1].wMaxPacketSize; + } + + // Now make sure we got both an IN and an OUT + if (ether_dev->data_ep_in && ether_dev->data_ep_out) { + // We did get both, we are in good shape... + info( "detected BULK OUT packets of size %d", ether_dev->data_ep_out_size ); + return 0; + } + return -1; +} + +static int verify_ethernet_data_interface( struct usb_device *device, ether_dev_t *ether_dev ) +{ + struct usb_config_descriptor *conf = NULL; + struct usb_interface *data_intf_group = NULL; + struct usb_interface_descriptor *data_intf = NULL; + int rc = -1; + int status; + int altset_num; + + // The assumption here is that parse_ethernet_class_information() + // and find_valid_configuration() + // have already filled in the information about where to find + // a data interface + conf = &( device->config[ether_dev->configuration_num] ); + data_intf_group = &( conf->interface[ether_dev->data_interface] ); + + // start out assuming we won't find what we are looking for. + ether_dev->data_interface_altset_num_with_traffic = -1; + ether_dev->data_bAlternateSetting_with_traffic = -1; + ether_dev->data_interface_altset_num_without_traffic = -1; + ether_dev->data_bAlternateSetting_without_traffic = -1; + + // Walk through every possible setting for this interface until + // we find what makes us happy. + for ( altset_num = 0; altset_num < data_intf_group->num_altsetting; altset_num++ ) { + data_intf = &( data_intf_group->altsetting[altset_num] ); + + // Is this a data interface we like? + if ( ( data_intf->bInterfaceClass == 0x0A ) + && ( data_intf->bInterfaceSubClass == 0x00 ) + && ( data_intf->bInterfaceProtocol == 0x00 ) ) { + if ( data_intf->bNumEndpoints == 2 ) { + // We are required to have one of these. + // An interface with 2 endpoints to send Ethernet traffic back and forth + // It actually may be possible that the device might only + // communicate in a vendor specific manner. + // That would not be very nice. + // We can add that one later. + ether_dev->data_bInterfaceNumber = data_intf->bInterfaceNumber; + ether_dev->data_interface_altset_num_with_traffic = altset_num; + ether_dev->data_bAlternateSetting_with_traffic = data_intf->bAlternateSetting; + status = get_data_interface_endpoints( device, ether_dev ); + if (!status) { + rc = 0; + } + } + if ( data_intf->bNumEndpoints == 0 ) { + // According to the spec we are SUPPOSED to have one of these + // In fact the device is supposed to come up in this state. + // However, I have seen a device that did not have such an interface. + // So it must be just optional for our driver... + ether_dev->data_bInterfaceNumber = data_intf->bInterfaceNumber; + ether_dev->data_interface_altset_num_without_traffic = altset_num; + ether_dev->data_bAlternateSetting_without_traffic = data_intf->bAlternateSetting; + } + } + } + return rc; +} + +////////////////////////////////////////////////////////////////////////////// +// Routine to find a communication interface ///////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +static int find_ethernet_comm_interface( struct usb_device *device, ether_dev_t *ether_dev ) +{ + struct usb_config_descriptor *conf = NULL; + struct usb_interface *comm_intf_group = NULL; + struct usb_interface_descriptor *comm_intf = NULL; + int intf_num; + int altset_num; + int rc; + + conf = &( device->config[ether_dev->configuration_num] ); + + // We need to check and see if any of these interfaces are something we want. + // Walk through each interface one at a time + for ( intf_num = 0; intf_num < conf->bNumInterfaces; intf_num++ ) { + comm_intf_group = &( conf->interface[intf_num] ); + // Now for each of those interfaces, check every possible + // alternate setting. + for ( altset_num = 0; altset_num < comm_intf_group->num_altsetting; altset_num++ ) { + comm_intf = &( comm_intf_group->altsetting[altset_num] ); + + // Is this a communication class of interface of the + // ethernet subclass variety. + if ( ( comm_intf->bInterfaceClass == 0x02 ) + && ( comm_intf->bInterfaceSubClass == 0x06 ) + && ( comm_intf->bInterfaceProtocol == 0x00 ) ) { + if ( comm_intf->bNumEndpoints == 1 ) { + // Good, we found one, we will try this one + // Fill in the structure... + ether_dev->comm_interface = intf_num; + ether_dev->comm_bInterfaceNumber = comm_intf->bInterfaceNumber; + ether_dev->comm_interface_altset_num = altset_num; + ether_dev->comm_bAlternateSetting = comm_intf->bAlternateSetting; + + // Look for the Ethernet Functional Descriptors + rc = find_and_parse_ethernet_class_information( device, ether_dev ); + if (rc) { + // Nope this was no good after all. + continue; + } + + // Check that we really can talk to the data + // interface + // This includes # of endpoints, protocols, + // etc. + rc = verify_ethernet_data_interface( device, ether_dev ); + if (rc) { + // We got something we didn't like + continue; + } + // This communication interface seems to give us everything + // we require. We have all the ethernet info we need. + // Let's get out of here and go home right now. + return 0; + } else { + // bNumEndPoints != 1 + // We found an interface that had the wrong number of + // endpoints but would have otherwise been okay + } // end bNumEndpoints check. + } // end interface specifics check. + } // end for altset_num + } // end for intf_num + return -1; +} + +////////////////////////////////////////////////////////////////////////////// +// Routine to go through all configurations and find one that //////////////// +// is an Ethernet Networking Device ////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +static int find_valid_configuration( struct usb_device *device, ether_dev_t *ether_dev ) +{ + struct usb_config_descriptor *conf = NULL; + int conf_num; + int rc; + + // We will try each and every possible configuration + for ( conf_num = 0; conf_num < device->descriptor.bNumConfigurations; conf_num++ ) { + conf = &( device->config[conf_num] ); + + // Our first requirement : 2 interfaces + if ( conf->bNumInterfaces != 2 ) { + // I currently don't know how to handle devices with any number of interfaces + // other than 2. + continue; + } + + // This one passed our first check, fill in some + // useful data + ether_dev->configuration_num = conf_num; + ether_dev->bConfigurationValue = conf->bConfigurationValue; + + // Now run it through the ringers and see what comes + // out the other side. + rc = find_ethernet_comm_interface( device, ether_dev ); + + // Check if we found an ethernet Communcation Device + if ( !rc ) { + // We found one. + return 0; + } + } + // None of the configurations suited us. + return -1; +} + +////////////////////////////////////////////////////////////////////////////// +// Routine that checks a given configuration to see if any driver //////////// +// has claimed any of the devices interfaces ///////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +static int check_for_claimed_interfaces( struct usb_config_descriptor *config ) +{ + struct usb_interface *comm_intf_group; + int intf_num; + + // Go through all the interfaces and make sure none are + // claimed by anybody else. + for ( intf_num = 0; intf_num < config->bNumInterfaces; intf_num++ ) { + comm_intf_group = &( config->interface[intf_num] ); + if ( usb_interface_claimed( comm_intf_group ) ) { + // Somebody has beat us to this guy. + // We can't change the configuration out from underneath of whoever + // is using this device, so we will go ahead and give up. + return -1; + } + } + // We made it all the way through. + // I guess no one has claimed any of these interfaces. + return 0; +} + +////////////////////////////////////////////////////////////////////////////// +// Routines to ask for and set the kernel network interface's MAC address //// +// Used by driver's probe routine //////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +static inline unsigned char hex2dec( unsigned char digit ) +{ + // Is there a standard way to do this??? + // I have written this code TOO MANY times. + if ( (digit >= '0') && (digit <= '9') ) { + return (digit - '0'); + } + if ( (digit >= 'a') && (digit <= 'f') ) { + return (digit - 'a' + 10); + } + if ( (digit >= 'A') && (digit <= 'F') ) { + return (digit - 'A' + 10); + } + return 0; +} + +static void set_ethernet_addr( ether_dev_t *ether_dev ) +{ + unsigned char mac_addr[6]; + int i; + int len; + unsigned char buffer[13]; + + // Let's assume we don't get anything... + mac_addr[0] = 0x00; + mac_addr[1] = 0x00; + mac_addr[2] = 0x00; + mac_addr[3] = 0x00; + mac_addr[4] = 0x00; + mac_addr[5] = 0x00; + + // Let's ask the device... + len = usb_string(ether_dev->usb, ether_dev->iMACAddress, buffer, 13); + + // Sanity check! + if (len != 12) { + // You gotta love failing sanity checks + err("Attempting to get MAC address returned %d bytes", len); + return; + } + + // Fill in the mac_addr + for (i = 0; i < 6; i++) { + mac_addr[i] = ( hex2dec( buffer[2 * i] ) << 4 ) + hex2dec( buffer[2 * i + 1] ); + } + + // Now copy it over to the kernel's network driver. + memcpy( ether_dev->net->dev_addr, mac_addr, sizeof(mac_addr) ); +} + +////////////////////////////////////////////////////////////////////////////// +// Routine to print to syslog information about the driver /////////////////// +// Used by driver's probe routine //////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +void log_device_info(ether_dev_t *ether_dev) +{ + int len; + int string_num; + unsigned char manu[256]; + unsigned char prod[256]; + unsigned char sern[256]; + unsigned char *mac_addr; + + // Default empty strings in case we don't find a real one + manu[0] = 0x00; + prod[0] = 0x00; + sern[0] = 0x00; + + // Try to get the device Manufacturer + string_num = ether_dev->usb->descriptor.iManufacturer; + if (string_num) { + // Put it into its buffer + len = usb_string(ether_dev->usb, string_num, manu, 255); + // Just to be safe + manu[len] = 0x00; + } + + // Try to get the device Product Name + string_num = ether_dev->usb->descriptor.iProduct; + if (string_num) { + // Put it into its buffer + len = usb_string(ether_dev->usb, string_num, prod, 255); + // Just to be safe + prod[len] = 0x00; + } + + // Try to get the device Serial Number + string_num = ether_dev->usb->descriptor.iSerialNumber; + if (string_num) { + // Put it into its buffer + len = usb_string(ether_dev->usb, string_num, sern, 255); + // Just to be safe + sern[len] = 0x00; + } + + // This makes it easier for us to print + mac_addr = ether_dev->net->dev_addr; + + // Now send everything we found to the syslog + info( "%s: %s %s %s %02X:%02X:%02X:%02X:%02X:%02X", + ether_dev->net->name, manu, prod, sern, mac_addr[0], + mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], + mac_addr[5] ); +} + +/* Forward declaration */ +static struct usb_driver CDCEther_driver ; + +////////////////////////////////////////////////////////////////////////////// +// Module's probe routine //////////////////////////////////////////////////// +// claims interfaces if they are for an Ethernet CDC ///////////////////////// +////////////////////////////////////////////////////////////////////////////// + +static void * CDCEther_probe( struct usb_device *usb, unsigned int ifnum, + const struct usb_device_id *id) +{ + struct net_device *net; + ether_dev_t *ether_dev; + int rc; + + // First we should check the active configuration to see if + // any other driver has claimed any of the interfaces. + if ( check_for_claimed_interfaces( usb->actconfig ) ) { + // Someone has already put there grubby paws on this device. + // We don't want it now... + return NULL; + } + + // We might be finding a device we can use. + // We all go ahead and allocate our storage space. + // We need to because we have to start filling in the data that + // we are going to need later. + if(!(ether_dev = kmalloc(sizeof(ether_dev_t), GFP_KERNEL))) { + err("out of memory allocating device structure"); + return NULL; + } + + // Zero everything out. + memset(ether_dev, 0, sizeof(ether_dev_t)); + + // Let's see if we can find a configuration we can use. + rc = find_valid_configuration( usb, ether_dev ); + if (rc) { + // Nope we couldn't find one we liked. + // This device was not meant for us to control. + kfree( ether_dev ); + return NULL; + } + + // Now that we FOUND a configuration. let's try to make the + // device go into it. + if ( usb_set_configuration( usb, ether_dev->bConfigurationValue ) ) { + err("usb_set_configuration() failed"); + kfree( ether_dev ); + return NULL; + } + + // Now set the communication interface up as required. + if (usb_set_interface(usb, ether_dev->comm_bInterfaceNumber, ether_dev->comm_bAlternateSetting)) { + err("usb_set_interface() failed"); + kfree( ether_dev ); + return NULL; + } + + // Only turn traffic on right now if we must... + if (ether_dev->data_interface_altset_num_without_traffic >= 0) { + // We found an alternate setting for the data + // interface that allows us to turn off traffic. + // We should use it. + if (usb_set_interface( usb, + ether_dev->data_bInterfaceNumber, + ether_dev->data_bAlternateSetting_without_traffic)) { + err("usb_set_interface() failed"); + kfree( ether_dev ); + return NULL; + } + } else { + // We didn't find an alternate setting for the data + // interface that would let us turn off traffic. + // Oh well, let's go ahead and do what we must... + if (usb_set_interface( usb, + ether_dev->data_bInterfaceNumber, + ether_dev->data_bAlternateSetting_with_traffic)) { + err("usb_set_interface() failed"); + kfree( ether_dev ); + return NULL; + } + } + + // Now we need to get a kernel Ethernet interface. + net = init_etherdev( NULL, 0 ); + if ( !net ) { + // Hmm... The kernel is not sharing today... + // Fine, we didn't want it anyway... + err( "Unable to initialize ethernet device" ); + kfree( ether_dev ); + return NULL; + } + + // Now that we have an ethernet device, let's set it up + // (And I don't mean "set [it] up the bomb".) + net->priv = ether_dev; + net->open = CDCEther_open; + net->stop = CDCEther_close; + net->watchdog_timeo = CDC_ETHER_TX_TIMEOUT; + net->tx_timeout = CDCEther_tx_timeout; // TX timeout function + net->do_ioctl = CDCEther_ioctl; + net->hard_start_xmit = CDCEther_start_xmit; + net->set_multicast_list = CDCEther_set_multicast; + net->get_stats = CDCEther_netdev_stats; + net->mtu = ether_dev->wMaxSegmentSize - 14; + + // We'll keep track of this information for later... + ether_dev->usb = usb; + ether_dev->net = net; + + // and don't forget the MAC address. + set_ethernet_addr( ether_dev ); + + // Send a message to syslog about what we are handling + log_device_info( ether_dev ); + + // I claim this interface to be a CDC Ethernet Networking device + usb_driver_claim_interface( &CDCEther_driver, + &(usb->config[ether_dev->configuration_num].interface[ether_dev->comm_interface]), + ether_dev ); + // I claim this interface to be a CDC Ethernet Networking device + usb_driver_claim_interface( &CDCEther_driver, + &(usb->config[ether_dev->configuration_num].interface[ether_dev->data_interface]), + ether_dev ); + + // Does this REALLY do anything??? + usb_inc_dev_use( usb ); + + // TODO - last minute HACK + ether_dev->comm_ep_in = 5; + + // Okay, we are finally done... + return NULL; +} + + +////////////////////////////////////////////////////////////////////////////// +// Module's disconnect routine /////////////////////////////////////////////// +// Called when the driver is unloaded or the device is unplugged ///////////// +// (Whichever happens first assuming the driver suceeded at its probe) /////// +////////////////////////////////////////////////////////////////////////////// + +static void CDCEther_disconnect( struct usb_device *usb, void *ptr ) +{ + ether_dev_t *ether_dev = ptr; + + // Sanity check!!! + if ( !ether_dev || !ether_dev->usb ) { + // We failed. We are insane!!! + warn("unregistering non-existant device"); + return; + } + + // Make sure we fail the sanity check if we try this again. + ether_dev->usb = NULL; + + // It is possible that this function is called before + // the "close" function. + // This tells the close function we are already disconnected + ether_dev->flags |= CDC_ETHER_UNPLUG; + + // We don't need the network device any more + unregister_netdev( ether_dev->net ); + + // For sanity checks + ether_dev->net = NULL; + + // I ask again, does this do anything??? + usb_dec_dev_use( usb ); + + // We are done with this interface + usb_driver_release_interface( &CDCEther_driver, + &(usb->config[ether_dev->configuration_num].interface[ether_dev->comm_interface]) ); + + // We are done with this interface too + usb_driver_release_interface( &CDCEther_driver, + &(usb->config[ether_dev->configuration_num].interface[ether_dev->data_interface]) ); + + // No more tied up kernel memory + kfree( ether_dev ); + + // This does no good, but it looks nice! + ether_dev = NULL; +} + +////////////////////////////////////////////////////////////////////////////// +// Driver info /////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +static struct usb_driver CDCEther_driver = { + name: "CDCEther", + probe: CDCEther_probe, + disconnect: CDCEther_disconnect, + id_table: CDCEther_ids, +}; + +////////////////////////////////////////////////////////////////////////////// +// init and exit routines called when driver is installed and uninstalled //// +////////////////////////////////////////////////////////////////////////////// + +int __init CDCEther_init(void) +{ + info( "%s", version ); + return usb_register( &CDCEther_driver ); +} + +void __exit CDCEther_exit(void) +{ + usb_deregister( &CDCEther_driver ); +} + +////////////////////////////////////////////////////////////////////////////// +// Module info /////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +module_init( CDCEther_init ); +module_exit( CDCEther_exit ); + +MODULE_AUTHOR("Brad Hards and another"); +MODULE_DESCRIPTION("USB CDC Ethernet driver"); + +MODULE_DEVICE_TABLE (usb, CDCEther_ids); + +////////////////////////////////////////////////////////////////////////////// +// End of file /////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/CDCEther.h linux.ac/drivers/usb/CDCEther.h --- linux.vanilla/drivers/usb/CDCEther.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/CDCEther.h Sun Apr 22 01:58:23 2001 @@ -0,0 +1,88 @@ +// Portions of this file taken from +// Petko Manolov - Petkan (petkan@dce.bg) +// from his driver pegasus.h + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define CS_INTERFACE 0x24 + +#define CDC_ETHER_MAX_MTU 1536 + +#define CDC_ETHER_PRESENT 0x00000001 +#define CDC_ETHER_RUNNING 0x00000002 +#define CDC_ETHER_TX_BUSY 0x00000004 +#define CDC_ETHER_RX_BUSY 0x00000008 +#define CDC_ETHER_UNPLUG 0x00000040 + +#define CDC_ETHER_TX_TIMEOUT (HZ*10) + +#define TX_UNDERRUN 0x80 +#define EXCESSIVE_COL 0x40 +#define LATE_COL 0x20 +#define NO_CARRIER 0x10 +#define LOSS_CARRIER 0x08 +#define JABBER_TIMEOUT 0x04 + +#define CDC_ETHER_REQT_READ 0xc0 +#define CDC_ETHER_REQT_WRITE 0x40 +#define CDC_ETHER_REQ_GET_REGS 0xf0 +#define CDC_ETHER_REQ_SET_REGS 0xf1 +#define CDC_ETHER_REQ_SET_REG PIPERIDER_REQ_SET_REGS +#define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) + +typedef struct _ether_dev_t { + struct usb_device *usb; + struct net_device *net; + struct net_device_stats stats; + unsigned flags; + int configuration_num; + int bConfigurationValue; + int comm_interface; + int comm_bInterfaceNumber; + int comm_interface_altset_num; + int comm_bAlternateSetting; + int comm_ep_in; + int data_interface; + int data_bInterfaceNumber; + int data_interface_altset_num_with_traffic; + int data_bAlternateSetting_with_traffic; + int data_interface_altset_num_without_traffic; + int data_bAlternateSetting_without_traffic; + int data_ep_in; + int data_ep_out; + int data_ep_out_size; + __u16 bcdCDC; + __u8 iMACAddress; + __u32 bmEthernetStatistics; + __u16 wMaxSegmentSize; + __u16 wNumberMCFilters; + __u8 bNumberPowerFilters; + int intr_interval; + struct urb rx_urb, tx_urb, intr_urb; + unsigned char ALIGN(rx_buff[CDC_ETHER_MAX_MTU]); + unsigned char ALIGN(tx_buff[CDC_ETHER_MAX_MTU]); + unsigned char ALIGN(intr_buff[8]); +} ether_dev_t; + +#define REQ_HDR_FUNC_DESCR 0x0001 +#define REQ_UNION_FUNC_DESCR 0x0002 +#define REQ_ETH_FUNC_DESCR 0x0004 +#define REQUIREMENTS_TOTAL 0x0007 + + + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/Config.in linux.ac/drivers/usb/Config.in --- linux.vanilla/drivers/usb/Config.in Sat May 26 16:53:14 2001 +++ linux.ac/drivers/usb/Config.in Wed May 23 00:35:00 2001 @@ -7,6 +7,8 @@ tristate 'Support for USB' CONFIG_USB if [ ! "$CONFIG_USB" = "n" ]; then bool ' USB verbose debug messages' CONFIG_USB_DEBUG + bool ' Long timeout for slow-responding devices (some MGE Ellipse UPSes)' CONFIG_USB_LONG_TIMEOUT + bool ' Large report fetching for "broken" devices (some APC UPSes)' CONFIG_USB_LARGE_CONFIG comment 'Miscellaneous USB options' bool ' Preliminary USB device filesystem' CONFIG_USB_DEVICEFS @@ -22,6 +24,8 @@ fi if [ "$CONFIG_USB_UHCI" != "y" ]; then dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB + else + define_bool CONFIG_USB_UHCI_ALT n fi dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB @@ -33,6 +37,7 @@ bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG bool ' Freecom USB/ATAPI Bridge support' CONFIG_USB_STORAGE_FREECOM bool ' Microtech CompactFlash/SmartMedia reader' CONFIG_USB_STORAGE_DPCM + bool ' SanDisk SDDR-09 SmartMedia reader support' CONFIG_USB_STORAGE_SDDR09 fi dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB @@ -42,6 +47,7 @@ comment ' Input core support is needed for USB HID' else dep_tristate ' USB Human Interface Device (full HID) support' CONFIG_USB_HID $CONFIG_USB $CONFIG_INPUT + dep_tristate ' /dev/usb/hiddev raw HID device support (EXPERIMENTAL)' CONFIG_USB_HIDDEV $CONFIG_USB $CONFIG_USB_HID if [ "$CONFIG_USB_HID" != "y" ]; then dep_tristate ' USB HIDBP Keyboard (basic) support' CONFIG_USB_KBD $CONFIG_USB $CONFIG_INPUT dep_tristate ' USB HIDBP Mouse (basic) support' CONFIG_USB_MOUSE $CONFIG_USB $CONFIG_INPUT @@ -54,18 +60,22 @@ dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB dep_tristate ' Microtek X6USB scanner support (EXPERIMENTAL)' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL + dep_tristate ' HP 5300 C scanner support (EXPERIMENTAL)' CONFIG_USB_HP5300 $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL comment 'USB Multimedia devices' dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' USB Philips Cameras' CONFIG_USB_PWC $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB SE401 Camera support' CONFIG_USB_SE401 $CONFIG_USB $CONFIG_VIDEO_DEV dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB comment 'USB Network adaptors' dep_tristate ' PLUSB Prolific USB-Network driver (EXPERIMENTAL)' CONFIG_USB_PLUSB $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL dep_tristate ' USB ADMtek Pegasus-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' NetChip 1080-based USB Host-to-Host Link (EXPERIMENTAL)' CONFIG_USB_NET1080 $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB KLSI KL5USB101-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_KAWETH $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB Communication Class Ethernet driver (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB-to-USB Networking (NetChip, Prolific, ...) (EXPERIMENTAL)' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL comment 'USB port drivers' dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/Makefile linux.ac/drivers/usb/Makefile --- linux.vanilla/drivers/usb/Makefile Sat May 26 16:53:14 2001 +++ linux.ac/drivers/usb/Makefile Wed May 23 13:56:19 2001 @@ -47,6 +47,7 @@ obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_HID) += hid.o +obj-$(CONFIG_USB_HIDDEV) += hiddev.o obj-$(CONFIG_USB_KBD) += usbkbd.o obj-$(CONFIG_USB_WACOM) += wacom.o @@ -54,20 +55,27 @@ obj-$(CONFIG_USB_ACM) += acm.o obj-$(CONFIG_USB_PRINTER) += printer.o obj-$(CONFIG_USB_AUDIO) += audio.o -obj-$(CONFIG_USB_IBMCAM) += ibmcam.o obj-$(CONFIG_USB_PWC) += pwc.o +obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o obj-$(CONFIG_USB_DC2XX) += dc2xx.o obj-$(CONFIG_USB_MDC800) += mdc800.o obj-$(CONFIG_USB_USS720) += uss720.o obj-$(CONFIG_USB_DABUSB) += dabusb.o -obj-$(CONFIG_USB_PLUSB) += plusb.o obj-$(CONFIG_USB_OV511) += ov511.o -obj-$(CONFIG_USB_PEGASUS) += pegasus.o +obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_USB_MICROTEK) += microtek.o +obj-$(CONFIG_USB_HP5300) += hp5300.o obj-$(CONFIG_USB_BLUETOOTH) += bluetooth.o -obj-$(CONFIG_USB_NET1080) += net1080.o + +# network drivers + +obj-$(CONFIG_USB_CDCETHER) += CDCEther.o +obj-$(CONFIG_USB_KAWETH) += kaweth.o +obj-$(CONFIG_USB_PEGASUS) += pegasus.o +obj-$(CONFIG_USB_PLUSB) += plusb.o +obj-$(CONFIG_USB_USBNET) += usbnet.o # Object files in subdirectories @@ -89,6 +97,5 @@ usbcore.o: $(usbcore-objs) $(LD) -r -o $@ $(usbcore-objs) -pwc.o: $(pwc-objs) +pwc.o: $(pwc-objs) $(LD) -r -o $@ $(pwc-objs) - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/acm.c linux.ac/drivers/usb/acm.c --- linux.vanilla/drivers/usb/acm.c Sat May 26 16:53:14 2001 +++ linux.ac/drivers/usb/acm.c Wed May 23 00:11:45 2001 @@ -1,5 +1,5 @@ /* - * acm.c Version 0.18 + * acm.c Version 0.19 * * Copyright (c) 1999 Armin Fuerst * Copyright (c) 1999 Pavel Machek @@ -21,6 +21,7 @@ * v0.16 - added code for modems with swapped data and control interfaces * v0.17 - added new style probing * v0.18 - fixed new style probing for devices with more configurations + * v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan) */ /* @@ -51,6 +52,7 @@ #include #include #include +#include #undef DEBUG #include @@ -202,13 +204,10 @@ newctrl = le16_to_cpup((__u16 *) data); -#if 0 - /* Please someone tell me how to do this properly to kill pppd and not kill minicom */ if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { dbg("calling hangup"); tty_hangup(acm->tty); } -#endif acm->ctrlin = newctrl; @@ -305,7 +304,14 @@ MOD_INC_USE_COUNT; - if (acm->used++) return 0; + lock_kernel(); + + if (acm->used++) { + unlock_kernel(); + return 0; + } + + unlock_kernel(); acm->ctrlurb.dev = acm->dev; if (usb_submit_urb(&acm->ctrlurb)) @@ -475,7 +481,7 @@ (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; newline.databits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; - acm->clocal = termios->c_cflag & CLOCAL; + acm->clocal = ((termios->c_cflag & CLOCAL) != 0); if (!newline.speed) { newline.speed = acm->line.speed; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/audio.c linux.ac/drivers/usb/audio.c --- linux.vanilla/drivers/usb/audio.c Sat May 26 16:53:14 2001 +++ linux.ac/drivers/usb/audio.c Wed May 23 00:11:56 2001 @@ -3,7 +3,7 @@ /* * audio.c -- USB Audio Class driver * - * Copyright (C) 1999, 2000 + * Copyright (C) 1999, 2000, 2001 * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch) * @@ -92,9 +92,9 @@ * 2000-11-26: Thomas Sailer * Workaround for Dallas DS4201. The DS4201 uses PCM8 as format tag for * its 8 bit modes, but expects signed data (and should therefore have used PCM). - * 2001-04-08: gb - * Identify version on module load. - * + * 2001-03-10: Thomas Sailer + * provide abs function, prevent picking up a bogus kernel macro + * for abs. Bug report by Andrew Morton */ /* @@ -199,6 +199,20 @@ #define DRIVER_AUTHOR "Alan Cox , Thomas Sailer (sailer@ife.ee.ethz.ch)" #define DRIVER_DESC "USB Audio Class driver" +/* + * Version Information + */ +#define DRIVER_VERSION "v1.0.0" +#define DRIVER_AUTHOR "Alan Cox , Thomas Sailer (sailer@ife.ee.ethz.ch)" +#define DRIVER_DESC "USB Audio Class driver" + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.0.0" +#define DRIVER_AUTHOR "Alan Cox , Thomas Sailer (sailer@ife.ee.ethz.ch)" +#define DRIVER_DESC "USB Audio Class driver" + #define AUDIO_DEBUG 1 #define SND_DEV_DSP16 5 @@ -390,6 +404,17 @@ /* --------------------------------------------------------------------- */ +/* prevent picking up a bogus abs macro */ +#undef abs +extern inline int abs(int x) +{ + if (x < 0) + return -x; + return x; +} + +/* --------------------------------------------------------------------- */ + extern inline unsigned ld2(unsigned int x) { unsigned r = 0; @@ -3688,8 +3713,8 @@ return NULL; } ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buf, 8); - if (ret<0) { - printk(KERN_ERR "usbaudio: cannot get first 8 bytes of config descriptor %d of device %d\n", i, dev->devnum); + if (ret < 0) { + printk(KERN_ERR "usbaudio: cannot get first 8 bytes of config descriptor %d of device %d (error %d)\n", i, dev->devnum, ret); return NULL; } if (buf[1] != USB_DT_CONFIG || buf[0] < 9) { @@ -3702,7 +3727,7 @@ ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buffer, buflen); if (ret < 0) { kfree(buffer); - printk(KERN_ERR "usbaudio: cannot get config descriptor %d of device %d\n", i, dev->devnum); + printk(KERN_ERR "usbaudio: cannot get config descriptor %d of device %d (error %d)\n", i, dev->devnum, ret); return NULL; } return usb_audio_parsecontrol(dev, buffer, buflen, ifnum); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/devices.c linux.ac/drivers/usb/devices.c --- linux.vanilla/drivers/usb/devices.c Mon Sep 25 23:25:29 2000 +++ linux.ac/drivers/usb/devices.c Sun Apr 22 01:58:23 2001 @@ -384,7 +384,7 @@ int chix; int ret, cnt = 0; int parent_devnum = 0; - char *pages_start, *data_end; + char *pages_start, *data_end, *speed; unsigned int length; ssize_t total_written = 0; @@ -404,8 +404,21 @@ * So the root hub's parent is 0 and any device that is * plugged into the root hub has a parent of 0. */ - data_end = pages_start + sprintf(pages_start, format_topo, bus->busnum, level, parent_devnum, index, count, - usbdev->devnum, usbdev->slow ? "1.5" : "12 ", usbdev->maxchild); + switch (usbdev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; break; + case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */ + case USB_SPEED_FULL: + speed = "12 "; break; + case USB_SPEED_HIGH: + speed = "480"; break; + default: + speed = "?? "; + } + data_end = pages_start + sprintf(pages_start, format_topo, + bus->busnum, level, parent_devnum, + index, count, usbdev->devnum, + speed, usbdev->maxchild); /* * level = topology-tier level; * parent_devnum = parent device number; @@ -475,6 +488,7 @@ return -EFAULT; /* enumerate busses */ + read_lock_irq (&usb_bus_list_lock); for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) { /* print devices for this bus */ bus = list_entry(buslist, struct usb_bus, bus_list); @@ -484,6 +498,7 @@ return ret; total_written += ret; } + read_unlock_irq (&usb_bus_list_lock); return total_written; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/devio.c linux.ac/drivers/usb/devio.c --- linux.vanilla/drivers/usb/devio.c Mon Apr 30 15:13:27 2001 +++ linux.ac/drivers/usb/devio.c Sun Apr 22 01:58:23 2001 @@ -681,7 +681,7 @@ struct usbdevfs_connectinfo ci; ci.devnum = ps->dev->devnum; - ci.slow = ps->dev->slow; + ci.slow = ps->dev->speed == USB_SPEED_LOW; if (copy_to_user(arg, &ci, sizeof(ci))) return -EFAULT; return 0; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/dsbr100.c linux.ac/drivers/usb/dsbr100.c --- linux.vanilla/drivers/usb/dsbr100.c Sat May 26 16:53:14 2001 +++ linux.ac/drivers/usb/dsbr100.c Wed May 23 09:44:39 2001 @@ -85,6 +85,8 @@ static int usb_dsbr100_open(struct video_device *dev, int flags); static void usb_dsbr100_close(struct video_device *dev); +static int radio_nr = -1; +MODULE_PARM(radio_nr, "i"); typedef struct { struct urb readurb,writeurb; @@ -107,7 +109,6 @@ }; static int users = 0; -static int radio_nr = -1; static struct usb_device_id usb_dsbr100_table [] = { { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hid.c linux.ac/drivers/usb/hid.c --- linux.vanilla/drivers/usb/hid.c Sat May 26 16:53:14 2001 +++ linux.ac/drivers/usb/hid.c Wed May 23 00:13:41 2001 @@ -46,6 +46,7 @@ #include #include "hid.h" +#include #ifdef DEBUG #include "hid-debug.h" @@ -156,8 +157,14 @@ usage = parser->local.usage[0]; - if (type == HID_COLLECTION_APPLICATION && !parser->device->application) - parser->device->application = usage; + if (type == HID_COLLECTION_APPLICATION) { + if (IS_INPUT_APPLICATION(usage) && + !parser->device->input_application) { + parser->device->input_application = usage; + } + if (parser->device->maxapplication < HID_MAX_APPLICATIONS) + parser->device->application[parser->device->maxapplication++] = usage; + } if (parser->collection_stack_ptr < HID_COLLECTION_STACK_SIZE) { /* PUSH on stack */ struct hid_collection *collection = parser->collection_stack + parser->collection_stack_ptr++; @@ -249,6 +256,7 @@ return 0; field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL); field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL); + field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); for (i = 0; i < usages; i++) field->usage[i].hid = parser->local.usage[i]; field->maxusage = usages; field->flags = flags; @@ -755,7 +763,7 @@ usage->code = ((usage->hid - 1) & 0xf) + 0x100; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; - switch (device->application) { + switch (field->application) { case HID_GD_GAMEPAD: usage->code += 0x10; case HID_GD_JOYSTICK: usage->code += 0x10; case HID_GD_MOUSE: usage->code += 0x10; break; @@ -955,8 +963,17 @@ } } -static void hid_process_event(struct input_dev *input, int *quirks, struct hid_field *field, struct hid_usage *usage, __s32 value) +static void hid_process_event(struct hid_device *dev, struct hid_field *field, struct hid_usage *usage, __s32 value) { + struct input_dev *input = &dev->input; + int *quirks = &dev->quirks; + + if (dev->hiddev.private != NULL && + !IS_INPUT_APPLICATION(field->application)) { + hiddev_event(dev->hiddev.private, usage->hid, value); + return; + } + hid_dump_input(usage, value); if (usage->hat) { @@ -1042,21 +1059,19 @@ } else { if (value[n] == field->value[n]) continue; } - hid_process_event(&dev->input, &dev->quirks, field, &field->usage[n], value[n]); + hid_process_event(dev, field, &field->usage[n], value[n]); } else { if (field->value[n] >= min && field->value[n] <= max /* non-NULL value */ && field->usage[field->value[n] - min].hid /* nonzero usage */ && search(value, field->value[n], count)) - hid_process_event(&dev->input, &dev->quirks, field, - &field->usage[field->value[n] - min], 0); + hid_process_event(dev, field, &field->usage[field->value[n] - min], 0); if (value[n] >= min && value[n] <= max /* non-NULL value */ && field->usage[value[n] - min].hid /* nonzero usage */ && search(field->value, value[n], count)) - hid_process_event(&dev->input, &dev->quirks, - field, &field->usage[value[n] - min], 1); + hid_process_event(dev, field, &field->usage[value[n] - min], 1); } } @@ -1129,27 +1144,35 @@ * modules start with real values. This is especially important for joydev.c * automagic calibration. Doesn't work yet, though. Don't know why, the control * request just times out on most devices I have and returns nonsense on others. + * + * This code failed before because there's an extra byte for the report number + * if the reports are numbered! -- PJS */ static void hid_read_report(struct hid_device *hid, struct hid_report *report) { -#if 0 - int rlen = ((report->size - 1) >> 3) + 1; + int rlen = ((report->size - 1) >> 3) + 2; char rdata[rlen]; - struct urb urb; + char *data = rdata; int read, j; + int numbered = (hid->report_enum + report->type)->numbered; - memset(&urb, 0, sizeof(struct urb)); - memset(rdata, 0, rlen); + /* If the reports are not numbered, there won't be an extra byte + * returned in the get_report transfer. Otherwise, the first byte + * will be the report ID which should not be bassed to hid_input_field + */ + if (!numbered) { + rlen--; + } else { + data++; + } - urb.transfer_buffer = rdata; - urb.actual_length = rlen; - urb.context = hid; + memset(rdata, 0, rlen); dbg("getting report type %d id %d len %d", report->type + 1, report->id, rlen); - - if ((read = usb_get_report(hid->dev, hid->ifnum, report->type + 1, report->id, rdata, rlen)) != rlen) { - dbg("reading report failed rlen %d read %d", rlen, read); + if ((read = usb_get_report(hid->dev, hid->ifnum, report->type + 1, report->id, rdata, rlen)) != rlen || (numbered && rdata[0] != report->id)) { + dbg("reading report failed rlen %d read %d (id %d/%d)", + rlen, read, report->id, rdata[0]); #ifdef DEBUG printk(KERN_DEBUG __FILE__ ": report = "); for (j = 0; j < rlen; j++) printk(" %02x", rdata[j]); @@ -1158,8 +1181,31 @@ return; } - hid_irq(&urb); -#endif + for (j = 0; j < report->maxfield; j++) + hid_input_field(hid, report->field[j], data); +} + +/* + * Initialize all readable reports + */ +static void hid_read_all_reports(struct hid_device *hid) +{ + int i; + struct hid_report *report; + struct hid_report_enum *report_enum; + struct list_head *list; + + for (i = 0; i < HID_REPORT_TYPES; i++) { + if (i == HID_FEATURE_REPORT || i == HID_INPUT_REPORT) { + report_enum = hid->report_enum + i; + list = report_enum->report_list.next; + while (list != &report_enum->report_list) { + report = (struct hid_report *) list; + hid_read_report(hid, report); + list = list->next; + } + } + } } /* @@ -1190,11 +1236,11 @@ unsigned n; for (n = 0; n < report->maxfield; n++) hid_output_field(report->field[n], data); -}; +} /* * Set a field value. The report this field belongs to has to be - * created and transfered to the device, to set this value in the + * created and transferred to the device, to set this value in the * device. */ @@ -1270,6 +1316,22 @@ hid_submit_out(hid); } +static void hid_write_report(struct hid_device *hid, struct hid_report *report) +{ + hid_output_report(report, hid->out[hid->outhead].buffer); + + hid->out[hid->outhead].dr.value = cpu_to_le16(0x200 | report->id); + hid->out[hid->outhead].dr.length = cpu_to_le16((report->size + 7) >> 3); + + hid->outhead = (hid->outhead + 1) & (HID_CONTROL_FIFO_SIZE - 1); + + if (hid->outhead == hid->outtail) + hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1); + + if (hid->urbout.status != -EINPROGRESS) + hid_submit_out(hid); +} + static int hid_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct hid_device *hid = dev->private; @@ -1282,18 +1344,7 @@ } hid_set_field(field, offset, value); - hid_output_report(field->report, hid->out[hid->outhead].buffer); - - hid->out[hid->outhead].dr.value = cpu_to_le16(0x200 | field->report->id); - hid->out[hid->outhead].dr.length = cpu_to_le16((field->report->size + 7) >> 3); - - hid->outhead = (hid->outhead + 1) & (HID_CONTROL_FIFO_SIZE - 1); - - if (hid->outhead == hid->outtail) - hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1); - - if (hid->urbout.status != -EINPROGRESS) - hid_submit_out(hid); + hid_write_report(hid, field->report); return 0; } @@ -1321,6 +1372,21 @@ usb_unlink_urb(&hid->urb); } +static void hid_init_hiddev(struct hid_device *hid) +{ + int i; + + for (i = 0; i < hid->maxapplication; i++) { + if (!IS_INPUT_APPLICATION(hid->application[i])) { + hid->hiddev.read_report = hid_read_report; + hid->hiddev.read_all_reports = hid_read_all_reports; + hid->hiddev.write_report = hid_write_report; + hid->hiddev.private = hiddev_connect(hid); + return; + } + } +} + /* * Configure the input layer interface * Read all reports and initalize the absoulte field values. @@ -1332,6 +1398,14 @@ struct list_head *list; int i, j, k; + if (hid->input_application == 0) return; + + hid->input.name = hid->name; + hid->input.idbus = BUS_USB; + hid->input.idvendor = hid->dev->descriptor.idVendor; + hid->input.idproduct = hid->dev->descriptor.idProduct; + hid->input.idversion = hid->dev->descriptor.bcdDevice; + hid->input.private = hid; hid->input.event = hid_event; hid->input.open = hid_open; @@ -1349,15 +1423,32 @@ list = list->next; for (i = 0; i < report->maxfield; i++) - for (j = 0; j < report->field[i]->maxusage; j++) - hid_configure_usage(hid, report->field[i], report->field[i]->usage + j); + if (IS_INPUT_APPLICATION(report->field[i]->application)) + for (j = 0; j < report->field[i]->maxusage; j++) + hid_configure_usage(hid, report->field[i], report->field[i]->usage + j); if (k == HID_INPUT_REPORT) { usb_set_idle(hid->dev, hid->ifnum, 0, report->id); - hid_read_report(hid, report); + /* Commented out since Vojtech has had trouble with this function + * hid_read_report(hid, report); */ } } } + + input_register_device(&hid->input); + printk(KERN_INFO "input%d: USB HID v%x.%02x %s", + hid->input.number, + hid->version >> 8, hid->version & 0xff, + ((hid->input_application >= 0x00010000) && + (hid->input_application <= 0x00010008)) ? + hid_types[hid->input_application & 0xffff] : "Device"); + if (strlen(hid->name)) + printk(" [%s]", hid->name); + else + printk(" [%04x:%04x]", hid->input.idvendor, + hid->input.idproduct); + printk(" on usb%d:%d.%d\n", hid->dev->bus->busnum, hid->dev->devnum, + hid->ifnum); } #define USB_VENDOR_ID_WACOM 0x056a @@ -1461,19 +1552,14 @@ hid->out[n].dr.index = cpu_to_le16(hid->ifnum); } - hid->input.name = hid->name; - hid->input.idbus = BUS_USB; - hid->input.idvendor = dev->descriptor.idVendor; - hid->input.idproduct = dev->descriptor.idProduct; - hid->input.idversion = dev->descriptor.bcdDevice; - if (strlen(name)) strcpy(hid->name, name); else sprintf(hid->name, "USB HID %s %04x:%04x", - ((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ? - hid_types[hid->application & 0xffff] : "Device", - hid->input.idvendor, hid->input.idproduct); + ((hid->input_application >= 0x00010000) && (hid->input_application <= 0x00010008)) ? + hid_types[hid->input_application & 0xffff] : "Device", + hid->dev->descriptor.idVendor, + hid->dev->descriptor.idProduct); FILL_CONTROL_URB(&hid->urbout, dev, usb_sndctrlpipe(dev, 0), (void*) &hid->out[0].dr, hid->out[0].buffer, 1, hid_ctrl, hid); @@ -1512,21 +1598,8 @@ hid_dump_device(hid); + hid_init_hiddev(hid); hid_init_input(hid); - input_register_device(&hid->input); - - printk(KERN_INFO "input%d: USB HID v%x.%02x %s", - hid->input.number, - hid->version >> 8, hid->version & 0xff, - ((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ? - hid_types[hid->application & 0xffff] : "Device"); - - if (strlen(name)) - printk(" [%s]", name); - else - printk(" [%04x:%04x]", hid->input.idvendor, hid->input.idproduct); - - printk(" on usb%d:%d.%d\n", dev->bus->busnum, dev->devnum, ifnum); return hid; } @@ -1537,7 +1610,12 @@ dbg("cleanup called"); usb_unlink_urb(&hid->urb); - input_unregister_device(&hid->input); + if (hid->hiddev.private != NULL) { + hiddev_disconnect(hid->hiddev.private); + } + if (hid->input.private != NULL) { + input_unregister_device(&hid->input); + } hid_free_device(hid); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hid.h linux.ac/drivers/usb/hid.h --- linux.vanilla/drivers/usb/hid.h Mon Apr 30 15:13:27 2001 +++ linux.ac/drivers/usb/hid.h Sat May 26 18:16:55 2001 @@ -210,6 +210,7 @@ #define HID_MAX_DESCRIPTOR_SIZE 4096 #define HID_MAX_USAGES 1024 +#define HID_MAX_APPLICATIONS 4 struct hid_local { unsigned usage[HID_MAX_USAGES]; /* usage array */ @@ -239,6 +240,7 @@ struct hid_field { unsigned physical; /* physical usage for this field */ unsigned logical; /* logical usage for this field */ + unsigned application; /* application usage for this field */ struct hid_usage *usage; /* usage table for this function */ unsigned maxusage; /* maximum usage index */ unsigned flags; /* main-item flags (i.e. volatile,array,constant) */ @@ -284,10 +286,19 @@ char buffer[HID_BUFFER_SIZE]; }; +struct hid_iodev { + void *private; /* hiddev opaque device structure */ + void (*read_report)(struct hid_device *, struct hid_report *); + void (*read_all_reports)(struct hid_device *); + void (*write_report)(struct hid_device *, struct hid_report *); +}; + struct hid_device { /* device report descriptor */ __u8 *rdesc; unsigned rsize; - unsigned application; /* HID application, i.e. Digitizer */ + unsigned input_application; /* HID application, i.e. Digitizer */ + unsigned application[HID_MAX_APPLICATIONS]; /* List of all supported applications */ + unsigned maxapplication; unsigned version; /* HID version */ unsigned country; /* HID country */ struct hid_report_enum report_enum[HID_REPORT_TYPES]; @@ -303,6 +314,7 @@ unsigned char outhead, outtail; /* Tx buffer head & tail */ struct input_dev input; /* input device structure */ + struct hid_iodev hiddev; int open; /* is the device open by input? */ int quirks; /* Various nasty tricks the device can pull on us */ char name[128]; /* Device name */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hiddev.c linux.ac/drivers/usb/hiddev.c --- linux.vanilla/drivers/usb/hiddev.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/hiddev.c Thu May 17 20:27:16 2001 @@ -0,0 +1,705 @@ +/* + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * HID (non-input) char devices, giving access to raw HID device events. + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#define HIDDEV_MINOR_BASE 96 +#define HIDDEV_MINORS 16 +#define HIDDEV_BUFFER_SIZE 64 + +#include +#include +#include +#include +#include +#include +#define DEBUG /* since this is experimental */ +#include +#include "hid.h" +#include + +struct hiddev { + int exist; + int open; + int minor; + wait_queue_head_t wait; + devfs_handle_t devfs; + struct hid_device *hid; + struct hiddev_list *list; +}; + +struct hiddev_list { + struct hiddev_event buffer[HIDDEV_BUFFER_SIZE]; + int head; + int tail; + struct fasync_struct *fasync; + struct hiddev *hiddev; + struct hiddev_list *next; +}; + +static struct hiddev *hiddev_table[HIDDEV_MINORS] = { NULL, /* ... */ }; +static devfs_handle_t hiddev_devfs_handle; + +/* + * Find a report, given the report's type and ID. The ID can be specified + * indirectly by REPORT_ID_FIRST (which returns the first report of the given + * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the + * given type which follows old_id. + */ +static struct hid_report * +hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) +{ + struct hid_report_enum *report_enum; + struct list_head *list; + + if (rinfo->report_type < HID_REPORT_TYPE_MIN || + rinfo->report_type > HID_REPORT_TYPE_MAX) return NULL; + + report_enum = hid->report_enum + + (rinfo->report_type - HID_REPORT_TYPE_MIN); + if ((rinfo->report_id & ~HID_REPORT_ID_MASK) != 0) { + switch (rinfo->report_id & ~HID_REPORT_ID_MASK) { + case HID_REPORT_ID_FIRST: + list = report_enum->report_list.next; + if (list == &report_enum->report_list) return NULL; + rinfo->report_id = ((struct hid_report *) list)->id; + break; + + case HID_REPORT_ID_NEXT: + list = (struct list_head *) + report_enum->report_id_hash[rinfo->report_id & + HID_REPORT_ID_MASK]; + if (list == NULL) return NULL; + list = list->next; + if (list == &report_enum->report_list) return NULL; + rinfo->report_id = ((struct hid_report *) list)->id; + break; + + default: + return NULL; + } + } + + return report_enum->report_id_hash[rinfo->report_id]; +} + +/* + * Perform an exhaustive search of the report table for a usage, given its + * type and usage id. + */ +static struct hid_field * +hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref) +{ + int i, j; + struct hid_report *report; + struct hid_report_enum *report_enum; + struct list_head *list; + struct hid_field *field; + + if (uref->report_type < HID_REPORT_TYPE_MIN || + uref->report_type > HID_REPORT_TYPE_MAX) return NULL; + + report_enum = hid->report_enum + + (uref->report_type - HID_REPORT_TYPE_MIN); + list = report_enum->report_list.next; + while (list != &report_enum->report_list) { + report = (struct hid_report *) list; + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + for (j = 0; j < field->maxusage; j++) { + if (field->usage[j].hid == uref->usage_code) { + uref->report_id = report->id; + uref->field_index = i; + uref->usage_index = j; + return field; + } + } + } + list = list->next; + } + + return NULL; +} + +/* + * This is where hid.c calls into hiddev to pass an event that occurred over + * the interrupt pipe + */ +void hiddev_event(void *private, unsigned int usage, int value) +{ + struct hiddev *hiddev = private; + struct hiddev_list *list = hiddev->list; + + while (list) { + list->buffer[list->head].hid = usage; + list->buffer[list->head].value = value; + list->head = (list->head + 1) & (HIDDEV_BUFFER_SIZE - 1); + + kill_fasync(&list->fasync, SIGIO, POLL_IN); + + list = list->next; + } + + wake_up_interruptible(&hiddev->wait); +} + +/* + * fasync file op + */ +static int hiddev_fasync(int fd, struct file *file, int on) +{ + int retval; + struct hiddev_list *list = file->private_data; + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +/* + * If there are no more readers, deregister interest in interrupt events + */ +static void hiddev_close(struct hiddev *hiddev) +{ + struct hid_device *hid = hiddev->hid; + if (!--hid->open) usb_unlink_urb(&hid->urb); +} + +/* + * De-allocate a hiddev structure + */ +static void hiddev_cleanup(struct hiddev *hiddev) +{ + devfs_unregister(hiddev->devfs); + hiddev_table[hiddev->minor] = NULL; + kfree(hiddev); +} + +/* + * release file op + */ +static int hiddev_release(struct inode * inode, struct file * file) +{ + struct hiddev_list *list = file->private_data; + struct hiddev_list **listptr; + + lock_kernel(); + listptr = &list->hiddev->list; + hiddev_fasync(-1, file, 0); + + while (*listptr && (*listptr != list)) + listptr = &((*listptr)->next); + *listptr = (*listptr)->next; + + if (!--list->hiddev->open) { + if (list->hiddev->exist) { + hiddev_close(list->hiddev); + } else { + hiddev_cleanup(list->hiddev); + } + } + + kfree(list); + unlock_kernel(); + + return 0; +} + +/* + * open file op + */ +static int hiddev_open(struct inode * inode, struct file * file) { + struct hiddev_list *list; + struct hid_device *hid; + + int i = MINOR(inode->i_rdev) - HIDDEV_MINOR_BASE; + + if (i >= HIDDEV_MINORS || !hiddev_table[i]) + return -ENODEV; + + if (!(list = kmalloc(sizeof(struct hiddev_list), GFP_KERNEL))) + return -ENOMEM; + memset(list, 0, sizeof(struct hiddev_list)); + + list->hiddev = hiddev_table[i]; + list->next = hiddev_table[i]->list; + hiddev_table[i]->list = list; + + file->private_data = list; + + if (!list->hiddev->open++) + if (list->hiddev->exist) { + hid = hiddev_table[i]->hid; + if (!hid->open++) { + hid->urb.dev = hid->dev; + if (usb_submit_urb(&hid->urb)) return -EIO; + } + } + + return 0; +} + +/* + * "write" file op + */ +static ssize_t hiddev_write(struct file * file, const char * buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +/* + * "read" file op + */ +static ssize_t hiddev_read(struct file * file, char * buffer, size_t count, + loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct hiddev_list *list = file->private_data; + int retval = 0; + + if (list->head == list->tail) { + + add_wait_queue(&list->hiddev->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while (list->head == list->tail) { + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + if (!list->hiddev->exist) { + retval = -EIO; + break; + } + + schedule(); + } + + current->state = TASK_RUNNING; + remove_wait_queue(&list->hiddev->wait, &wait); + } + + if (retval) + return retval; + + while (list->head != list->tail && retval + sizeof(struct hiddev_event) <= count) { + if (copy_to_user(buffer + retval, list->buffer + list->tail, + sizeof(struct hiddev_event))) return -EFAULT; + list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1); + retval += sizeof(struct hiddev_event); + } + + return retval; +} + +/* + * "poll" file op + * No kernel lock - fine + */ +static unsigned int hiddev_poll(struct file *file, poll_table *wait) +{ + struct hiddev_list *list = file->private_data; + poll_wait(file, &list->hiddev->wait, wait); + if (list->head != list->tail) + return POLLIN | POLLRDNORM; + if (!list->hiddev->exist) + return POLLERR | POLLHUP; + return 0; +} + +#define GET_TIMEOUT 3 +#define SET_TIMEOUT 3 + +/* + * "ioctl" file op + */ +static int hiddev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct hiddev_list *list = file->private_data; + struct hiddev *hiddev = list->hiddev; + struct hid_device *hid = hiddev->hid; + struct usb_device *dev = hid->dev; + struct hiddev_report_info rinfo; + struct hiddev_usage_ref uref; + struct hid_report *report; + struct hid_field *field; + + if (!hiddev->exist) return -EIO; + + switch (cmd) { + + case HIDIOCGVERSION: + return put_user(HID_VERSION, (int *) arg); + + case HIDIOCAPPLICATION: + if (arg < 0 || arg >= hid->maxapplication) + return -EINVAL; + return hid->application[arg]; + + case HIDIOCGDEVINFO: + { + struct hiddev_devinfo dinfo; + dinfo.bustype = BUS_USB; + dinfo.busnum = dev->bus->busnum; + dinfo.devnum = dev->devnum; + dinfo.ifnum = hid->ifnum; + dinfo.vendor = dev->descriptor.idVendor; + dinfo.product = dev->descriptor.idProduct; + dinfo.version = dev->descriptor.bcdDevice; + dinfo.num_applications = hid->maxapplication; + return copy_to_user((void *) arg, &dinfo, sizeof(dinfo)); + } + + case HIDIOCGSTRING: + { + int idx, len; + char *buf; + + if (get_user(idx, (int *) arg)) + return -EFAULT; + + if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL) + return -ENOMEM; + + if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) { + kfree(buf); + return -EINVAL; + } + + if (copy_to_user((void *) (arg+sizeof(int)), buf, len+1)) { + kfree(buf); + return -EFAULT; + } + + kfree(buf); + + return len; + } + + case HIDIOCINITREPORT: + (*hid->hiddev.read_all_reports)(hid); + + return 0; + + case HIDIOCGREPORT: + if (copy_from_user(&rinfo, (void *) arg, sizeof(rinfo))) + return -EFAULT; + + if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT) + return -EINVAL; + + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + (*hid->hiddev.read_report)(hid, report); + + return 0; + + case HIDIOCSREPORT: + if (copy_from_user(&rinfo, (void *) arg, sizeof(rinfo))) + return -EFAULT; + + if (rinfo.report_type == HID_REPORT_TYPE_INPUT) + return -EINVAL; + + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + (*hid->hiddev.write_report)(hid, report); + + return 0; + + case HIDIOCGREPORTINFO: + if (copy_from_user(&rinfo, (void *) arg, sizeof(rinfo))) + return -EFAULT; + + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + rinfo.num_fields = report->maxfield; + + return copy_to_user((void *) arg, &rinfo, sizeof(rinfo)); + + case HIDIOCGFIELDINFO: + { + struct hiddev_field_info finfo; + if (copy_from_user(&finfo, (void *) arg, sizeof(finfo))) + return -EFAULT; + rinfo.report_type = finfo.report_type; + rinfo.report_id = finfo.report_id; + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + if (finfo.field_index >= report->maxfield) + return -EINVAL; + + field = report->field[finfo.field_index]; + memset(&finfo, 0, sizeof(finfo)); + finfo.report_type = rinfo.report_type; + finfo.report_id = rinfo.report_id; + finfo.field_index = field->report_count - 1; + finfo.maxusage = field->maxusage; + finfo.flags = field->flags; + finfo.physical = field->physical; + finfo.logical = field->logical; + finfo.application = field->application; + finfo.logical_minimum = field->logical_minimum; + finfo.logical_maximum = field->logical_maximum; + finfo.physical_minimum = field->physical_minimum; + finfo.physical_maximum = field->physical_maximum; + finfo.unit_exponent = field->unit_exponent; + finfo.unit = field->unit; + + return copy_to_user((void *) arg, &finfo, sizeof(finfo)); + } + + case HIDIOCGUCODE: + if (copy_from_user(&uref, (void *) arg, sizeof(uref))) + return -EFAULT; + + rinfo.report_type = uref.report_type; + rinfo.report_id = uref.report_id; + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + if (uref.field_index >= report->maxfield) + return -EINVAL; + + field = report->field[uref.field_index]; + if (uref.usage_index >= field->maxusage) + return -EINVAL; + + uref.usage_code = field->usage[uref.usage_index].hid; + + return copy_to_user((void *) arg, &uref, sizeof(uref)); + + case HIDIOCGUSAGE: + if (copy_from_user(&uref, (void *) arg, sizeof(uref))) + return -EFAULT; + + if (uref.report_id == HID_REPORT_ID_UNKNOWN) { + field = hiddev_lookup_usage(hid, &uref); + if (field == NULL) + return -EINVAL; + } else { + rinfo.report_type = uref.report_type; + rinfo.report_id = uref.report_id; + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + if (uref.field_index >= report->maxfield) + return -EINVAL; + + field = report->field[uref.field_index]; + if (uref.usage_index >= field->maxusage) + return -EINVAL; + } + + uref.value = field->value[uref.usage_index]; + + return copy_to_user((void *) arg, &uref, sizeof(uref)); + + case HIDIOCSUSAGE: + if (copy_from_user(&uref, (void *) arg, sizeof(uref))) + return -EFAULT; + + if (uref.report_type == HID_REPORT_TYPE_INPUT) + return -EINVAL; + + if (uref.report_id == HID_REPORT_ID_UNKNOWN) { + field = hiddev_lookup_usage(hid, &uref); + if (field == NULL) + return -EINVAL; + } else { + rinfo.report_type = uref.report_type; + rinfo.report_id = uref.report_id; + if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) + return -EINVAL; + + if (uref.field_index >= report->maxfield) + return -EINVAL; + + field = report->field[uref.field_index]; + if (uref.usage_index >= field->maxusage) + return -EINVAL; + } + + field->value[uref.usage_index] = uref.value; + + return 0; + + default: + + if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) + return -EINVAL; + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) { + int len; + if (!hid->name) return 0; + len = strlen(hid->name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user((char *) arg, hid->name, len) ? + -EFAULT : len; + } + } + return -EINVAL; +} + +static struct file_operations hiddev_fops = { + owner: THIS_MODULE, + read: hiddev_read, + write: hiddev_write, + poll: hiddev_poll, + open: hiddev_open, + release: hiddev_release, + ioctl: hiddev_ioctl, + fasync: hiddev_fasync, +}; + +/* + * This is where hid.c calls us to connect a hid device to the hiddev driver + */ +void *hiddev_connect(struct hid_device *hid) +{ + struct hiddev *hiddev; + int minor; + char devfs_name[16]; + + for (minor = 0; minor < HIDDEV_MINORS && hiddev_table[minor]; minor++); + if (minor == HIDDEV_MINORS) { + printk(KERN_ERR "hiddev: no more free hiddev devices\n"); + return NULL; + } + + if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL))) + return NULL; + memset(hiddev, 0, sizeof(struct hiddev)); + + init_waitqueue_head(&hiddev->wait); + + hiddev->minor = minor; + hiddev_table[minor] = hiddev; + + hiddev->hid = hid; + hiddev->exist = 1; + + sprintf(devfs_name, "hiddev%d", minor); + hiddev->devfs = devfs_register(hiddev_devfs_handle, devfs_name, + DEVFS_FL_DEFAULT, USB_MAJOR, + minor + HIDDEV_MINOR_BASE, + S_IFCHR | S_IRUGO | S_IWUSR, + &hiddev_fops, NULL); + + printk(KERN_INFO "%s: USB HID v%x.%02x", devfs_name, + hid->version >> 8, hid->version & 0xff); + if (strlen(hid->name)) + printk(" [%s]", hid->name); + else + printk(" [%04x:%04x]", hid->dev->descriptor.idVendor, + hid->dev->descriptor.idProduct); + printk(" on usb%d:%d.%d\n", hid->dev->bus->busnum, hid->dev->devnum, + hid->ifnum); + + return hiddev; +} + +/* + * This is where hid.c calls us to disconnect a hiddev device from the + * corresponding hid device (usually because the usb device has disconnected) + */ +void hiddev_disconnect(void *private) +{ + struct hiddev *hiddev = private; + + hiddev->exist = 0; + + if (hiddev->open) { + hiddev_close(hiddev); + wake_up_interruptible(&hiddev->wait); + } else { + hiddev_cleanup(hiddev); + } +} + +/* Currently this driver is a USB driver. It's not a conventional one in + * the sense that it doesn't probe at the USB level. Instead it waits to + * be connected by HID through the hiddev_connect / hiddev_disconnect + * routines. The reason to register as a USB device is to gain part of the + * minor number space from the USB major. + * + * In theory, should the HID code be generalized to more than one physical + * medium (say, IEEE 1384), this driver will probably need to register its + * own major number, and in doing so, no longer need to register with USB. + * At that point the probe routine and hiddev_driver struct below will no + * longer be useful. + */ + + +/* We never attach in this manner, and rely on HID to connect us. This + * is why there is no disconnect routine defined in the usb_driver either. + */ +static void *hiddev_usbd_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *hiddev_info) +{ + return NULL; +} + + +static /* const */ struct usb_driver hiddev_driver = { + name: "hiddev", + probe: hiddev_usbd_probe, + fops: &hiddev_fops, + minor: HIDDEV_MINOR_BASE +}; + +static int __init hiddev_init(void) +{ + hiddev_devfs_handle = + devfs_mk_dir(devfs_find_handle(NULL, "usb", 0, 0, 0, 0), "hid", NULL); + usb_register(&hiddev_driver); + return 0; +} + +static void __exit hiddev_exit(void) +{ + devfs_unregister(hiddev_devfs_handle); + usb_deregister(&hiddev_driver); +} + +module_init(hiddev_init); +module_exit(hiddev_exit); + +MODULE_AUTHOR("Paul Stewart "); +MODULE_DESCRIPTION("HID device control interface"); + +EXPORT_SYMBOL(hiddev_event); +EXPORT_SYMBOL(hiddev_connect); +EXPORT_SYMBOL(hiddev_disconnect); + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hp5300.c linux.ac/drivers/usb/hp5300.c --- linux.vanilla/drivers/usb/hp5300.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/hp5300.c Sun Apr 22 01:58:23 2001 @@ -0,0 +1,1088 @@ +/* a driver to encapsulate SCSI in USB +* + * (C) Copyright 2000 John Fremlin + * (C) Copyright 2000 Oliver Neukum + * (C) Copyright 2000 Jeremy Hall + * + * Parts shamelessly stolen from usb-storage and microtek and copyright by their + * authors. Thanks to Matt Dharm for giving us permission! + * + * This driver implements a SCSI host controller driver and a USB + * device driver. To avoid confusion, all the USB related stuff is + * prefixed by usc_usb_ and all the SCSI stuff by usc_scsi_. + * + * Guessed protocol: + * + * Send raw SCSI command to EP 0x1 + * + * If there is data to receive: + * If the command was READ datatype=image: + * Read a lot of data from EP 0x83 + * Else: + * Read data from EP 0x82 + * Else: + * If there is data to transmit: + * Write it to EP 0x1 + * + * Read status byte from EP 0x82 + * + * References: + * + * The SCSI command set for the scanner is available from + * ftp://ftp.microtek.com/microtek/devpack/ + * + * We'll fix the comments later + * 20001006 16:53 EDT work begins + * 20001007 00:58:35 first test run + * 20001008 02:24:40 FIRST IMAGE SCANNED! + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../scsi/scsi.h" +#include "../scsi/hosts.h" +#include "../scsi/sd.h" + +#include "hp5300.h" + +/* Constants */ + +#define USC_ABORT_TIMEOUT HZ /*jiffies*/ + + +/* Should we do debugging? */ + +#define USC_DO_DEBUG + + +static struct usb_device_id hp5300_usb_id [] = +{ + { USB_DEVICE(0x3f0, 0x0701) }, + { } +}; + + +/* USB layer driver interface */ + +static void *usc_usb_probe(struct usb_device *dev, unsigned int interface, const struct usb_device_id *id); +static void usc_usb_disconnect(struct usb_device *dev, void *ptr); + +static struct usb_driver usc_usb_driver = { + name: "hp5300", + probe: usc_usb_probe, + disconnect: usc_usb_disconnect, + id_table: hp5300_usb_id, +}; + + +/* Internal driver stuff */ + +#define USC_VERSION "0.0.1" +#define USC_NAME "USB-SCSI: " + +#define USC_WARNING(x...) \ + printk( KERN_WARNING USC_NAME x ) +#define USC_ERROR(x...) \ + printk( KERN_ERR USC_NAME x ) +#define USC_INT_ERROR(x...) \ + USC_ERROR(x) +#define USC_MESSAGE(x...) \ + printk( KERN_INFO USC_NAME x ) + +#if defined USC_DO_DEBUG + +#define USC_DEBUG(x...) \ + printk( KERN_DEBUG USC_NAME x ) + +#define USC_DEBUG_GOT_HERE() \ + USC_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __PRETTY_FUNCTION__ ) +#define USC_DEBUG_INT() \ + do { USC_DEBUG_GOT_HERE(); \ + USC_DEBUG("transfer = %x context = %x\n",(int)transfer,(int)context ); \ + USC_DEBUG("transfer->status = %x data-length = %x sent = %x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \ + usc_debug_dump(context->instance);\ + } while(0) +#else + +#define USC_NUL_STATEMENT do { } while(0) + +#define USC_DEBUG(x...) USC_NUL_STATEMENT +#define USC_DEBUG_GOT_HERE() USC_NUL_STATEMENT +#define USC_DEBUG_INT() USC_NUL_STATEMENT +#define USC_DEBUG_HERE USC_NUL_STATEMENT + +#endif + + +static void usc_transfer_cleanup( struct urb *transfer ); + +#define USC_INT_INIT()\ + do {\ + context = (struct usc_transfer_context*)transfer->context; \ + if (atomic_read(&context->do_abort)) {\ + usc_transfer_cleanup(transfer);\ + return;\ + }\ + USC_DEBUG_INT();\ + } while (0) + +static inline void usc_debug_dump(struct usc_desc* desc) { + USC_DEBUG("desc at 0x%x: halted = %x%x, toggle = %x%x\n", + (int)desc,(int)desc->usb_dev->halted[1],(int)desc->usb_dev->halted[0], + (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0] + ); + USC_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n", + desc->out_pipe, + desc->status_pipe, + desc->image_pipe + ); +} + + +static inline void usc_show_command(Scsi_Cmnd *srb) +{ + char *what = NULL; + + switch (srb->cmnd[0]) { + case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break; + case REZERO_UNIT: what = "REZERO_UNIT"; break; + case REQUEST_SENSE: what = "REQUEST_SENSE"; break; + case FORMAT_UNIT: what = "FORMAT_UNIT"; break; + case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break; + case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break; + case READ_6: what = "READ_6"; break; + case WRITE_6: what = "WRITE_6"; break; + case SEEK_6: what = "SEEK_6"; break; + case READ_REVERSE: what = "READ_REVERSE"; break; + case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break; + case SPACE: what = "SPACE"; break; + case INQUIRY: what = "INQUIRY"; break; + case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break; + case MODE_SELECT: what = "MODE_SELECT"; break; + case RESERVE: what = "RESERVE"; break; + case RELEASE: what = "RELEASE"; break; + case COPY: what = "COPY"; break; + case ERASE: what = "ERASE"; break; + case MODE_SENSE: what = "MODE_SENSE"; break; + case START_STOP: what = "START_STOP"; break; + case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break; + case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break; + case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break; + case SET_WINDOW: what = "SET_WINDOW"; break; + case READ_CAPACITY: what = "READ_CAPACITY"; break; + case READ_10: what = "READ_10"; break; + case WRITE_10: what = "WRITE_10"; break; + case SEEK_10: what = "SEEK_10"; break; + case WRITE_VERIFY: what = "WRITE_VERIFY"; break; + case VERIFY: what = "VERIFY"; break; + case SEARCH_HIGH: what = "SEARCH_HIGH"; break; + case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break; + case SEARCH_LOW: what = "SEARCH_LOW"; break; + case SET_LIMITS: what = "SET_LIMITS"; break; + case READ_POSITION: what = "READ_POSITION"; break; + case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break; + case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break; + case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break; + case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break; + case COMPARE: what = "COMPARE"; break; + case COPY_VERIFY: what = "COPY_VERIFY"; break; + case WRITE_BUFFER: what = "WRITE_BUFFER"; break; + case READ_BUFFER: what = "READ_BUFFER"; break; + case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break; + case READ_LONG: what = "READ_LONG"; break; + case WRITE_LONG: what = "WRITE_LONG"; break; + case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break; + case WRITE_SAME: what = "WRITE_SAME"; break; + case READ_TOC: what = "READ_TOC"; break; + case LOG_SELECT: what = "LOG_SELECT"; break; + case LOG_SENSE: what = "LOG_SENSE"; break; + case MODE_SELECT_10: what = "MODE_SELECT_10"; break; + case MODE_SENSE_10: what = "MODE_SENSE_10"; break; + case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break; + case READ_12: what = "READ_12"; break; + case WRITE_12: what = "WRITE_12"; break; + case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break; + case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break; + case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break; + case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break; + case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; + case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; + case WRITE_LONG_2: what = "WRITE_LONG_2"; break; + default: + USC_DEBUG("can't decode command\n"); + goto out; + break; + } + USC_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len); + + out: + USC_DEBUG( " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5], + srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]); +} + +static inline int usc_is_aborting(struct usc_desc* desc) { + return (atomic_read(&desc->context.do_abort)); +} + +static inline void usc_request_abort(struct usc_desc* desc, struct urb *urb) { + USC_DEBUG_GOT_HERE(); + usc_debug_dump(desc); + atomic_set(&desc->context.do_abort,1); +} + +static inline void usc_urb_abort(struct usc_desc* desc, struct urb* urb) { + USC_DEBUG_GOT_HERE(); + usc_debug_dump(desc); + if ( urb->status == USB_ST_URB_PENDING ) { + usb_unlink_urb( urb ); + } +} + +static inline void usc_wait_abort(struct usc_desc* desc, struct urb *urb, int which_urb) +{ + usc_request_abort(desc, urb); + + if (which_urb) { + while( !atomic_read(&desc->status_lock.count) ) { +/* Is there a function to check if the semaphore is locked? */ + schedule_timeout( USC_ABORT_TIMEOUT ); + USC_DEBUG_GOT_HERE(); + usc_urb_abort(desc, urb); + } + } else { + while( !atomic_read(&desc->cmd_lock.count) ) { +/* Is there a function to check if the semaphore is locked? */ + schedule_timeout( USC_ABORT_TIMEOUT ); + USC_DEBUG_GOT_HERE(); + usc_urb_abort(desc, urb); + } + } + +} + + +static struct usc_desc * usc_list; /* list of active scanners */ +struct semaphore usc_list_semaphore; + +/* Internal list operations */ + +static +void usc_remove_nolock( struct usc_desc* to_remove ) +{ + USC_DEBUG( "removing 0x%x from list\n", + (int)to_remove ); + + USC_DEBUG("line %d: locking kernel!\n",__LINE__); + lock_kernel(); + USC_DEBUG("line %d: trying to abort cmd_urb.\n",__LINE__); + usc_wait_abort(to_remove, &to_remove->cmd_urb, 0); + USC_DEBUG("line %d: Trying to abort status urb.\n",__LINE__); +up(&to_remove->status_lock); +// usc_wait_abort(to_remove, &to_remove->status_urb, 1); +USC_DEBUG("line %d: unlinking status_urb which currently has status 0x%x\n",__LINE__,to_remove->status_urb.status); + usb_unlink_urb(&to_remove->status_urb); + USC_DEBUG("Finished aborting my urbs!\n"); + + USC_DEBUG_GOT_HERE(); + + if ( to_remove != usc_list ) { + USC_DEBUG_GOT_HERE(); + if (to_remove->prev && to_remove->next) + to_remove->prev->next = to_remove->next; + } else { + USC_DEBUG_GOT_HERE(); + usc_list = to_remove->next; + if (usc_list) { + USC_DEBUG_GOT_HERE(); + usc_list->prev = 0; + } + } + + if ( to_remove->next ) { + USC_DEBUG_GOT_HERE(); + to_remove->next->prev = to_remove->prev; + } + + USC_DEBUG_GOT_HERE(); + scsi_unregister_module(MODULE_SCSI_HA, &(to_remove->ctempl)); + USC_DEBUG_GOT_HERE(); + unlock_kernel(); + USC_DEBUG_GOT_HERE(); + + kfree( to_remove ); + USC_DEBUG_GOT_HERE(); +} + +static +void usc_add_nolock( struct usc_desc* to_add ) +{ + USC_DEBUG( "adding 0x%x to list\n", (int)to_add ); + + to_add->prev = 0; + to_add->next = usc_list; + if ( usc_list ) { + usc_list->prev = to_add; + } + + usc_list = to_add; +} + + +static void usc_get_status( struct usc_desc *desc, struct urb *status_transfer, int pipe, void *status, int want_init ); + + +/* SCSI driver interface */ + +/* scsi related functions - dummies for now mostly */ + +static int usc_scsi_release(struct Scsi_Host *psh) +{ + USC_DEBUG_GOT_HERE(); + + return 0; +} + +static int usc_scsi_abort (Scsi_Cmnd *srb) +/* interrupt context (!) */ /* FIXME this is about to become task context */ +{ + struct usc_desc* desc = (struct usc_desc*)(srb->host->hostdata[0]); + + USC_DEBUG_GOT_HERE(); + + usc_request_abort(desc, &desc->cmd_urb); + usc_urb_abort(desc, &desc->cmd_urb); + + return SCSI_ABORT_PENDING; +} + +static int usc_scsi_host_reset (Scsi_Cmnd *srb) +{ + struct usc_desc* desc = (struct usc_desc*)(srb->host->hostdata[0]); + + USC_DEBUG_GOT_HERE(); + usc_debug_dump(desc); + + usb_reset_device(desc->usb_dev); /*FIXME: untested on new reset code */ + return 0; /* RANT why here 0 and not SUCCESS */ +} + +/* the core of the scsi part */ + +/* faking a detection - which can't fail :-) */ + +static int usc_scsi_detect (struct SHT * sht) +{ + /* Whole function stolen from usb-storage */ + + struct usc_desc * desc = (struct usc_desc *)sht->proc_dir; + /* What a hideous hack! */ + + char local_name[48]; + + USC_DEBUG_GOT_HERE(); + + /* set up the name of our subdirectory under /proc/scsi/ */ + sprintf(local_name, "usb-scsi-%d", desc->host_number); + sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_KERNEL); + /* FIXME: where is this freed ? */ + + if (!sht->proc_name) { + USC_ERROR( "unable to allocate memory for proc interface!!\n" ); + return 0; + } + + strcpy(sht->proc_name, local_name); + + sht->proc_dir = NULL; + + /* In host->hostdata we store a pointer to desc */ + desc->host = scsi_register(sht, sizeof(desc)); + desc->host->hostdata[0] = (unsigned long)desc; +/* FIXME: what if sizeof(void*) != sizeof(unsigned long)? */ + + return 1; +} + + + +/* Main entrypoint: SCSI commands are dispatched to here */ + + + +static +int usc_scsi_queuecommand (Scsi_Cmnd *srb, usc_scsi_cmnd_callback callback ); + + + +inline static +void usc_int_submit_urb (struct urb* transfer, + int pipe, + void* data, + unsigned length, + usc_usb_urb_callback callback ) +/* Interrupt context! */ + +/* Holding transfer->context->lock! */ +{ + int res; + struct usc_transfer_context* context; + + USC_INT_INIT(); + + USC_DEBUG("Line %d: filling the bulk urb.\n",__LINE__); + FILL_BULK_URB(transfer, + context->instance->usb_dev, + pipe, + data, + length, + callback, + context + ); + +/* transfer->transfer_flags = USB_DISABLE_SPD;*/ + transfer->transfer_flags = USB_ASYNC_UNLINK; + transfer->status = 0; + transfer->timeout = 100; + + USC_DEBUG("about to submit the bulk urb.\n"); + res = usb_submit_urb( transfer ); + USC_DEBUG("submitted! res is %d\n",res); + if ( res ) { + USC_INT_ERROR( "could not submit URB! Error was %d\n",(int)res ); + context->srb->result = DID_ERROR << 16; + usc_transfer_cleanup(transfer); + } + return; +} + + +static void usc_transfer_cleanup( struct urb *transfer ) +/* Interrupt context! */ +{ + usc_scsi_cmnd_callback callback; + struct usc_transfer_context* context = (struct usc_transfer_context*)transfer->context; + + USC_DEBUG_GOT_HERE(); + if ( context->lock) + return; + context->lock = 1; + USC_DEBUG("not locked.\n"); + if ( context->final_callback ) { + callback = context->final_callback; + USC_DEBUG("running srb->callback.\n"); + context->final_callback = 0; + wmb(); + callback(context->srb); + } + USC_DEBUG("releasing lock.\n"); + up( &context->instance->cmd_lock ); + +} + +static void usc_status_done( struct urb *transfer ) +/* interrupt context */ +{ +// struct usc_transfer_context* context; + +// USC_INT_INIT(); + struct usc_transfer_context* context = (struct usc_transfer_context*)transfer->context; + +USC_DEBUG_GOT_HERE(); + if (context->status) { +USC_DEBUG_GOT_HERE(); +USC_DEBUG ("context->status is 0x%x\n",context->status); + context->srb->result = DID_ERROR << 16; + context->srb->result &= USC_SCSI_ERR_MASK; + context->srb->result |= (unsigned)context->status<<1; + usc_request_abort(context->instance, &context->instance->cmd_urb); + usc_urb_abort(context->instance, &context->instance->cmd_urb); +USC_DEBUG_GOT_HERE(); + usc_transfer_cleanup(&context->instance->cmd_urb); +// usc_get_status( context->instance, &context->instance->status_urb, context->instance->status_pipe, &context->status, 0); +return; + } else { +USC_DEBUG_GOT_HERE(); + USC_DEBUG("status == 0\n"); + context->srb->result &= USC_SCSI_ERR_MASK; + context->srb->result |= (unsigned)context->status<<1; +USC_DEBUG_GOT_HERE(); +USC_DEBUG("context->srb->result = 0x%x\n",context->srb->result); + usc_transfer_cleanup(&context->instance->cmd_urb); + +// usc_get_status( context->instance, &context->instance->status_urb, context->instance->status_pipe, &context->status, 0); +return; + } + +} + +static void usc_transfer_done( struct urb *transfer ) +{ + struct usc_transfer_context* context; + + USC_INT_INIT(); + + context->srb->result &= USC_SCSI_ERR_MASK; + context->srb->result |= (unsigned)context->status<<1; + + usc_transfer_cleanup(transfer); + + return; +} + + +static void usc_get_status( struct usc_desc *desc, struct urb *status_transfer, int pipe, void *status, int want_init ) +/* Interrupt context! */ +{ +// struct usc_transfer_context* context; + +// USC_INT_INIT(); +int res; + + + if (want_init) { +USC_DEBUG("line %d: filling the status urb.\n",__LINE__); + FILL_INT_URB(status_transfer, + desc->usb_dev, + pipe, + status, + 1, + usc_status_done, + &desc->context, + desc->interrupt_interval + ); + status_transfer->transfer_flags = USB_ASYNC_UNLINK; + } + USC_DEBUG("line %d: sending the status urb.\n",__LINE__); + res = usb_submit_urb(status_transfer); + if (res) + USC_ERROR ("ERROR! unable to transmit status urb. res == %d status_transfer->status == 0x%x\n",res, status_transfer->status); + + + return; +} + +static void usc_data_done( struct urb* transfer ) +/* Interrupt context! */ +{ + struct usc_transfer_context* context; + + USC_INT_INIT(); + +USC_DEBUG_GOT_HERE(); +if ( transfer->status ) { + context->srb->result = DID_ERROR<<16; +USC_DEBUG ("JHALL: line %d: status %d error %x hope status_done takes this.\n",__LINE__, (int)transfer->status, context->srb->result); + usc_transfer_cleanup(transfer); + } else if ( context->data_length != transfer->actual_length ) { + context->srb->resid = context->data_length - transfer->actual_length; +USC_DEBUG_GOT_HERE(); + usc_transfer_cleanup(transfer); + } +//usc_transfer_done(transfer); + + return; +} + + +static void usc_command_done( struct urb *transfer ) +/* Interrupt context! */ +{ + struct usc_transfer_context* context; + + USC_INT_INIT(); + + USC_DEBUG_GOT_HERE(); + if ( transfer->status ) { + USC_DEBUG_GOT_HERE(); + context->srb->result = DID_ERROR<<16; + usc_transfer_cleanup(transfer); + + return; + } + + USC_DEBUG_GOT_HERE(); + if ( context->data ) { + USC_DEBUG_GOT_HERE(); + usc_int_submit_urb(transfer, + context->data_pipe, + context->data, + context->data_length, + usc_data_done); +// } else usc_transfer_done(transfer); +} + + return; +} + + + + static const unsigned char usc_direction[256/8] = { + 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, + 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + +#define USC_DIRECTION_IS_IN(x) ((usc_direction[x>>3] >> (x & 7)) & 1) + +static void +usc_build_transfer_context( Scsi_Cmnd *srb, struct usc_desc* desc ) +{ + + int pipe; + + + USC_DEBUG_GOT_HERE(); + + desc->context.instance = desc; + desc->context.srb = srb; + atomic_set(&desc->context.do_abort,0); + + if ( !srb->bufflen ){ + desc->context.data = 0; + desc->context.data_length = 0; + return; + } else { + desc->context.data = srb->buffer; + desc->context.data_length = srb->bufflen; + } + + /* can't rely on srb->sc_data_direction */ + + /* Brutally ripped from usb-storage */ + + if ( USC_DIRECTION_IS_IN(srb->cmnd[0]) ) { + pipe = desc->image_pipe; + USC_DEBUG( "transfering from desc->ep_image == %d\n", + (int)desc->ep_image ); + } else { + USC_DEBUG("transfering to desc->ep_out == %d\n", + (int)desc->ep_out); + pipe = desc->out_pipe; + } + desc->context.data_pipe = pipe; +} + + +static +int usc_scsi_queuecommand( Scsi_Cmnd *srb, usc_scsi_cmnd_callback callback ) +{ + struct usc_desc* desc = (struct usc_desc*)(srb->host->hostdata[0]); + int err = 0; + int res; + + USC_DEBUG_GOT_HERE(); + usc_show_command(srb); + usc_debug_dump(desc); + + if ( srb->device->lun || srb->device->id || srb->device->channel ) { + + USC_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel ); + + USC_DEBUG("this device doesn't exist\n"); + + srb->result = DID_BAD_TARGET << 16; + + if(callback) + callback(srb); + + goto out; + } + +USC_DEBUG("Line %d: trying to engage the cmd_lock.\n",__LINE__); + down(&desc->cmd_lock); + desc->context.lock = 0; + + USC_DEBUG_GOT_HERE(); + usc_show_command(srb); + + + FILL_BULK_URB(&desc->cmd_urb, + desc->usb_dev, + desc->out_pipe, + srb->cmnd, + srb->cmd_len, + usc_command_done, + &desc->context + ); + + + usc_build_transfer_context( srb, desc ); + desc->context.final_callback = callback; + desc->cmd_urb.timeout = 100; + desc->cmd_urb.transfer_flags = USB_ASYNC_UNLINK; + +/* desc->cmd_urb.transfer_flags = USB_DISABLE_SPD;*/ + + res=usb_submit_urb(&desc->cmd_urb); + + if(res){ + USC_ERROR("error %d submitting URB\n",(int)res); + srb->result = DID_ERROR << 16; + USC_DEBUG_GOT_HERE(); +desc->context.lock = 1; + + if(callback) + callback(srb); + up(&desc->cmd_lock); /* no further cleanup is done */ + + goto out; + } + + USC_DEBUG_GOT_HERE(); + + out: + return err; +} +/* + * this defines our 'host' + */ + +/* NOTE: This is taken from usb-storage, should be right. */ + + +static Scsi_Host_Template usc_scsi_host_template = { + name: "hp5300", + detect: usc_scsi_detect, + release: usc_scsi_release, + command: 0, + queuecommand: usc_scsi_queuecommand, + + eh_abort_handler: usc_scsi_abort, + eh_device_reset_handler:0, + eh_bus_reset_handler: 0, + eh_host_reset_handler: usc_scsi_host_reset, + + can_queue: 1, + this_id: -1, + cmd_per_lun: 1, + present: 0, + unchecked_isa_dma: FALSE, + use_clustering: FALSE, + use_new_eh_code: TRUE, + emulated: TRUE +}; + + +/* USB layer driver interface implementation */ + +static void usc_usb_disconnect (struct usb_device *dev, void *ptr) +{ + struct usc_desc* to_remove = (struct usc_desc*)ptr; + + USC_DEBUG_GOT_HERE(); + + /* leave the list - lock it */ + down(&usc_list_semaphore); + + usc_remove_nolock(to_remove); + + up(&usc_list_semaphore); +} + +struct vendor_product +{ + u16 idVendor; + u16 idProduct; + char* name; + enum + { + usc_sup_unknown=0, + usc_sup_alpha, + usc_sup_full + } + support_status; +} ; + + +/* These are taken from the msmUSB.inf file on the Windows driver CD */ +const static struct vendor_product usc_supported_products[] = +{ + { + 0x3f0, 0x701,"HP 5300C",usc_sup_unknown + } +} +; + + +MODULE_DEVICE_TABLE (usb, hp5300_usb_id); + +const static struct vendor_product* usc_last_product = &usc_supported_products[ sizeof(usc_supported_products) / sizeof(struct vendor_product) ]; + /* Must never be derefed, points to one after last entry */ + + +static void * usc_usb_probe (struct usb_device *dev, unsigned int interface, const struct usb_device_id *id) +{ + int i; + int result; + int ep_out = -1; + int interrupt_interval = 0; + int ep_in_set[3]; /* this will break if we have more than three endpoints + which is why we check */ + int *ep_in_current = ep_in_set; + + struct usc_desc * new_desc; + struct vendor_product const* p; + + /* the altsettting 0 on the interface we're probing */ + struct usb_interface_descriptor *altsetting; + + USC_DEBUG_GOT_HERE(); + USC_DEBUG( "usb-device descriptor at %x\n", (int)dev ); + + USC_DEBUG( "product id = 0x%x, vendor id = 0x%x\n", + (int)dev->descriptor.idProduct, + (int)dev->descriptor.idVendor ); + + USC_DEBUG_GOT_HERE(); + + /* checking IDs */ + for( p = usc_supported_products; p != usc_last_product; p++ ) + if ( dev->descriptor.idVendor == p->idVendor && + dev->descriptor.idProduct == p->idProduct ) + goto is_supported; + else + USC_DEBUG( "doesn't appear to be model %s\n", p->name ); + + USC_DEBUG( "returning NULL: unsupported\n" ); + + return NULL; + + is_supported: + + USC_DEBUG_GOT_HERE(); + + USC_DEBUG( "found model %s\n", p->name ); + if ( p->support_status != usc_sup_full ) + USC_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n", + p->name ); + + /* the altsettting 0 on the interface we're probing */ + altsetting = + &(dev->actconfig->interface[interface].altsetting[0]); + + + /* Check if the config is sane */ + + if ( altsetting->bNumEndpoints != USC_EP_TOTAL ) { + USC_WARNING( "expecting %d got %d endpoints! Bailing out.\n", + (int)USC_EP_TOTAL, (int)altsetting->bNumEndpoints ); + return NULL; + } + + for( i = 0; i < altsetting->bNumEndpoints; i++ ) { + if ((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + interrupt_interval = altsetting->endpoint[i].bInterval; + if (((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) && + ((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)) { + + USC_WARNING( "can only deal with 2 bulk and 1 interrupt endpoints; endpoint %d is not either.\n", + (int)altsetting->endpoint[i].bEndpointAddress ); + } else { + if (altsetting->endpoint[i].bEndpointAddress & + USB_DIR_IN) + *ep_in_current++ + = altsetting->endpoint[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + else { + if ( ep_out != -1 ) { + USC_WARNING( "can only deal with one output endpoints. Bailing out." ); + return NULL; + } + + ep_out = altsetting->endpoint[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + } + } + + } + + + if ( ep_out == -1 ) { + USC_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" ); + return NULL; + } + + +USC_DEBUG_GOT_HERE(); + /* I don't understand the following fully (it's from usb-storage) -- John */ + + /* set the interface -- STALL is an acceptable response here */ + result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0); + + USC_DEBUG("usb_set_interface returned %d.\n",result); + switch( result ) + { + case 0: /* no error */ + break; + + case -EPIPE: + usb_clear_halt(dev, usb_sndctrlpipe(dev, 0)); + USC_DEBUG( "clearing clearing stall on control interface\n" ); + break; + + default: + USC_DEBUG( "unknown error %d from usb_set_interface\n", + (int)result ); + return NULL; + } + + + /* allocating a new descriptor */ + new_desc = (struct usc_desc *)kmalloc(sizeof(struct usc_desc), GFP_KERNEL); + if (new_desc == NULL) + { + USC_ERROR("couldn't allocate scanner desc, bailing out!\n"); + return NULL; + } + + /* As done by usb_alloc_urb */ + memset( new_desc, 0, sizeof(*new_desc) ); + spin_lock_init(&new_desc->cmd_urb.lock); + spin_lock_init(&new_desc->status_urb.lock); + + + /* initialising that descriptor */ + new_desc->usb_dev = dev; + new_desc->interface = interface; + + init_MUTEX(&new_desc->cmd_lock); + init_MUTEX(&new_desc->status_lock); + + if(usc_list){ + new_desc->host_number = usc_list->host_number+1; + } else { + new_desc->host_number = 0; + } + + /* endpoints */ + + new_desc->ep_out = ep_out; + new_desc->out_pipe = usb_sndbulkpipe(dev, ep_out); + new_desc->ep_response = ep_in_set[0]; + new_desc->status_pipe = usb_rcvintpipe(dev, ep_in_set[0]); + new_desc->ep_image = ep_in_set[1]; + new_desc->image_pipe = usb_rcvbulkpipe(dev, ep_in_set[1]); + new_desc->interrupt_interval = interrupt_interval; + + + if ( new_desc->ep_out != USC_EP_OUT ) + USC_WARNING( "will this work? Command EP is not usually %d\n", + (int)new_desc->ep_out ); + + if ( new_desc->ep_response != USC_EP_RESPONSE ) + USC_WARNING( "will this work? Response EP is not usually %d\n", + (int)new_desc->ep_response ); + + if ( new_desc->ep_image != USC_EP_IMAGE ) + USC_WARNING( "will this work? Image data EP is not usually %d\n", + (int)new_desc->ep_image ); + +/* start the status handler */ +down(&new_desc->status_lock); +usc_get_status(new_desc, &new_desc->status_urb, new_desc->status_pipe, &new_desc->context.status, 1); + + /* Initialize the host template based on the default one */ + memcpy(&(new_desc->ctempl), &usc_scsi_host_template, sizeof(usc_scsi_host_template)); + /* HACK from usb-storage - this is needed for scsi detection */ + (struct usc_desc *)new_desc->ctempl.proc_dir = new_desc; /* FIXME */ + + USC_DEBUG("registering SCSI module\n"); + + new_desc->ctempl.module = THIS_MODULE; + result = scsi_register_module(MODULE_SCSI_HA, &(new_desc->ctempl)); + /* Will get hit back in microtek_detect by this func */ + if ( result ) + { + USC_ERROR( "error %d from scsi_register_module! Help!\n", + (int)result ); + usb_unlink_urb(&new_desc->status_urb); + + /* FIXME: need more cleanup? */ + kfree( new_desc ); + return NULL; + } + USC_DEBUG_GOT_HERE(); + + /* FIXME: the bomb is armed, must the host be registered under lock ? */ + /* join the list - lock it */ + down(&usc_list_semaphore); + + usc_add_nolock( new_desc ); + + up(&usc_list_semaphore); + + + USC_DEBUG("completed probe and exiting happily\n"); + + return (void *)new_desc; +} + + + +/* get us noticed by the rest of the kernel */ + +int __init usbscsibulkint_drv_init(void) +{ + int result; + + USC_DEBUG_GOT_HERE(); + init_MUTEX(&usc_list_semaphore); + + if ((result = usb_register(&usc_usb_driver)) < 0) { + USC_DEBUG("usb_register returned %d\n", result ); + return -1; + } else { + USC_DEBUG("driver registered.\n"); + } + + return 0; +} + +void __exit usbscsibulkint_drv_exit(void) +{ + struct usc_desc* next; + + USC_DEBUG_GOT_HERE(); + + usb_deregister(&usc_usb_driver); + + down(&usc_list_semaphore); + + while (usc_list) { + /* keep track of where the next one is */ + next = usc_list->next; + + usc_remove_nolock( usc_list ); + + /* advance the list pointer */ + usc_list = next; + } + + up(&usc_list_semaphore); +} + +module_init(usbscsibulkint_drv_init); +module_exit(usbscsibulkint_drv_exit); + +MODULE_AUTHOR("Jeremy Hall, "); +MODULE_DESCRIPTION("SCSI over USB driver for scanners"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hp5300.h linux.ac/drivers/usb/hp5300.h --- linux.vanilla/drivers/usb/hp5300.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/hp5300.h Sun Apr 22 01:58:23 2001 @@ -0,0 +1,69 @@ + /* + * Driver for Microtek Scanmaker X6 USB scanner and possibly others. + * + * (C) Copyright 2000 John Fremlin + * (C) Copyright 2000 Oliver Neukum + * + * See microtek.c for history + * + */ + +typedef void (*usc_scsi_cmnd_callback)(Scsi_Cmnd *); +typedef void (*usc_usb_urb_callback) (struct urb *); + + +struct usc_transfer_context +{ + struct usc_desc* instance; + usc_scsi_cmnd_callback final_callback; + Scsi_Cmnd *srb; + + void* data; + unsigned data_length; + int data_pipe; + + int lock; + + atomic_t do_abort; /* when != 0 URB completion routines will + return straightaway */ + + u8 status; /* status returned from ep_response after command completion */ +}; + + +struct usc_desc { + struct usc_desc *next; + struct usc_desc *prev; + + struct usb_device *usb_dev; + + int interface; + + /* Endpoint addresses */ + u8 ep_out; + u8 ep_response; + u8 ep_image; + u8 interrupt_interval; + int image_pipe; + int status_pipe; + int out_pipe; + + struct Scsi_Host * host; + Scsi_Host_Template ctempl; + int host_number; + + struct semaphore cmd_lock; + struct semaphore status_lock; + + struct urb cmd_urb; + struct urb status_urb; + struct usc_transfer_context context; +}; + + +#define USC_EP_OUT 0x1 +#define USC_EP_RESPONSE 0x2 +#define USC_EP_IMAGE 0x3 +#define USC_EP_TOTAL 0x3 + +#define USC_SCSI_ERR_MASK ~0x3fu diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hub.c linux.ac/drivers/usb/hub.c --- linux.vanilla/drivers/usb/hub.c Mon Apr 30 15:13:27 2001 +++ linux.ac/drivers/usb/hub.c Mon Apr 30 15:48:45 2001 @@ -4,6 +4,8 @@ * (C) Copyright 1999 Linus Torvalds * (C) Copyright 1999 Johannes Erdfelt * (C) Copyright 1999 Gregory P. Smith + * (C) Copyright 2001 Brad Hards (bhards@bigpond.net.au + * */ #include @@ -38,6 +40,19 @@ static int khubd_pid = 0; /* PID of khubd */ static DECLARE_MUTEX_LOCKED(khubd_exited); +#ifdef DEBUG +static inline char *portspeed (int portstatus) +{ + if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) + return "480 Mb/s"; + else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED)) + return "1.5 Mb/s"; + else + return "12 Mb/s"; +} +#endif + +/* USB 2.0 spec Section 11.24.4.5 */ static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), @@ -45,24 +60,38 @@ USB_DT_HUB << 8, 0, data, size, HZ); } +/* + * USB 2.0 spec Section 11.24.2.1 + */ static int usb_clear_hub_feature(struct usb_device *dev, int feature) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, HZ); } +/* + * USB 2.0 spec Section 11.24.2.2 + * BUG: doesn't handle port indicator selector in high byte of wIndex + */ static int usb_clear_port_feature(struct usb_device *dev, int port, int feature) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ); } +/* + * USB 2.0 spec Section 11.24.2.13 + * BUG: doesn't handle port indicator selector in high byte of wIndex + */ static int usb_set_port_feature(struct usb_device *dev, int port, int feature) { return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ); } +/* + * USB 2.0 spec Section 11.24.2.6 + */ static int usb_get_hub_status(struct usb_device *dev, void *data) { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), @@ -70,6 +99,9 @@ data, sizeof(struct usb_hub_status), HZ); } +/* + * USB 2.0 spec Section 11.24.2.7 + */ static int usb_get_port_status(struct usb_device *dev, int port, void *data) { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), @@ -112,7 +144,7 @@ /* Enable power to the ports */ dbg("enabling power on all ports"); - for (i = 0; i < hub->nports; i++) + for (i = 0; i < hub->descriptor->bNbrPorts; i++) usb_set_port_feature(hub->dev, i + 1, USB_PORT_FEAT_POWER); /* Wait for power to be enabled */ @@ -127,14 +159,14 @@ unsigned int pipe; int i, maxp, ret; - hub->descriptor = kmalloc(HUB_DESCRIPTOR_MAX_SIZE, GFP_KERNEL); + hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL); if (!hub->descriptor) { - err("Unable to kmalloc %d bytes for hub descriptor", HUB_DESCRIPTOR_MAX_SIZE); + err("Unable to kmalloc %d bytes for hub descriptor", sizeof(*hub->descriptor)); return -1; } /* Request the entire hub descriptor. */ - ret = usb_get_hub_descriptor(dev, hub->descriptor, HUB_DESCRIPTOR_MAX_SIZE); + ret = usb_get_hub_descriptor(dev, hub->descriptor, sizeof(*hub->descriptor)); /* descriptor> is large enough for a hub with 127 ports; * the hub can/will return fewer bytes here. */ if (ret < 0) { @@ -143,10 +175,10 @@ return -1; } - le16_to_cpus(&hub->descriptor->wHubCharacteristics); + dev->maxchild = hub->descriptor->bNbrPorts; + info("%d port%s detected", hub->descriptor->bNbrPorts, (hub->descriptor->bNbrPorts == 1) ? "" : "s"); - hub->nports = dev->maxchild = hub->descriptor->bNbrPorts; - info("%d port%s detected", hub->nports, (hub->nports == 1) ? "" : "s"); + le16_to_cpus(&hub->descriptor->wHubCharacteristics); if (hub->descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) dbg("part of a compound device"); @@ -179,11 +211,45 @@ break; } + switch (dev->descriptor.bDeviceProtocol) { + case 0: + break; + case 1: + dbg("Single TT"); + break; + case 2: + dbg("Multiple TT"); + break; + default: + dbg("Unrecognized hub protocol %d", + dev->descriptor.bDeviceProtocol); + break; + } + + switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) { + case 0x00: + if (dev->descriptor.bDeviceProtocol != 0) + dbg("TT requires at most 8 FS bit times"); + break; + case 0x20: + dbg("TT requires at most 16 FS bit times"); + break; + case 0x40: + dbg("TT requires at most 24 FS bit times"); + break; + case 0x60: + dbg("TT requires at most 32 FS bit times"); + break; + } + + dbg("Port indicators are %s supported", + (hub->descriptor->wHubCharacteristics & HUB_CHAR_PORTIND) ? "" : "not"); + dbg("power on to power good time: %dms", hub->descriptor->bPwrOn2PwrGood * 2); dbg("hub controller current requirement: %dmA", hub->descriptor->bHubContrCurrent); for (i = 0; i < dev->maxchild; i++) - portstr[i] = hub->descriptor->bitmap[((i + 1) / 8)] & (1 << ((i + 1) % 8)) ? 'F' : 'R'; + portstr[i] = hub->descriptor->DeviceRemovable[((i + 1) / 8)] & (1 << ((i + 1) % 8)) ? 'F' : 'R'; portstr[dev->maxchild] = 0; dbg("port removable status: %s", portstr); @@ -396,7 +462,7 @@ int i; /* Disconnect any attached devices */ - for (i = 0; i < hub->nports; i++) { + for (i = 0; i < hub->descriptor->bNbrPorts; i++) { if (dev->children[i]) usb_disconnect(&dev->children[i]); } @@ -464,8 +530,7 @@ portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); dbg("port %d, portstatus %x, change %x, %s", port + 1, - portstatus, portchange, - portstatus & (1 << USB_PORT_FEAT_LOWSPEED) ? "1.5 Mb/s" : "12 Mb/s"); + portstatus, portchange, portspeed (portstatus)); /* bomb out completely if something weird happened */ if ((portchange & USB_PORT_STAT_C_CONNECTION) || @@ -475,7 +540,12 @@ /* if we`ve finished resetting, then break out of the loop */ if (!(portstatus & USB_PORT_STAT_RESET) && (portstatus & USB_PORT_STAT_ENABLE)) { - dev->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; + if (portstatus & USB_PORT_STAT_HIGH_SPEED) + dev->speed = USB_SPEED_HIGH; + else if (portstatus & USB_PORT_STAT_LOW_SPEED) + dev->speed = USB_SPEED_LOW; + else + dev->speed = USB_SPEED_FULL; return 0; } @@ -538,8 +608,8 @@ portstatus = le16_to_cpu(portsts->wPortStatus); portchange = le16_to_cpu(portsts->wPortChange); - dbg("port %d, portstatus %x, change %x, %s", port + 1, portstatus, - portchange, portstatus & (1 << USB_PORT_FEAT_LOWSPEED) ? "1.5 Mb/s" : "12 Mb/s"); + dbg("port %d, portstatus %x, change %x, %s", + port + 1, portstatus, portchange, portspeed (portstatus)); /* Clear the connection change status */ usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION); @@ -556,8 +626,7 @@ return; } - /* Some low speed devices have problems with the quick delay, so */ - /* be a bit pessimistic with those devices. RHbug #23670 */ + /* zaitcev RHbug #23670 - 1.5Mb/s mice die when switching VCs */ if (portstatus & USB_PORT_STAT_LOW_SPEED) { wait_ms(400); delay = HUB_LONG_RESET_TIME; @@ -685,7 +754,7 @@ hub->error = 0; } - for (i = 0; i < hub->nports; i++) { + for (i = 0; i < hub->descriptor->bNbrPorts; i++) { struct usb_port_status portsts; unsigned short portstatus, portchange; @@ -781,6 +850,7 @@ dbg("usb_hub_thread exiting"); + unlock_kernel(); up_and_exit(&khubd_exited, 0); } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/hub.h linux.ac/drivers/usb/hub.h --- linux.vanilla/drivers/usb/hub.h Mon Apr 30 15:13:27 2001 +++ linux.ac/drivers/usb/hub.h Thu May 17 23:06:21 2001 @@ -12,12 +12,14 @@ /* * Hub Class feature numbers + * See USB 2.0 spec Table 11-17 */ #define C_HUB_LOCAL_POWER 0 #define C_HUB_OVER_CURRENT 1 /* * Port feature numbers + * See USB 2.0 spec Table 11-17 */ #define USB_PORT_FEAT_CONNECTION 0 #define USB_PORT_FEAT_ENABLE 1 @@ -26,37 +28,61 @@ #define USB_PORT_FEAT_RESET 4 #define USB_PORT_FEAT_POWER 8 #define USB_PORT_FEAT_LOWSPEED 9 +#define USB_PORT_FEAT_HIGHSPEED 10 #define USB_PORT_FEAT_C_CONNECTION 16 #define USB_PORT_FEAT_C_ENABLE 17 #define USB_PORT_FEAT_C_SUSPEND 18 #define USB_PORT_FEAT_C_OVER_CURRENT 19 #define USB_PORT_FEAT_C_RESET 20 +#define USB_PORT_FEAT_TEST 21 +#define USB_PORT_FEAT_INDICATOR 22 +/* + * Hub Status and Hub Change results + * See USB 2.0 spec Table 11-19 and Table 11-20 + */ struct usb_port_status { __u16 wPortStatus; __u16 wPortChange; } __attribute__ ((packed)); -/* wPortStatus bits */ +/* + * wPortStatus bit field + * See USB 2.0 spec Table 11-21 + */ #define USB_PORT_STAT_CONNECTION 0x0001 #define USB_PORT_STAT_ENABLE 0x0002 #define USB_PORT_STAT_SUSPEND 0x0004 #define USB_PORT_STAT_OVERCURRENT 0x0008 #define USB_PORT_STAT_RESET 0x0010 +/* bits 5 for 7 are reserved */ #define USB_PORT_STAT_POWER 0x0100 #define USB_PORT_STAT_LOW_SPEED 0x0200 - -/* wPortChange bits */ +#define USB_PORT_STAT_HIGH_SPEED 0x0400 +#define USB_PORT_STAT_TEST 0x0800 +#define USB_PORT_STAT_INDICATOR 0x1000 +/* bits 13 to 15 are reserved */ + +/* + * wPortChange bit field + * See USB 2.0 spec Table 11-22 + * Bits 0 to 4 shown, bits 5 to 15 are reserved + */ #define USB_PORT_STAT_C_CONNECTION 0x0001 #define USB_PORT_STAT_C_ENABLE 0x0002 #define USB_PORT_STAT_C_SUSPEND 0x0004 #define USB_PORT_STAT_C_OVERCURRENT 0x0008 #define USB_PORT_STAT_C_RESET 0x0010 -/* wHubCharacteristics (masks) */ -#define HUB_CHAR_LPSM 0x0003 -#define HUB_CHAR_COMPOUND 0x0004 -#define HUB_CHAR_OCPM 0x0018 +/* + * wHubCharacteristics (masks) + * See USB 2.0 spec Table 11-13, offset 3 + */ +#define HUB_CHAR_LPSM 0x0003 /* D1 .. D0 */ +#define HUB_CHAR_COMPOUND 0x0004 /* D2 */ +#define HUB_CHAR_OCPM 0x0018 /* D4 .. D3 */ +#define HUB_CHAR_TTTT 0x0060 /* D6 .. D5 */ +#define HUB_CHAR_PORTIND 0x0080 /* D7 */ struct usb_hub_status { __u16 wHubStatus; @@ -64,28 +90,31 @@ } __attribute__ ((packed)); /* - *Hub Status & Hub Change bit masks + * Hub Status & Hub Change bit masks + * See USB 2.0 spec Table 11-19 and Table 11-20 + * Bits 0 and 1 for wHubStatus and wHubChange + * Bits 2 to 15 are reserved for both */ #define HUB_STATUS_LOCAL_POWER 0x0001 #define HUB_STATUS_OVERCURRENT 0x0002 - #define HUB_CHANGE_LOCAL_POWER 0x0001 #define HUB_CHANGE_OVERCURRENT 0x0002 -#define HUB_DESCRIPTOR_MAX_SIZE 39 /* enough for 127 ports on a hub */ -/* Hub descriptor */ +/* + * Hub descriptor + * See USB 2.0 spec Table 11-13 + */ struct usb_hub_descriptor { - __u8 bLength; + __u8 bDescLength; __u8 bDescriptorType; __u8 bNbrPorts; __u16 wHubCharacteristics; __u8 bPwrOn2PwrGood; __u8 bHubContrCurrent; - - /* DeviceRemovable and PortPwrCtrlMask want to be variable-length - bitmaps that hold max 256 entries, but for now they're ignored */ - __u8 bitmap[0]; + /* add 1 bit for hub status change; round to bytes */ + __u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8]; + __u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8]; } __attribute__ ((packed)); struct usb_device; @@ -104,12 +133,9 @@ struct list_head event_list; - /* Number of ports on the hub */ - int nports; - struct usb_hub_descriptor *descriptor; atomic_t refcnt; }; -#endif +#endif /* __LINUX_HUB_H */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ibmcam.c linux.ac/drivers/usb/ibmcam.c --- linux.vanilla/drivers/usb/ibmcam.c Sat May 26 16:53:14 2001 +++ linux.ac/drivers/usb/ibmcam.c Sat May 26 17:07:06 2001 @@ -27,58 +27,71 @@ #include #include -#include -#include -#include -#include -#include -#include #include #include #include -#include -#include -#include +#include "usbvideo.h" -#include "ibmcam.h" +#define IBMCAM_VENDOR_ID 0x0545 +#define IBMCAM_PRODUCT_ID 0x8080 +#define NETCAM_PRODUCT_ID 0x8002 /* IBM NetCamera, close to model 2 */ + +#define MAX_IBMCAM 4 /* How many devices we allow to connect */ +#define USES_IBMCAM_PUTPIXEL 0 /* 0=Fast/oops 1=Slow/secure */ + +/* Header signatures */ + +/* Model 1 header: 00 FF 00 xx */ +#define HDRSIG_MODEL1_128x96 0x06 /* U Y V Y ... */ +#define HDRSIG_MODEL1_176x144 0x0e /* U Y V Y ... */ +#define HDRSIG_MODEL1_352x288 0x00 /* V Y U Y ... */ + +#define IBMCAM_MODEL_1 1 /* XVP-501, 3 interfaces, rev. 0.02 */ +#define IBMCAM_MODEL_2 2 /* KSX-X9903, 2 interfaces, rev. 3.0a */ +#define IBMCAM_MODEL_3 3 /* KSX-X9902, 2 interfaces, rev. 3.01 */ +#define IBMCAM_MODEL_4 4 /* IBM NetCamera, 0545/8002/3.0a */ + +/* Video sizes supported */ +#define VIDEOSIZE_128x96 VIDEOSIZE(128, 96) +#define VIDEOSIZE_176x144 VIDEOSIZE(176,144) +#define VIDEOSIZE_352x288 VIDEOSIZE(352,288) +#define VIDEOSIZE_320x240 VIDEOSIZE(320,240) +#define VIDEOSIZE_352x240 VIDEOSIZE(352,240) +#define VIDEOSIZE_640x480 VIDEOSIZE(640,480) +#define VIDEOSIZE_160x120 VIDEOSIZE(160,120) + +/* Video sizes supported */ +enum { + SIZE_128x96 = 0, + SIZE_160x120, + SIZE_176x144, + SIZE_320x240, + SIZE_352x240, + SIZE_352x288, + SIZE_640x480, + /* Add/remove/rearrange items before this line */ + SIZE_LastItem +}; /* - * Version Information + * This structure lives in uvd_t->user field. */ -#define DRIVER_VERSION "v1.0.0" -#define DRIVER_AUTHOR "http://www.linux-usb.org/ibmcam/" -#define DRIVER_DESC "IBM/Xirlink C-it USB Camera Driver for Linux (c) 2000" - -#define ENABLE_HEXDUMP 0 /* Enable if you need it */ -static int debug = 0; +typedef struct { + int initialized; /* Had we already sent init sequence? */ + int camera_model; /* What type of IBM camera we got? */ + int has_hdr; +} ibmcam_t; +#define IBMCAM_T(uvd) ((ibmcam_t *)((uvd)->user_data)) -static int video_nr = -1; +usbvideo_t *cams = NULL; -/* Completion states of the data parser */ -typedef enum { - scan_Continue, /* Just parse next item */ - scan_NextFrame, /* Frame done, send it to V4L */ - scan_Out, /* Not enough data for frame */ - scan_EndParse /* End parsing */ -} scan_state_t; - -/* Bit flags (options) */ -#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) -#define FLAGS_MONOCHROME (1 << 1) -#define FLAGS_DISPLAY_HINTS (1 << 2) -#define FLAGS_OVERLAY_STATS (1 << 3) -#define FLAGS_FORCE_TESTPATTERN (1 << 4) -#define FLAGS_SEPARATE_FRAMES (1 << 5) -#define FLAGS_CLEAN_FRAMES (1 << 6) +static int debug = 0; static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */ -/* This is the size of V4L frame that we provide */ -static const int imgwidth = V4L_FRAME_WIDTH_USED; -static const int imgheight = V4L_FRAME_HEIGHT; -static const int min_imgwidth = 8; -static const int min_imgheight = 4; +static const int min_canvasWidth = 8; +static const int min_canvasHeight = 4; static int lighting = 1; /* Medium */ @@ -88,27 +101,9 @@ #define FRAMERATE_MIN 0 #define FRAMERATE_MAX 6 -static int framerate = 2; /* Lower, reliable frame rate (8-12 fps) */ - -enum { - VIDEOSIZE_128x96 = 0, - VIDEOSIZE_176x144, - VIDEOSIZE_352x288, - VIDEOSIZE_320x240, - VIDEOSIZE_352x240, -}; - -static int videosize = VIDEOSIZE_352x288; +static int framerate = -1; -/* - * The value of 'scratchbufsize' affects quality of the picture - * in many ways. Shorter buffers may cause loss of data when client - * is too slow. Larger buffers are memory-consuming and take longer - * to work with. This setting can be adjusted, but the default value - * should be OK for most desktop users. - */ -#define DEFAULT_SCRATCH_BUF_SIZE (0x10000) /* 64 KB */ -static const int scratchbufsize = DEFAULT_SCRATCH_BUF_SIZE; +static int size = SIZE_352x288; /* * Here we define several initialization variables. They may @@ -131,36 +126,37 @@ static int hue_correction = 128; /* Settings for camera model 2 */ -static int init_model2_rg = -1; static int init_model2_rg2 = -1; static int init_model2_sat = -1; static int init_model2_yb = -1; +/* 01.01.08 - Added for RCA video in support -LO */ +/* Settings for camera model 3 */ +static int init_model3_input = 0; + MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); MODULE_PARM(flags, "i"); -MODULE_PARM_DESC(flags, "Bitfield: 0=VIDIOCSYNC, 1=B/W, 2=show hints, 3=show stats, 4=test pattern, 5=seperate frames, 6=clean frames"); +MODULE_PARM_DESC(flags, "Bitfield: 0=VIDIOCSYNC, 1=B/W, 2=show hints, 3=show stats, 4=test pattern, 5=separate frames, 6=clean frames"); MODULE_PARM(framerate, "i"); MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)"); MODULE_PARM(lighting, "i"); MODULE_PARM_DESC(lighting, "Photosensitivity: 0=bright, 1=medium (default), 2=low light"); MODULE_PARM(sharpness, "i"); MODULE_PARM_DESC(sharpness, "Model1 noise reduction: 0=smooth, 6=sharp (default=4)"); -MODULE_PARM(videosize, "i"); -MODULE_PARM_DESC(videosize, "Image size: 0=128x96, 1=176x144, 2=352x288, 3=320x240, 4=352x240 (default=1)"); +MODULE_PARM(size, "i"); +MODULE_PARM_DESC(size, "Image size: 0=128x96 1=160x120 2=176x144 3=320x240 4=352x240 5=352x288 6=640x480 (default=5)"); MODULE_PARM(init_brightness, "i"); MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)"); MODULE_PARM(init_contrast, "i"); MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)"); MODULE_PARM(init_color, "i"); -MODULE_PARM_DESC(init_color, "Dolor preconfiguration: 0-255 (default=128)"); +MODULE_PARM_DESC(init_color, "Color preconfiguration: 0-255 (default=128)"); MODULE_PARM(init_hue, "i"); MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)"); MODULE_PARM(hue_correction, "i"); MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)"); -MODULE_PARM(init_model2_rg, "i"); -MODULE_PARM_DESC(init_model2_rg, "Model2 preconfiguration: 0-255 (default=112)"); MODULE_PARM(init_model2_rg2, "i"); MODULE_PARM_DESC(init_model2_rg2, "Model2 preconfiguration: 0-255 (default=47)"); MODULE_PARM(init_model2_sat, "i"); @@ -168,6 +164,13 @@ MODULE_PARM(init_model2_yb, "i"); MODULE_PARM_DESC(init_model2_yb, "Model2 preconfiguration: 0-255 (default=160)"); +/* 01.01.08 - Added for RCA video in support -LO */ +MODULE_PARM(init_model3_input, "i"); +MODULE_PARM_DESC(init_model3_input, "Model3 input: 0=CCD 1=RCA"); + +MODULE_AUTHOR ("Dmitri"); +MODULE_DESCRIPTION ("IBM/Xirlink C-it USB Camera Driver for Linux (c) 2000"); + /* Still mysterious i2c commands */ static const unsigned short unknown_88 = 0x0088; static const unsigned short unknown_89 = 0x0089; @@ -182,594 +185,326 @@ static const unsigned short mod2_color_balance_rg2 = 0x001e; /* 0 (red) .. $7F (green) */ static const unsigned short mod2_saturation = 0x0020; /* 0 (b/w) - $7F (full color) */ static const unsigned short mod2_color_balance_yb = 0x0022; /* 0..$7F, $50 is about right */ -static const unsigned short mod2_color_balance_rg = 0x0024; /* 0..$7F, $70 is about right */ +static const unsigned short mod2_hue = 0x0024; /* 0..$7F, $70 is about right */ static const unsigned short mod2_sensitivity = 0x0028; /* 0 (min) .. $1F (max) */ -#define MAX_IBMCAM 4 - -struct usb_ibmcam cams[MAX_IBMCAM]; - -/*******************************/ -/* Memory management functions */ -/*******************************/ - -#define MDEBUG(x) do { } while(0) /* Debug memory management */ - -static struct usb_driver ibmcam_driver; -static void usb_ibmcam_release(struct usb_ibmcam *ibmcam); - -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if (pte_present(pte)) { - ret = (unsigned long) page_address(pte_page(pte)); - ret |= (adr & (PAGE_SIZE - 1)); - } - } - } - MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); - return ret; -} - -static inline unsigned long uvirt_to_bus(unsigned long adr) -{ - unsigned long kva, ret; - - kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); - return ret; -} - -static inline unsigned long kvirt_to_bus(unsigned long adr) -{ - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); - return ret; -} - -/* Here we want the physical address of the memory. - * This is used when initializing the contents of the - * area and marking the pages as reserved. - */ -static inline unsigned long kvirt_to_pa(unsigned long adr) -{ - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); - ret = __pa(kva); - MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); - return ret; -} - -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr, page; - - /* Round it off to PAGE_SIZE */ - size += (PAGE_SIZE - 1); - size &= ~(PAGE_SIZE - 1); - - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - page = kvirt_to_pa(adr); - mem_map_reserve(virt_to_page(__va(page))); - adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr, page; - - if (!mem) - return; - - size += (PAGE_SIZE - 1); - size &= ~(PAGE_SIZE - 1); - - adr=(unsigned long) mem; - while (size > 0) { - page = kvirt_to_pa(adr); - mem_map_unreserve(virt_to_page(__va(page))); - adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - vfree(mem); -} - -#if ENABLE_HEXDUMP -static void ibmcam_hexdump(const unsigned char *data, int len) -{ - char tmp[80]; - int i, k; - - for (i=k=0; len > 0; i++, len--) { - if (i > 0 && (i%16 == 0)) { - printk("%s\n", tmp); - k=0; - } - k += sprintf(&tmp[k], "%02x ", data[i]); - } - if (k > 0) - printk("%s\n", tmp); -} -#endif - -/* - * usb_ibmcam_overlaychar() - * - * History: - * 1/2/00 Created. - */ -void usb_ibmcam_overlaychar( - struct usb_ibmcam *ibmcam, - struct ibmcam_frame *frame, - int x, int y, int ch) -{ - static const unsigned short digits[16] = { - 0xF6DE, /* 0 */ - 0x2492, /* 1 */ - 0xE7CE, /* 2 */ - 0xE79E, /* 3 */ - 0xB792, /* 4 */ - 0xF39E, /* 5 */ - 0xF3DE, /* 6 */ - 0xF492, /* 7 */ - 0xF7DE, /* 8 */ - 0xF79E, /* 9 */ - 0x77DA, /* a */ - 0xD75C, /* b */ - 0xF24E, /* c */ - 0xD6DC, /* d */ - 0xF34E, /* e */ - 0xF348 /* f */ - }; - unsigned short digit; - int ix, iy; - - if ((ibmcam == NULL) || (frame == NULL)) - return; - - if (ch >= '0' && ch <= '9') - ch -= '0'; - else if (ch >= 'A' && ch <= 'F') - ch = 10 + (ch - 'A'); - else if (ch >= 'a' && ch <= 'f') - ch = 10 + (ch - 'a'); - else - return; - digit = digits[ch]; - - for (iy=0; iy < 5; iy++) { - for (ix=0; ix < 3; ix++) { - if (digit & 0x8000) { - IBMCAM_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF); - } - digit = digit << 1; - } - } -} +struct struct_initData { + unsigned char req; + unsigned short value; + unsigned short index; +}; /* - * usb_ibmcam_overlaystring() + * ibmcam_size_to_videosize() * - * History: - * 1/2/00 Created. + * This procedure converts module option 'size' into the actual + * videosize_t that defines the image size in pixels. We need + * simplified 'size' because user wants a simple enumerated list + * of choices, not an infinite set of possibilities. */ -void usb_ibmcam_overlaystring( - struct usb_ibmcam *ibmcam, - struct ibmcam_frame *frame, - int x, int y, const char *str) +static videosize_t ibmcam_size_to_videosize(int size) { - while (*str) { - usb_ibmcam_overlaychar(ibmcam, frame, x, y, *str); - str++; - x += 4; /* 3 pixels character + 1 space */ + videosize_t vs = VIDEOSIZE_352x288; + RESTRICT_TO_RANGE(size, 0, (SIZE_LastItem-1)); + switch (size) { + case SIZE_128x96: + vs = VIDEOSIZE_128x96; + break; + case SIZE_160x120: + vs = VIDEOSIZE_160x120; + break; + case SIZE_176x144: + vs = VIDEOSIZE_176x144; + break; + case SIZE_320x240: + vs = VIDEOSIZE_320x240; + break; + case SIZE_352x240: + vs = VIDEOSIZE_352x240; + break; + case SIZE_352x288: + vs = VIDEOSIZE_352x288; + break; + case SIZE_640x480: + vs = VIDEOSIZE_640x480; + break; + default: + err("size=%d. is not valid", size); + break; } + return vs; } /* - * usb_ibmcam_overlaystats() - * - * Overlays important debugging information. - * - * History: - * 1/2/00 Created. - */ -void usb_ibmcam_overlaystats(struct usb_ibmcam *ibmcam, struct ibmcam_frame *frame) -{ - const int y_diff = 8; - char tmp[16]; - int x = 10; - int y = 10; - - sprintf(tmp, "%8x", ibmcam->frame_num); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", ibmcam->urb_count); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", ibmcam->urb_length); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", ibmcam->data_count); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", ibmcam->header_count); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", ibmcam->scratch_ovf_count); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", ibmcam->iso_skip_count); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", ibmcam->iso_err_count); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", ibmcam->vpic.colour); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", ibmcam->vpic.hue); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", ibmcam->vpic.brightness >> 8); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", ibmcam->vpic.contrast >> 12); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8d", ibmcam->vpic.whiteness >> 8); - usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); - y += y_diff; -} - -/* - * usb_ibmcam_testpattern() - * - * Procedure forms a test pattern (yellow grid on blue background). + * ibmcam_find_header() * - * Parameters: - * fullframe: if TRUE then entire frame is filled, otherwise the procedure - * continues from the current scanline. - * pmode 0: fill the frame with solid blue color (like on VCR or TV) - * 1: Draw a colored grid + * Locate one of supported header markers in the queue. + * Once found, remove all preceding bytes AND the marker (4 bytes) + * from the data pump queue. Whatever follows must be video lines. * * History: - * 1/2/00 Created. + * 1/21/00 Created. */ -void usb_ibmcam_testpattern(struct usb_ibmcam *ibmcam, int fullframe, int pmode) +static ParseState_t ibmcam_find_header(uvd_t *uvd) /* FIXME: Add frame here */ { - static const char proc[] = "usb_ibmcam_testpattern"; - struct ibmcam_frame *frame; - unsigned char *f; - int num_cell = 0; - int scan_length = 0; - static int num_pass = 0; - - if (ibmcam == NULL) { - printk(KERN_ERR "%s: ibmcam == NULL\n", proc); - return; - } - if ((ibmcam->curframe < 0) || (ibmcam->curframe >= IBMCAM_NUMFRAMES)) { - printk(KERN_ERR "%s: ibmcam->curframe=%d.\n", proc, ibmcam->curframe); - return; - } - - /* Grab the current frame */ - frame = &ibmcam->frame[ibmcam->curframe]; + usbvideo_frame_t *frame; + ibmcam_t *icam; - /* Optionally start at the beginning */ - if (fullframe) { - frame->curline = 0; - frame->scanlength = 0; + if ((uvd->curframe) < 0 || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { + err("ibmcam_find_header: Illegal frame %d.", uvd->curframe); + return scan_EndParse; } - - /* Form every scan line */ - for (; frame->curline < imgheight; frame->curline++) { - int i; - - f = frame->data + (imgwidth * 3 * frame->curline); - for (i=0; i < imgwidth; i++) { - unsigned char cb=0x80; - unsigned char cg = 0; - unsigned char cr = 0; - - if (pmode == 1) { - if (frame->curline % 32 == 0) - cb = 0, cg = cr = 0xFF; - else if (i % 32 == 0) { - if (frame->curline % 32 == 1) - num_cell++; - cb = 0, cg = cr = 0xFF; - } else { - cb = ((num_cell*7) + num_pass) & 0xFF; - cg = ((num_cell*5) + num_pass*2) & 0xFF; - cr = ((num_cell*3) + num_pass*3) & 0xFF; + icam = IBMCAM_T(uvd); + assert(icam != NULL); + frame = &uvd->frame[uvd->curframe]; + icam->has_hdr = 0; + switch (icam->camera_model) { + case IBMCAM_MODEL_1: + { + const int marker_len = 4; + while (RingQueue_GetLength(&uvd->dp) >= marker_len) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF) && + (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00)) + { +#if 0 /* This code helps to detect new frame markers */ + info("Header sig: 00 FF 00 %02X", RING_QUEUE_PEEK(&uvd->dp, 3)); +#endif + frame->header = RING_QUEUE_PEEK(&uvd->dp, 3); + if ((frame->header == HDRSIG_MODEL1_128x96) || + (frame->header == HDRSIG_MODEL1_176x144) || + (frame->header == HDRSIG_MODEL1_352x288)) + { +#if 0 + info("Header found."); +#endif + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); + icam->has_hdr = 1; + break; } - } else { - /* Just the blue screen */ } - - *f++ = cb; - *f++ = cg; - *f++ = cr; - scan_length += 3; + /* If we are still here then this doesn't look like a header */ + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); } + break; } - - frame->grabstate = FRAME_DONE; - frame->scanlength += scan_length; - ++num_pass; - - /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */ - usb_ibmcam_overlaystats(ibmcam, frame); -} - -static unsigned char *ibmcam_model1_find_header(unsigned char hdr_sig, unsigned char *data, int len) -{ - while (len >= 4) + case IBMCAM_MODEL_2: +case IBMCAM_MODEL_4: { - if ((data[0] == 0x00) && (data[1] == 0xFF) && (data[2] == 0x00)) - { + int marker_len = 0; + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + marker_len = 10; + break; + default: + marker_len = 2; + break; + } + while (RingQueue_GetLength(&uvd->dp) >= marker_len) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF)) + { #if 0 - /* This code helps to detect new frame markers */ - printk(KERN_DEBUG "Header sig: 00 FF 00 %02X\n", data[3]); + info("Header found."); #endif - if (data[3] == hdr_sig) { - if (debug > 2) - printk(KERN_DEBUG "Header found.\n"); - return data+4; + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); + icam->has_hdr = 1; + frame->header = HDRSIG_MODEL1_176x144; + break; } + /* If we are still here then this doesn't look like a header */ + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); } - ++data; - --len; - } - return NULL; -} - -static unsigned char *ibmcam_model2_find_header(unsigned char hdr_sig, unsigned char *data, int len) -{ - int marker_len = 0; - - switch (videosize) { - case VIDEOSIZE_176x144: - marker_len = 10; - break; - default: - marker_len = 2; break; } - while (len >= marker_len) - { - if ((data[0] == 0x00) && (data[1] == 0xFF)) - { + case IBMCAM_MODEL_3: + { /* + * Headers: (one precedes every frame). nc=no compression, + * bq=best quality bf=best frame rate. + * + * 176x144: 00 FF 02 { 0A=nc CA=bq EA=bf } + * 320x240: 00 FF 02 { 08=nc 28=bq 68=bf } + * 640x480: 00 FF 03 { 08=nc 28=bq 68=bf } + * + * Bytes '00 FF' seem to indicate header. Other two bytes + * encode the frame type. This is a set of bit fields that + * encode image size, compression type etc. These fields + * do NOT contain frame number because all frames carry + * the same header. + */ + const int marker_len = 4; + while (RingQueue_GetLength(&uvd->dp) >= marker_len) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF) && + (RING_QUEUE_PEEK(&uvd->dp, 2) != 0xFF)) + { + /* + * Combine 2 bytes of frame type into one + * easy to use value + */ + unsigned long byte3, byte4; + + byte3 = RING_QUEUE_PEEK(&uvd->dp, 2); + byte4 = RING_QUEUE_PEEK(&uvd->dp, 3); + frame->header = (byte3 << 8) | byte4; #if 0 - /* This code helps to detect new frame markers */ - static int pass = 0; - if (pass++ == 0) - ibmcam_hexdump(data, (len > 16) ? 16 : len); + info("Header found."); #endif - if (debug > 2) - printk(KERN_DEBUG "Header found.\n"); - return data+marker_len; + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); + icam->has_hdr = 1; + break; + } + /* If we are still here then this doesn't look like a header */ + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); } - ++data; - --len; + break; } - return NULL; -} - -/* How much data is left in the scratch buf? */ -#define scratch_left(x) (ibmcam->scratchlen - (int)((char *)x - (char *)ibmcam->scratch)) - -/* Grab the remaining */ -static void usb_ibmcam_align_scratch(struct usb_ibmcam *ibmcam, unsigned char *data) -{ - unsigned long left; - - left = scratch_left(data); - memmove(ibmcam->scratch, data, left); - ibmcam->scratchlen = left; -} - -/* - * usb_ibmcam_find_header() - * - * Locate one of supported header markers in the scratch buffer. - * Once found, remove all preceding bytes AND the marker (4 bytes) - * from the scratch buffer. Whatever follows must be video lines. - * - * History: - * 1/21/00 Created. - */ -static scan_state_t usb_ibmcam_find_header(struct usb_ibmcam *ibmcam) -{ - struct ibmcam_frame *frame; - unsigned char *data, *tmp; - - data = ibmcam->scratch; - frame = &ibmcam->frame[ibmcam->curframe]; - - if (ibmcam->camera_model == IBMCAM_MODEL_1) - tmp = ibmcam_model1_find_header(frame->hdr_sig, data, scratch_left(data)); - else if (ibmcam->camera_model == IBMCAM_MODEL_2) - tmp = ibmcam_model2_find_header(frame->hdr_sig, data, scratch_left(data)); - else - tmp = NULL; - - if (tmp == NULL) { - /* No header - entire scratch buffer is useless! */ - if (debug > 2) - printk(KERN_DEBUG "Skipping frame, no header\n"); - ibmcam->scratchlen = 0; + default: + break; + } + if (!icam->has_hdr) { + if (uvd->debug > 2) + info("Skipping frame, no header"); return scan_EndParse; } - /* Header found */ - data = tmp; - ibmcam->has_hdr = 1; - ibmcam->header_count++; - frame->scanstate = STATE_LINES; + /* Header found */ + icam->has_hdr = 1; + uvd->stats.header_count++; + frame->scanstate = ScanState_Lines; frame->curline = 0; if (flags & FLAGS_FORCE_TESTPATTERN) { - usb_ibmcam_testpattern(ibmcam, 1, 1); + usbvideo_TestPattern(uvd, 1, 1); return scan_NextFrame; } - usb_ibmcam_align_scratch(ibmcam, data); return scan_Continue; } /* - * usb_ibmcam_parse_lines() + * ibmcam_parse_lines() * - * Parse one line (TODO: more than one!) from the scratch buffer, put - * decoded RGB value into the current frame buffer and add the written - * number of bytes (RGB) to the *pcopylen. + * Parse one line (interlaced) from the buffer, put + * decoded RGB value into the current frame buffer + * and add the written number of bytes (RGB) to + * the *pcopylen. * * History: - * 1/21/00 Created. + * 21-Jan-2000 Created. + * 12-Oct-2000 Reworked to reflect interlaced nature of the data. */ -static scan_state_t usb_ibmcam_parse_lines(struct usb_ibmcam *ibmcam, long *pcopylen) +static ParseState_t ibmcam_parse_lines( + uvd_t *uvd, + usbvideo_frame_t *frame, + long *pcopylen) { - struct ibmcam_frame *frame; - unsigned char *data, *f, *chromaLine; - unsigned int len; - const int v4l_linesize = imgwidth * V4L_BYTES_PER_PIXEL; /* V4L line offset */ - const int hue_corr = (ibmcam->vpic.hue - 0x8000) >> 10; /* -32..+31 */ + unsigned char *f; + ibmcam_t *icam; + unsigned int len, scanLength, scanHeight, order_uv, order_yc; + int v4l_linesize; /* V4L line offset */ + const int hue_corr = (uvd->vpic.hue - 0x8000) >> 10; /* -32..+31 */ const int hue2_corr = (hue_correction - 128) / 4; /* -32..+31 */ const int ccm = 128; /* Color correction median - see below */ - int y, u, v, i, frame_done=0, mono_plane, color_corr; - - color_corr = (ibmcam->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + int y, u, v, i, frame_done=0, color_corr; + static unsigned char lineBuffer[640*3]; + unsigned const char *chromaLine, *lumaLine; + + assert(uvd != NULL); + assert(frame != NULL); + icam = IBMCAM_T(uvd); + assert(icam != NULL); + color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1); - data = ibmcam->scratch; - frame = &ibmcam->frame[ibmcam->curframe]; - len = frame->frmwidth * 3; /* 1 line of mono + 1 line of color */ - /*printk(KERN_DEBUG "len=%d. left=%d.\n",len,scratch_left(data));*/ + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + + if (IBMCAM_T(uvd)->camera_model == IBMCAM_MODEL_4) { + /* Model 4 frame markers do not carry image size identification */ + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + case VIDEOSIZE_160x120: + case VIDEOSIZE_176x144: + scanLength = VIDEOSIZE_X(uvd->videosize); + scanHeight = VIDEOSIZE_Y(uvd->videosize); + break; + default: + err("ibmcam_parse_lines: Wrong mode."); + return scan_Out; + } + order_yc = 1; /* order_yc: true=Yc false=cY ('c'=either U or V) */ + order_uv = 1; /* Always true in this algorithm */ + } else { + switch (frame->header) { + case HDRSIG_MODEL1_128x96: + scanLength = 128; + scanHeight = 96; + order_uv = 1; /* U Y V Y ... */ + break; + case HDRSIG_MODEL1_176x144: + scanLength = 176; + scanHeight = 144; + order_uv = 1; /* U Y V Y ... */ + break; + case HDRSIG_MODEL1_352x288: + scanLength = 352; + scanHeight = 288; + order_uv = 0; /* Y V Y V ... */ + break; + default: + err("Unknown header signature 00 FF 00 %02lX", frame->header); + return scan_NextFrame; + } + /* order_yc: true=Yc false=cY ('c'=either U or V) */ + order_yc = (IBMCAM_T(uvd)->camera_model == IBMCAM_MODEL_2); + } - mono_plane = ((frame->curline & 1) == 0); + len = scanLength * 3; + assert(len <= sizeof(lineBuffer)); /* - * Lines are organized this way (or are they?) + * Lines are organized this way: * * I420: * ~~~~ + * * ___________________________________ * |-----Y-----|---UVUVUV...UVUV-----| \ * |-----------+---------------------| \ - * |<-- 176 -->|<------ 176*2 ------>| Total 72. pairs of lines - * |... ... ...| / - * |___________|_____________________| / - * - odd line- ------- even line --- - * - * another format: - * ~~~~~~~~~~~~~~ - * ___________________________________ - * |-----Y-----|---UVUVUV...UVUV-----| \ - * |-----------+---------------------| \ - * |<-- 352 -->|<------ 352*2 ------>| Total 144. pairs of lines - * |... ... ...| / + * |<-- 176 -->|<------ 176*2 ------>| Total 72. lines (interlaced) + * |... ... | ... | / + * |<-- 352 -->|<------ 352*2 ------>| Total 144. lines (interlaced) * |___________|_____________________| / - * - odd line- ------- even line --- + * \ \ + * lumaLine chromaLine */ /* Make sure there's enough data for the entire line */ - if (scratch_left(data) < (len+1024)) { - /*printk(KERN_DEBUG "out of data, need %u.\n", len);*/ + if (RingQueue_GetLength(&uvd->dp) < len) return scan_Out; - } -#if 0 - { /* This code prints beginning of the source frame */ - static int pass = 0; - if ((pass++ % 3000) == 0) - ibmcam_hexdump(data, 16); - } -#endif + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); -#if 0 - if (frame->curline == 10 || frame->curline == 11) { - /* This code prints beginning of 10th (mono), 11th (chroma) line */ - static int pass = 0; - if ((pass % 100) == 0) - ibmcam_hexdump(data, 16); - if (frame->curline == 11) - pass++; - } -#endif /* * Make sure that our writing into output buffer * will not exceed the buffer. Mind that we may write * not into current output scanline but in several after * it as well (if we enlarge image vertically.) */ - if ((frame->curline + 1) >= V4L_FRAME_HEIGHT) + if ((frame->curline + 2) >= VIDEOSIZE_Y(frame->request)) return scan_NextFrame; /* - * Now we are sure that entire line (representing all 'frame->frmwidth' - * pixels from the camera) is available in the scratch buffer. We - * start copying the line left-aligned to the V4L buffer (which - * might be larger - not smaller, hopefully). If the camera - * line is shorter then we should pad the V4L buffer with something - * (black in this case) to complete the line. + * Now we are sure that entire line (representing all 'scanLength' + * pixels from the camera) is available in the buffer. We + * start copying the line left-aligned to the V4L buffer. + * If the camera line is shorter then we should pad the V4L + * buffer with something (black) to complete the line. */ + assert(frame->data != NULL); f = frame->data + (v4l_linesize * frame->curline); /* - * chromaLine points to 1st pixel of the line with chrominance. - * If current line is monochrome then chromaLine points to next - * line after monochrome one. If current line has chrominance - * then chromaLine points to this very line. Such method allows - * to access chrominance data uniformly. - * * To obtain chrominance data from the 'chromaLine' use this: * v = chromaLine[0]; // 0-1:[0], 2-3:[4], 4-5:[8]... * u = chromaLine[2]; // 0-1:[2], 2-3:[6], 4-5:[10]... @@ -778,40 +513,16 @@ * v_index = (i >> 1) << 2; * u_index = (i >> 1) << 2 + 2; * - * where 'i' is the column number [0..frame->frmwidth-1] + * where 'i' is the column number [0..VIDEOSIZE_X(frame->request)-1] */ - chromaLine = data; - if (mono_plane) - chromaLine += frame->frmwidth; - - for (i = 0; i < frame->frmwidth; i++, data += (mono_plane ? 1 : 2)) + lumaLine = lineBuffer; + chromaLine = lineBuffer + scanLength; + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { unsigned char rv, gv, bv; /* RGB components */ - /* - * Search for potential Start-Of-Frame marker. It should - * not be here, of course, but if your formats don't match - * you might exceed the frame. We must match the marker to - * each byte of multi-byte data element if it is multi-byte. - */ -#if 1 - if ((ibmcam->camera_model == IBMCAM_MODEL_1) && (scratch_left(data) >= (4+2))) { - unsigned char *dp; - int j; - - for (j=0, dp=data; j < 2; j++, dp++) { - if ((dp[0] == 0x00) && (dp[1] == 0xFF) && - (dp[2] == 0x00) && (dp[3] == frame->hdr_sig)) { - ibmcam->has_hdr = 2; - frame_done++; - break; - } - } - } -#endif - /* Check for various visual debugging hints (colorized pixels) */ - if ((flags & FLAGS_DISPLAY_HINTS) && (ibmcam->has_hdr)) { + if ((flags & FLAGS_DISPLAY_HINTS) && (icam->has_hdr)) { /* * This is bad and should not happen. This means that * we somehow overshoot the line and encountered new @@ -819,7 +530,7 @@ * of whack. This cyan dot will help you to figure * out where exactly the new frame arrived. */ - if (ibmcam->has_hdr == 1) { + if (icam->has_hdr == 1) { bv = 0; /* Yellow marker */ gv = 0xFF; rv = 0xFF; @@ -828,15 +539,27 @@ gv = 0xFF; rv = 0; } - ibmcam->has_hdr = 0; + icam->has_hdr = 0; goto make_pixel; } - if (mono_plane || frame->order_yc) - y = data[0]; - else - y = data[1]; + /* + * Check if we are still in range. We may be out of range if our + * V4L canvas is wider or taller than the camera "native" image. + * Then we quickly fill the remainder of the line with zeros to + * make black color and quit the horizontal scanning loop. + */ + if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) { + const int j = i * V4L_BYTES_PER_PIXEL; +#if USES_IBMCAM_PUTPIXEL + /* Refresh 'f' because we don't use it much with PUTPIXEL */ + f = frame->data + (v4l_linesize * frame->curline) + j; +#endif + memset(f, 0, v4l_linesize - j); + break; + } + y = lumaLine[i]; if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */ rv = gv = bv = y; else { @@ -845,11 +568,11 @@ off_0 = (i >> 1) << 2; off_2 = off_0 + 2; - if (frame->order_yc) { + if (order_yc) { off_0++; off_2++; } - if (!frame->order_uv) { + if (!order_uv) { off_0 += 2; off_2 -= 2; } @@ -877,7 +600,7 @@ * (in this order). */ #if USES_IBMCAM_PUTPIXEL - IBMCAM_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); + RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); #else *f++ = bv; *f++ = gv; @@ -899,18 +622,19 @@ * may fill more than one output scanline if we do vertical * enlargement. */ - frame->curline++; - *pcopylen += v4l_linesize; - usb_ibmcam_align_scratch(ibmcam, data); + frame->curline += 2; + if (pcopylen != NULL) + *pcopylen += 2 * v4l_linesize; + frame->deinterlace = Deinterlace_FillOddLines; - if (frame_done || (frame->curline >= frame->frmheight)) + if (frame_done || (frame->curline >= VIDEOSIZE_Y(frame->request))) return scan_NextFrame; else return scan_Continue; } /* - * usb_ibmcam_model2_parse_lines() + * ibmcam_model2_320x240_parse_lines() * * This procedure deals with a weird RGB format that is produced by IBM * camera model 2 in modes 320x240 and above; 'x' below is 159 or 175, @@ -918,15 +642,15 @@ * * <--- 160 or 176 pairs of RA,RB bytes -----> * *-----------------------------------------* \ - * | RA0 | RB0 | RA1 | RB1 | ... | RAx | RBx | \ - * |-----+-----+-----+-----+ ... +-----+-----| *- This is pair of horizontal lines, - * | B0 | G0 | B1 | G1 | ... | Bx | Gx | / total 240 or 288 lines (120 or 144 - * |=====+=====+=====+=====+ ... +=====+=====| / such pairs). + * | RA0 | RB0 | RA1 | RB1 | ... | RAx | RBx | \ This is pair of horizontal lines, + * |-----+-----+-----+-----+ ... +-----+-----| *- or one interlaced line, total + * | B0 | G0 | B1 | G1 | ... | Bx | Gx | / 120 or 144 such pairs which yield + * |=====+=====+=====+=====+ ... +=====+=====| / 240 or 288 lines after deinterlacing. * * Each group of FOUR bytes (RAi, RBi, Bi, Gi) where i=0..frame_width/2-1 * defines ONE pixel. Therefore this format yields 176x144 "decoded" * resolution at best. I do not know why camera sends such format - the - * previous model just used I420 and everyone was happy. + * previous model (1) just used interlaced I420 and everyone was happy. * * I do not know what is the difference between RAi and RBi bytes. Both * seemingly represent R component, but slightly vary in value (so that @@ -934,49 +658,58 @@ * them both as R component in attempt to at least partially recover the * lost resolution. */ -static scan_state_t usb_ibmcam_model2_parse_lines(struct usb_ibmcam *ibmcam, long *pcopylen) +static ParseState_t ibmcam_model2_320x240_parse_lines( + uvd_t *uvd, + usbvideo_frame_t *frame, + long *pcopylen) { - struct ibmcam_frame *frame; - unsigned char *data, *f, *la, *lb; + unsigned char *f, *la, *lb; unsigned int len; - const int v4l_linesize = imgwidth * V4L_BYTES_PER_PIXEL; /* V4L line offset */ + int v4l_linesize; /* V4L line offset */ int i, j, frame_done=0, color_corr; + int scanLength, scanHeight; + static unsigned char lineBuffer[352*2]; - color_corr = (ibmcam->vpic.colour) >> 8; /* 0..+255 */ - - data = ibmcam->scratch; - frame = &ibmcam->frame[ibmcam->curframe]; - - /* Here we deal with pairs of horizontal lines */ - - len = frame->frmwidth * 2; /* 2 lines */ - /*printk(KERN_DEBUG "len=%d. left=%d.\n",len,scratch_left(data));*/ - - /* Make sure there's enough data for the entire line */ - if (scratch_left(data) < (len+32)) { - /*printk(KERN_DEBUG "out of data, need %u.\n", len);*/ - return scan_Out; + switch (uvd->videosize) { + case VIDEOSIZE_320x240: + case VIDEOSIZE_352x240: + case VIDEOSIZE_352x288: + scanLength = VIDEOSIZE_X(uvd->videosize); + scanHeight = VIDEOSIZE_Y(uvd->videosize); + break; + default: + err("ibmcam_model2_320x240_parse_lines: Wrong mode."); + return scan_Out; } + color_corr = (uvd->vpic.colour) >> 8; /* 0..+255 */ + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + + len = scanLength * 2; /* See explanation above */ + assert(len <= sizeof(lineBuffer)); + + /* Make sure there's enough data for the entire line */ + if (RingQueue_GetLength(&uvd->dp) < len) + return scan_Out; + + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); + /* * Make sure that our writing into output buffer * will not exceed the buffer. Mind that we may write * not into current output scanline but in several after * it as well (if we enlarge image vertically.) */ - if ((frame->curline + 1) >= V4L_FRAME_HEIGHT) + if ((frame->curline + 2) >= VIDEOSIZE_Y(frame->request)) return scan_NextFrame; - if ((frame->curline & 1) == 0) { - la = data; - lb = data + frame->frmwidth; - } else { - la = data + frame->frmwidth; - lb = data; - } + la = lineBuffer; + lb = lineBuffer + scanLength; /* - * Now we are sure that entire line (representing all 'frame->frmwidth' + * Now we are sure that entire line (representing all + * VIDEOSIZE_X(frame->request) * pixels from the camera) is available in the scratch buffer. We * start copying the line left-aligned to the V4L buffer (which * might be larger - not smaller, hopefully). If the camera @@ -986,14 +719,14 @@ f = frame->data + (v4l_linesize * frame->curline); /* Fill the 2-line strip */ - for (i = 0; i < frame->frmwidth; i++) { + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { int y, rv, gv, bv; /* RGB components */ j = i & (~1); /* Check for various visual debugging hints (colorized pixels) */ - if ((flags & FLAGS_DISPLAY_HINTS) && (ibmcam->has_hdr)) { - if (ibmcam->has_hdr == 1) { + if ((flags & FLAGS_DISPLAY_HINTS) && (IBMCAM_T(uvd)->has_hdr)) { + if (IBMCAM_T(uvd)->has_hdr == 1) { bv = 0; /* Yellow marker */ gv = 0xFF; rv = 0xFF; @@ -1002,11 +735,27 @@ gv = 0xFF; rv = 0; } - ibmcam->has_hdr = 0; + IBMCAM_T(uvd)->has_hdr = 0; goto make_pixel; } /* + * Check if we are still in range. We may be out of range if our + * V4L canvas is wider or taller than the camera "native" image. + * Then we quickly fill the remainder of the line with zeros to + * make black color and quit the horizontal scanning loop. + */ + if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) { + const int j = i * V4L_BYTES_PER_PIXEL; +#if USES_IBMCAM_PUTPIXEL + /* Refresh 'f' because we don't use it much with PUTPIXEL */ + f = frame->data + (v4l_linesize * frame->curline) + j; +#endif + memset(f, 0, v4l_linesize - j); + break; + } + + /* * Here I use RA and RB components, one per physical pixel. * This causes fine vertical grid on the picture but may improve * horizontal resolution. If you prefer replicating, use this: @@ -1045,8 +794,7 @@ } make_pixel: - IBMCAM_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); - IBMCAM_PUTPIXEL(frame, i, frame->curline+1, rv, gv, bv); + RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); } /* * Account for number of bytes that we wrote into output V4L frame. @@ -1056,257 +804,342 @@ */ frame->curline += 2; *pcopylen += v4l_linesize * 2; - data += frame->frmwidth * 2; - usb_ibmcam_align_scratch(ibmcam, data); + frame->deinterlace = Deinterlace_FillOddLines; - if (frame_done || (frame->curline >= frame->frmheight)) + if (frame_done || (frame->curline >= VIDEOSIZE_Y(frame->request))) return scan_NextFrame; else return scan_Continue; } -/* - * ibmcam_parse_data() - * - * Generic routine to parse the scratch buffer. It employs either - * usb_ibmcam_find_header() or usb_ibmcam_parse_lines() to do most - * of work. - * - * History: - * 1/21/00 Created. - */ -static void ibmcam_parse_data(struct usb_ibmcam *ibmcam) +static ParseState_t ibmcam_model3_parse_lines( + uvd_t *uvd, + usbvideo_frame_t *frame, + long *pcopylen) { - struct ibmcam_frame *frame; - unsigned char *data = ibmcam->scratch; - scan_state_t newstate; - long copylen = 0; - - /* Grab the current frame and the previous frame */ - frame = &ibmcam->frame[ibmcam->curframe]; + unsigned char *data; + const unsigned char *color; + unsigned int len; + int v4l_linesize; /* V4L line offset */ + const int hue_corr = (uvd->vpic.hue - 0x8000) >> 10; /* -32..+31 */ + const int hue2_corr = (hue_correction - 128) / 4; /* -32..+31 */ + const int ccm = 128; /* Color correction median - see below */ + int i, u, v, rw, data_w=0, data_h=0, color_corr; + static unsigned char lineBuffer[640*3]; - /* printk(KERN_DEBUG "parsing %u.\n", ibmcam->scratchlen); */ + color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1); - while (1) { + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; - newstate = scan_Out; - if (scratch_left(data)) { - if (frame->scanstate == STATE_SCANNING) - newstate = usb_ibmcam_find_header(ibmcam); - else if (frame->scanstate == STATE_LINES) { - if ((ibmcam->camera_model == IBMCAM_MODEL_2) && - (videosize >= VIDEOSIZE_352x288)) { - newstate = usb_ibmcam_model2_parse_lines(ibmcam, ©len); - } - else { - newstate = usb_ibmcam_parse_lines(ibmcam, ©len); - } - } - } - if (newstate == scan_Continue) - continue; - else if ((newstate == scan_NextFrame) || (newstate == scan_Out)) - break; - else - return; /* scan_EndParse */ + /* The header tells us what sort of data is in this frame */ + switch (frame->header) { + /* + * Uncompressed modes (that are easy to decode). + */ + case 0x0308: + data_w = 640; + data_h = 480; + break; + case 0x0208: + data_w = 320; + data_h = 240; + break; + case 0x020A: + data_w = 160; + data_h = 120; + break; + /* + * Compressed modes (ViCE - that I don't know how to decode). + */ + case 0x0328: /* 640x480, best quality compression */ + case 0x0368: /* 640x480, best frame rate compression */ + case 0x0228: /* 320x240, best quality compression */ + case 0x0268: /* 320x240, best frame rate compression */ + case 0x02CA: /* 160x120, best quality compression */ + case 0x02EA: /* 160x120, best frame rate compression */ + /* Do nothing with this - not supported */ + err("Unsupported mode $%04lx", frame->header); + return scan_NextFrame; + default: + /* Catch unknown headers, may help in learning new headers */ + err("Strange frame->header=$%08lx", frame->header); + return scan_NextFrame; } - if (newstate == scan_NextFrame) { - frame->grabstate = FRAME_DONE; - ibmcam->curframe = -1; - ibmcam->frame_num++; - - /* Optionally display statistics on the screen */ - if (flags & FLAGS_OVERLAY_STATS) - usb_ibmcam_overlaystats(ibmcam, frame); - - /* This will cause the process to request another frame. */ - if (waitqueue_active(&frame->wq)) - wake_up_interruptible(&frame->wq); + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Note that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 1) >= data_h) { + if (uvd->debug >= 3) + info("Reached line %d. (frame is done)", frame->curline); + return scan_NextFrame; } - /* Update the frame's uncompressed length. */ - frame->scanlength += copylen; -} + /* Make sure there's enough data for the entire line */ + len = 3 * data_w; /* */ + assert(len <= sizeof(lineBuffer)); -/* - * Make all of the blocks of data contiguous - */ -static int ibmcam_compress_isochronous(struct usb_ibmcam *ibmcam, urb_t *urb) -{ - unsigned char *cdata, *data, *data0; - int i, totlen = 0; + /* Make sure there's enough data for the entire line */ + if (RingQueue_GetLength(&uvd->dp) < len) + return scan_Out; - data = data0 = ibmcam->scratch + ibmcam->scratchlen; - for (i = 0; i < urb->number_of_packets; i++) { - int n = urb->iso_frame_desc[i].actual_length; - int st = urb->iso_frame_desc[i].status; - - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); - /* Detect and ignore errored packets */ - if (st < 0) { - if (debug >= 1) { - printk(KERN_ERR "ibmcam data error: [%d] len=%d, status=%X\n", - i, n, st); - } - ibmcam->iso_err_count++; - continue; - } + data = lineBuffer; + color = data + data_w; /* Point to where color planes begin */ - /* Detect and ignore empty packets */ - if (n <= 0) { - ibmcam->iso_skip_count++; - continue; - } + /* Bottom-to-top scanning */ + rw = (int)VIDEOSIZE_Y(frame->request) - (int)(frame->curline) - 1; + RESTRICT_TO_RANGE(rw, 0, VIDEOSIZE_Y(frame->request)-1); - /* - * If camera continues to feed us with data but there is no - * consumption (if, for example, V4L client fell asleep) we - * may overflow the buffer. We have to move old data over to - * free room for new data. This is bad for old data. If we - * just drop new data then it's bad for new data... choose - * your favorite evil here. - */ - if ((ibmcam->scratchlen + n) > scratchbufsize) { -#if 0 - ibmcam->scratch_ovf_count++; - if (debug >= 3) - printk(KERN_ERR "ibmcam: scratch buf overflow! " - "scr_len: %d, n: %d\n", ibmcam->scratchlen, n ); - return totlen; -#else - int mv; + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { + int y, rv, gv, bv; /* RGB components */ - ibmcam->scratch_ovf_count++; - if (debug >= 3) { - printk(KERN_ERR "ibmcam: scratch buf overflow! " - "scr_len: %d, n: %d\n", ibmcam->scratchlen, n ); - } - mv = (ibmcam->scratchlen + n) - scratchbufsize; - if (ibmcam->scratchlen >= mv) { - int newslen = ibmcam->scratchlen - mv; - memmove(ibmcam->scratch, ibmcam->scratch + mv, newslen); - ibmcam->scratchlen = newslen; - data = data0 = ibmcam->scratch + ibmcam->scratchlen; - } else { - printk(KERN_ERR "ibmcam: scratch buf too small\n"); - return totlen; + if (i < data_w) { + y = data[i]; /* Luminosity is the first line */ + + /* Apply static color correction */ + u = color[i*2] + hue_corr; + v = color[i*2 + 1] + hue2_corr; + + /* Apply color correction */ + if (color_corr != 0) { + /* Magnify up to 2 times, reduce down to zero saturation */ + u = 128 + ((ccm + color_corr) * (u - 128)) / ccm; + v = 128 + ((ccm + color_corr) * (v - 128)) / ccm; } -#endif - } + } else + y = 0, u = v = 128; - /* Now we know that there is enough room in scratch buffer */ - memmove(data, cdata, n); - data += n; - totlen += n; - ibmcam->scratchlen += n; + YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv); + RGB24_PUTPIXEL(frame, i, rw, rv, gv, bv); /* Done by deinterlacing now */ } -#if 0 - if (totlen > 0) { - static int foo=0; - if (foo < 1) { - printk(KERN_DEBUG "+%d.\n", totlen); - ibmcam_hexdump(data0, (totlen > 64) ? 64:totlen); - ++foo; + frame->deinterlace = Deinterlace_FillEvenLines; + + /* + * Account for number of bytes that we wrote into output V4L frame. + * We do it here, after we are done with the scanline, because we + * may fill more than one output scanline if we do vertical + * enlargement. + */ + frame->curline += 2; + *pcopylen += 2 * v4l_linesize; + + if (frame->curline >= VIDEOSIZE_Y(frame->request)) { + if (uvd->debug >= 3) { + info("All requested lines (%ld.) done.", + VIDEOSIZE_Y(frame->request)); } - } -#endif - return totlen; + return scan_NextFrame; + } else + return scan_Continue; } -static void ibmcam_isoc_irq(struct urb *urb) +/* + * ibmcam_model4_128x96_parse_lines() + * + * This decoder is for one strange data format that is produced by Model 4 + * camera only in 128x96 mode. This is RGB format and here is its description. + * First of all, this is non-interlaced stream, meaning that all scan lines + * are present in the datastream. There are 96 consecutive blocks of data + * that describe all 96 lines of the image. Each block is 5*128 bytes long + * and carries R, G, B components. The format of the block is shown in the + * code below. First 128*2 bytes are interleaved R and G components. Then + * we have a gap (junk data) 64 bytes long. Then follow B and something + * else, also interleaved (this makes another 128*2 bytes). After that + * probably another 64 bytes of junk follow. + * + * History: + * 10-Feb-2001 Created. + */ +static ParseState_t ibmcam_model4_128x96_parse_lines( + uvd_t *uvd, + usbvideo_frame_t *frame, + long *pcopylen) { - int len; - struct usb_ibmcam *ibmcam = urb->context; - struct ibmcam_sbuf *sbuf; - int i; + const unsigned char *data_rv, *data_gv, *data_bv; + unsigned int len; + int i, v4l_linesize; /* V4L line offset */ + const int data_w=128, data_h=96; + static unsigned char lineBuffer[128*5]; - /* We don't want to do anything if we are about to be removed! */ - if (!IBMCAM_IS_OPERATIONAL(ibmcam)) - return; + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; -#if 0 - if (urb->actual_length > 0) { - printk(KERN_DEBUG "ibmcam_isoc_irq: %p status %d, " - " errcount = %d, length = %d\n", urb, urb->status, - urb->error_count, urb->actual_length); - } else { - static int c = 0; - if (c++ % 100 == 0) - printk(KERN_DEBUG "ibmcam_isoc_irq: no data\n"); + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Note that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 1) >= data_h) { + if (uvd->debug >= 3) + info("Reached line %d. (frame is done)", frame->curline); + return scan_NextFrame; } -#endif - if (!ibmcam->streaming) { - if (debug >= 1) - printk(KERN_DEBUG "ibmcam: oops, not streaming, but interrupt\n"); - return; - } - - sbuf = &ibmcam->sbuf[ibmcam->cursbuf]; + /* + * RGRGRG .... RGRG_____________B?B?B? ... B?B?____________ + * <---- 128*2 ---><---- 64 ---><--- 128*2 ---><--- 64 ---> + */ + + /* Make sure there's enough data for the entire line */ + len = 5 * data_w; + assert(len <= sizeof(lineBuffer)); + + /* Make sure there's enough data for the entire line */ + if (RingQueue_GetLength(&uvd->dp) < len) + return scan_Out; - /* Copy the data received into our scratch buffer */ - len = ibmcam_compress_isochronous(ibmcam, urb); + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); - ibmcam->urb_count++; - ibmcam->urb_length = len; - ibmcam->data_count += len; - -#if 0 /* This code prints few initial bytes of ISO data: used to decode markers */ - if (ibmcam->urb_count % 64 == 1) { - if (ibmcam->urb_count == 1) { - ibmcam_hexdump(ibmcam->scratch, - (ibmcam->scratchlen > 32) ? 32 : ibmcam->scratchlen); + data_rv = lineBuffer; + data_gv = lineBuffer + 1; + data_bv = lineBuffer + data_w*2 + data_w/2; + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { + int rv, gv, bv; /* RGB components */ + if (i < data_w) { + const int j = i * 2; + gv = data_rv[j]; + rv = data_gv[j]; + bv = data_bv[j]; + if (flags & FLAGS_MONOCHROME) { + unsigned long y; + y = rv + gv + bv; + y /= 3; + if (y > 0xFF) + y = 0xFF; + rv = gv = bv = (unsigned char) y; + } + } else { + rv = gv = bv = 0; } + RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); } -#endif + frame->deinterlace = Deinterlace_None; + frame->curline++; + *pcopylen += v4l_linesize; - /* If we collected enough data let's parse! */ - if (ibmcam->scratchlen) { - /* If we don't have a frame we're current working on, complain */ - if (ibmcam->curframe >= 0) - ibmcam_parse_data(ibmcam); - else { - if (debug >= 1) - printk(KERN_DEBUG "ibmcam: received data, but no frame available\n"); + if (frame->curline >= VIDEOSIZE_Y(frame->request)) { + if (uvd->debug >= 3) { + info("All requested lines (%ld.) done.", + VIDEOSIZE_Y(frame->request)); + } + return scan_NextFrame; + } else + return scan_Continue; +} + +/* + * ibmcam_ProcessIsocData() + * + * Generic routine to parse the ring queue data. It employs either + * ibmcam_find_header() or ibmcam_parse_lines() to do most + * of work. + * + * History: + * 1/21/00 Created. + */ +void ibmcam_ProcessIsocData(uvd_t *uvd, usbvideo_frame_t *frame) +{ + ParseState_t newstate; + long copylen = 0; + int mod = IBMCAM_T(uvd)->camera_model; + + while (1) { + newstate = scan_Out; + if (RingQueue_GetLength(&uvd->dp) > 0) { + if (frame->scanstate == ScanState_Scanning) { + newstate = ibmcam_find_header(uvd); + } else if (frame->scanstate == ScanState_Lines) { + if ((mod == IBMCAM_MODEL_2) && + ((uvd->videosize == VIDEOSIZE_352x288) || + (uvd->videosize == VIDEOSIZE_320x240) || + (uvd->videosize == VIDEOSIZE_352x240))) + { + newstate = ibmcam_model2_320x240_parse_lines( + uvd, frame, ©len); + } else if (mod == IBMCAM_MODEL_4) { + /* + * Model 4 cameras (IBM NetCamera) use Model 2 decoder (RGB) + * for 320x240 and above; 160x120 and 176x144 uses Model 1 + * decoder (YUV), and 128x96 mode uses ??? + */ + if ((uvd->videosize == VIDEOSIZE_352x288) || + (uvd->videosize == VIDEOSIZE_320x240) || + (uvd->videosize == VIDEOSIZE_352x240)) + { + newstate = ibmcam_model2_320x240_parse_lines(uvd, frame, ©len); + } else if (uvd->videosize == VIDEOSIZE_128x96) { + newstate = ibmcam_model4_128x96_parse_lines(uvd, frame, ©len); + } else { + newstate = ibmcam_parse_lines(uvd, frame, ©len); + } + } else if (mod == IBMCAM_MODEL_3) { + newstate = ibmcam_model3_parse_lines(uvd, frame, ©len); + } else { + newstate = ibmcam_parse_lines(uvd, frame, ©len); + } + } } + if (newstate == scan_Continue) + continue; + else if ((newstate == scan_NextFrame) || (newstate == scan_Out)) + break; + else + return; /* scan_EndParse */ } - 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; + if (newstate == scan_NextFrame) { + frame->frameState = FrameState_Done; + uvd->curframe = -1; + uvd->stats.frame_num++; + if ((mod == IBMCAM_MODEL_2) || (mod == IBMCAM_MODEL_4)) { + /* Need software contrast adjustment for those cameras */ + frame->flags |= USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST; + } } - /* Move to the next sbuf */ - ibmcam->cursbuf = (ibmcam->cursbuf + 1) % IBMCAM_NUMSBUF; + /* Update the frame's uncompressed length. */ + frame->seqRead_Length += copylen; - return; +#if 0 + { + static unsigned char j=0; + memset(frame->data, j++, uvd->max_frame_size); + frame->frameState = FrameState_Ready; + } +#endif } /* - * usb_ibmcam_veio() + * ibmcam_veio() * * History: * 1/27/00 Added check for dev == NULL; this happens if camera is unplugged. */ -static int usb_ibmcam_veio( - struct usb_ibmcam *ibmcam, +static int ibmcam_veio( + uvd_t *uvd, unsigned char req, unsigned short value, unsigned short index) { - static const char proc[] = "usb_ibmcam_veio"; + static const char proc[] = "ibmcam_veio"; unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */; int i; - if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + if (!CAMERA_IS_OPERATIONAL(uvd)) return 0; if (req == 1) { i = usb_control_msg( - ibmcam->dev, - usb_rcvctrlpipe(ibmcam->dev, 0), + uvd->dev, + usb_rcvctrlpipe(uvd->dev, 0), req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, value, @@ -1315,15 +1148,15 @@ sizeof(cp), HZ); #if 0 - printk(KERN_DEBUG "USB => %02x%02x%02x%02x%02x%02x%02x%02x " - "(req=$%02x val=$%04x ind=$%04x)\n", + info("USB => %02x%02x%02x%02x%02x%02x%02x%02x " + "(req=$%02x val=$%04x ind=$%04x)", cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7], req, value, index); #endif } else { i = usb_control_msg( - ibmcam->dev, - usb_sndctrlpipe(ibmcam->dev, 0), + uvd->dev, + usb_sndctrlpipe(uvd->dev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, value, @@ -1333,15 +1166,15 @@ HZ); } if (i < 0) { - printk(KERN_ERR "%s: ERROR=%d. Camera stopped - " - "reconnect or reload driver.\n", proc, i); - ibmcam->last_error = i; + err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.", + proc, i); + uvd->last_error = i; } return i; } /* - * usb_ibmcam_calculate_fps() + * ibmcam_calculate_fps() * * This procedure roughly calculates the real frame rate based * on FPS code (framerate=NNN option). Actual FPS differs @@ -1358,13 +1191,13 @@ * History: * 1/18/00 Created. */ -static int usb_ibmcam_calculate_fps(void) +static int ibmcam_calculate_fps(uvd_t *uvd) { return 3 + framerate*4 + framerate/2; } /* - * usb_ibmcam_send_FF_04_02() + * ibmcam_send_FF_04_02() * * This procedure sends magic 3-command prefix to the camera. * The purpose of this prefix is not known. @@ -1372,108 +1205,140 @@ * History: * 1/2/00 Created. */ -static void usb_ibmcam_send_FF_04_02(struct usb_ibmcam *ibmcam) +static void ibmcam_send_FF_04_02(uvd_t *uvd) +{ + ibmcam_veio(uvd, 0, 0x00FF, 0x0127); + ibmcam_veio(uvd, 0, 0x0004, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); +} + +static void ibmcam_send_00_04_06(uvd_t *uvd) { - usb_ibmcam_veio(ibmcam, 0, 0x00FF, 0x0127); - usb_ibmcam_veio(ibmcam, 0, 0x0004, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x0004, 0x0124); + ibmcam_veio(uvd, 0, 0x0006, 0x0124); } -static void usb_ibmcam_send_00_04_06(struct usb_ibmcam *ibmcam) +static void ibmcam_send_x_00(uvd_t *uvd, unsigned short x) { - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0127); - usb_ibmcam_veio(ibmcam, 0, 0x0004, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0006, 0x0124); + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); } -static void usb_ibmcam_send_x_00(struct usb_ibmcam *ibmcam, unsigned short x) +static void ibmcam_send_x_00_05(uvd_t *uvd, unsigned short x) { - usb_ibmcam_veio(ibmcam, 0, x, 0x0127); - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); + ibmcam_send_x_00(uvd, x); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); } -static void usb_ibmcam_send_x_00_05(struct usb_ibmcam *ibmcam, unsigned short x) +static void ibmcam_send_x_00_05_02(uvd_t *uvd, unsigned short x) { - usb_ibmcam_send_x_00(ibmcam, x); - usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); } -static void usb_ibmcam_send_x_00_05_02(struct usb_ibmcam *ibmcam, unsigned short x) +static void ibmcam_send_x_01_00_05(uvd_t *uvd, unsigned short x) { - usb_ibmcam_veio(ibmcam, 0, x, 0x0127); - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0001, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); } -static void usb_ibmcam_send_x_01_00_05(struct usb_ibmcam *ibmcam, unsigned short x) +static void ibmcam_send_x_00_05_02_01(uvd_t *uvd, unsigned short x) { - usb_ibmcam_veio(ibmcam, 0, x, 0x0127); - usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0124); } -static void usb_ibmcam_send_x_00_05_02_01(struct usb_ibmcam *ibmcam, unsigned short x) +static void ibmcam_send_x_00_05_02_08_01(uvd_t *uvd, unsigned short x) { - usb_ibmcam_veio(ibmcam, 0, x, 0x0127); - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124); + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0124); } -static void usb_ibmcam_send_x_00_05_02_08_01(struct usb_ibmcam *ibmcam, unsigned short x) +static void ibmcam_Packet_Format1(uvd_t *uvd, unsigned char fkey, unsigned char val) { - usb_ibmcam_veio(ibmcam, 0, x, 0x0127); - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0008, 0x0124); - usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124); + ibmcam_send_x_01_00_05(uvd, unknown_88); + ibmcam_send_x_00_05(uvd, fkey); + ibmcam_send_x_00_05_02_08_01(uvd, val); + ibmcam_send_x_00_05(uvd, unknown_88); + ibmcam_send_x_00_05_02_01(uvd, fkey); + ibmcam_send_x_00_05(uvd, unknown_89); + ibmcam_send_x_00(uvd, fkey); + ibmcam_send_00_04_06(uvd); + ibmcam_veio(uvd, 1, 0x0000, 0x0126); + ibmcam_send_FF_04_02(uvd); } -static void usb_ibmcam_Packet_Format1(struct usb_ibmcam *ibmcam, unsigned char fkey, unsigned char val) +static void ibmcam_PacketFormat2(uvd_t *uvd, unsigned char fkey, unsigned char val) { - usb_ibmcam_send_x_01_00_05 (ibmcam, unknown_88); - usb_ibmcam_send_x_00_05 (ibmcam, fkey); - usb_ibmcam_send_x_00_05_02_08_01(ibmcam, val); - usb_ibmcam_send_x_00_05 (ibmcam, unknown_88); - usb_ibmcam_send_x_00_05_02_01 (ibmcam, fkey); - usb_ibmcam_send_x_00_05 (ibmcam, unknown_89); - usb_ibmcam_send_x_00 (ibmcam, fkey); - usb_ibmcam_send_00_04_06 (ibmcam); - usb_ibmcam_veio (ibmcam, 1, 0x0000, 0x0126); - usb_ibmcam_send_FF_04_02 (ibmcam); + ibmcam_send_x_01_00_05 (uvd, unknown_88); + ibmcam_send_x_00_05 (uvd, fkey); + ibmcam_send_x_00_05_02 (uvd, val); } -static void usb_ibmcam_PacketFormat2(struct usb_ibmcam *ibmcam, unsigned char fkey, unsigned char val) +static void ibmcam_model2_Packet2(uvd_t *uvd) { - usb_ibmcam_send_x_01_00_05 (ibmcam, unknown_88); - usb_ibmcam_send_x_00_05 (ibmcam, fkey); - usb_ibmcam_send_x_00_05_02 (ibmcam, val); + ibmcam_veio(uvd, 0, 0x00ff, 0x012d); + ibmcam_veio(uvd, 0, 0xfea3, 0x0124); } -static void usb_ibmcam_model2_Packet2(struct usb_ibmcam *ibmcam) +static void ibmcam_model2_Packet1(uvd_t *uvd, unsigned short v1, unsigned short v2) { - usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x012d); - usb_ibmcam_veio(ibmcam, 0, 0xfea3, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x00ff, 0x012e); + ibmcam_veio(uvd, 0, v1, 0x012f); + ibmcam_veio(uvd, 0, 0x00ff, 0x0130); + ibmcam_veio(uvd, 0, 0xc719, 0x0124); + ibmcam_veio(uvd, 0, v2, 0x0127); + + ibmcam_model2_Packet2(uvd); } -static void usb_ibmcam_model2_Packet1(struct usb_ibmcam *ibmcam, unsigned short v1, unsigned short v2) +/* + * ibmcam_model3_Packet1() + * + * 00_0078_012d + * 00_0097_012f + * 00_d141_0124 + * 00_0096_0127 + * 00_fea8_0124 +*/ +static void ibmcam_model3_Packet1(uvd_t *uvd, unsigned short v1, unsigned short v2) { - usb_ibmcam_veio(ibmcam, 0, 0x00aa, 0x012d); - usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x012e); - usb_ibmcam_veio(ibmcam, 0, v1, 0x012f); - usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x0130); - usb_ibmcam_veio(ibmcam, 0, 0xc719, 0x0124); - usb_ibmcam_veio(ibmcam, 0, v2, 0x0127); + ibmcam_veio(uvd, 0, 0x0078, 0x012d); + ibmcam_veio(uvd, 0, v1, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, v2, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); +} - usb_ibmcam_model2_Packet2(ibmcam); +static void ibmcam_model4_BrightnessPacket(uvd_t *uvd, int i) +{ + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0026, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, i, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0038, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); } /* - * usb_ibmcam_adjust_contrast() + * ibmcam_adjust_contrast() * * The contrast value changes from 0 (high contrast) to 15 (low contrast). * This is in reverse to usual order of things (such as TV controls), so @@ -1484,30 +1349,65 @@ * History: * 1/2/00 Created. */ -static void usb_ibmcam_adjust_contrast(struct usb_ibmcam *ibmcam) +static void ibmcam_adjust_contrast(uvd_t *uvd) { - unsigned char new_contrast = ibmcam->vpic.contrast >> 12; - const int ntries = 5; + unsigned char a_contrast = uvd->vpic.contrast >> 12; + unsigned char new_contrast; - if (new_contrast >= 16) - new_contrast = 15; - new_contrast = 15 - new_contrast; - if (new_contrast != ibmcam->vpic_old.contrast) { - ibmcam->vpic_old.contrast = new_contrast; - if (ibmcam->camera_model == IBMCAM_MODEL_1) { - int i; - for (i=0; i < ntries; i++) { - usb_ibmcam_Packet_Format1(ibmcam, contrast_14, new_contrast); - usb_ibmcam_send_FF_04_02(ibmcam); - } - } else { - /* Camera model 2 does not have this control; implemented in software. */ + if (a_contrast >= 16) + a_contrast = 15; + new_contrast = 15 - a_contrast; + if (new_contrast == uvd->vpic_old.contrast) + return; + uvd->vpic_old.contrast = new_contrast; + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { + const int ntries = 5; + int i; + for (i=0; i < ntries; i++) { + ibmcam_Packet_Format1(uvd, contrast_14, new_contrast); + ibmcam_send_FF_04_02(uvd); } + break; + } + case IBMCAM_MODEL_2: + case IBMCAM_MODEL_4: + /* Models 2, 4 do not have this control; implemented in software. */ + break; + case IBMCAM_MODEL_3: + { /* Preset hardware values */ + static const struct { + unsigned short cv1; + unsigned short cv2; + unsigned short cv3; + } cv[7] = { + { 0x05, 0x05, 0x0f }, /* Minimum */ + { 0x04, 0x04, 0x16 }, + { 0x02, 0x03, 0x16 }, + { 0x02, 0x08, 0x16 }, + { 0x01, 0x0c, 0x16 }, + { 0x01, 0x0e, 0x16 }, + { 0x01, 0x10, 0x16 } /* Maximum */ + }; + int i = a_contrast / 2; + RESTRICT_TO_RANGE(i, 0, 6); + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x0067, cv[i].cv1); + ibmcam_model3_Packet1(uvd, 0x005b, cv[i].cv2); + ibmcam_model3_Packet1(uvd, 0x005c, cv[i].cv3); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + break; + } + default: + break; } } /* - * usb_ibmcam_change_lighting_conditions() + * ibmcam_change_lighting_conditions() * * Camera model 1: * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low. @@ -1524,19 +1424,24 @@ * 1/5/00 Created. * 2/20/00 Added support for Model 2 cameras. */ -static void usb_ibmcam_change_lighting_conditions(struct usb_ibmcam *ibmcam) +static void ibmcam_change_lighting_conditions(uvd_t *uvd) { - static const char proc[] = "usb_ibmcam_change_lighting_conditions"; + static const char proc[] = "ibmcam_change_lighting_conditions"; if (debug > 0) - printk(KERN_INFO "%s: Set lighting to %hu.\n", proc, lighting); + info("%s: Set lighting to %hu.", proc, lighting); - if (ibmcam->camera_model == IBMCAM_MODEL_1) { + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { const int ntries = 5; int i; for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, light_27, (unsigned short) lighting); - } else { + ibmcam_Packet_Format1(uvd, light_27, (unsigned short) lighting); + break; + } + case IBMCAM_MODEL_2: +#if 0 /* * This command apparently requires camera to be stopped. My * experiments showed that it -is- possible to alter the lighting @@ -1546,385 +1451,549 @@ * is commented out because it does not work at -any- moment, so its * presence makes no sense. You may use it for experiments. */ -#if 0 - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x010c); /* Stop camera */ - usb_ibmcam_model2_Packet1(ibmcam, mod2_sensitivity, lighting); - usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c); /* Start camera */ + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop camera */ + ibmcam_model2_Packet1(uvd, mod2_sensitivity, lighting); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Start camera */ #endif + break; + case IBMCAM_MODEL_3: + case IBMCAM_MODEL_4: + default: + break; } } /* - * usb_ibmcam_set_sharpness() + * ibmcam_set_sharpness() * * Cameras model 1 have internal smoothing feature. It is controlled by value in * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess). * Recommended value is 4. Cameras model 2 do not have this feature at all. */ -static void usb_ibmcam_set_sharpness(struct usb_ibmcam *ibmcam) +static void ibmcam_set_sharpness(uvd_t *uvd) { - static const char proc[] = "usb_ibmcam_set_sharpness"; + static const char proc[] = "ibmcam_set_sharpness"; - if (ibmcam->camera_model == IBMCAM_MODEL_1) { + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { static const unsigned short sa[] = { 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a }; unsigned short i, sv; RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX); if (debug > 0) - printk(KERN_INFO "%s: Set sharpness to %hu.\n", proc, sharpness); + info("%s: Set sharpness to %hu.", proc, sharpness); sv = sa[sharpness - SHARPNESS_MIN]; for (i=0; i < 2; i++) { - usb_ibmcam_send_x_01_00_05 (ibmcam, unknown_88); - usb_ibmcam_send_x_00_05 (ibmcam, sharp_13); - usb_ibmcam_send_x_00_05_02 (ibmcam, sv); + ibmcam_send_x_01_00_05 (uvd, unknown_88); + ibmcam_send_x_00_05 (uvd, sharp_13); + ibmcam_send_x_00_05_02 (uvd, sv); } - } else { - /* Camera model 2 does not have this control */ + break; + } + case IBMCAM_MODEL_2: + case IBMCAM_MODEL_4: + /* Models 2, 4 do not have this control */ + break; + case IBMCAM_MODEL_3: + { /* + * "Use a table of magic numbers. + * This setting doesn't really change much. + * But that's how Windows does it." + */ + static const struct { + unsigned short sv1; + unsigned short sv2; + unsigned short sv3; + unsigned short sv4; + } sv[7] = { + { 0x00, 0x00, 0x05, 0x14 }, /* Smoothest */ + { 0x01, 0x04, 0x05, 0x14 }, + { 0x02, 0x04, 0x05, 0x14 }, + { 0x03, 0x04, 0x05, 0x14 }, + { 0x03, 0x05, 0x05, 0x14 }, + { 0x03, 0x06, 0x05, 0x14 }, + { 0x03, 0x07, 0x05, 0x14 } /* Sharpest */ + }; + RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX); + RESTRICT_TO_RANGE(sharpness, 0, 6); + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x0060, sv[sharpness].sv1); + ibmcam_model3_Packet1(uvd, 0x0061, sv[sharpness].sv2); + ibmcam_model3_Packet1(uvd, 0x0062, sv[sharpness].sv3); + ibmcam_model3_Packet1(uvd, 0x0063, sv[sharpness].sv4); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_veio(uvd, 0, 0x0001, 0x0113); + break; + } + default: + break; } } /* - * usb_ibmcam_set_brightness() + * ibmcam_set_brightness() * * This procedure changes brightness of the picture. */ -static void usb_ibmcam_set_brightness(struct usb_ibmcam *ibmcam) +static void ibmcam_set_brightness(uvd_t *uvd) { - static const char proc[] = "usb_ibmcam_set_brightness"; + static const char proc[] = "ibmcam_set_brightness"; static const unsigned short n = 1; - unsigned short i, j, bv[3]; - - bv[0] = bv[1] = bv[2] = ibmcam->vpic.brightness >> 10; - if (bv[0] == (ibmcam->vpic_old.brightness >> 10)) - return; - ibmcam->vpic_old.brightness = ibmcam->vpic.brightness; if (debug > 0) - printk(KERN_INFO "%s: Set brightness to (%hu,%hu,%hu)\n", - proc, bv[0], bv[1], bv[2]); + info("%s: Set brightness to %hu.", proc, uvd->vpic.brightness); - if (ibmcam->camera_model == IBMCAM_MODEL_1) { + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { + unsigned short i, j, bv[3]; + bv[0] = bv[1] = bv[2] = uvd->vpic.brightness >> 10; + if (bv[0] == (uvd->vpic_old.brightness >> 10)) + return; + uvd->vpic_old.brightness = bv[0]; for (j=0; j < 3; j++) for (i=0; i < n; i++) - usb_ibmcam_Packet_Format1(ibmcam, bright_3x[j], bv[j]); - } else { - i = ibmcam->vpic.brightness >> 12; /* 0 .. 15 */ + ibmcam_Packet_Format1(uvd, bright_3x[j], bv[j]); + break; + } + case IBMCAM_MODEL_2: + { + unsigned short i, j; + i = uvd->vpic.brightness >> 12; /* 0 .. 15 */ j = 0x60 + i * ((0xee - 0x60) / 16); /* 0x60 .. 0xee or so */ - usb_ibmcam_model2_Packet1(ibmcam, mod2_brightness, j); + if (uvd->vpic_old.brightness == j) + break; + uvd->vpic_old.brightness = j; + ibmcam_model2_Packet1(uvd, mod2_brightness, j); + break; + } + case IBMCAM_MODEL_3: + { + /* Model 3: Brightness range 'i' in [0x0C..0x3F] */ + unsigned short i = + 0x0C + (uvd->vpic.brightness / (0xFFFF / (0x3F - 0x0C + 1))); + RESTRICT_TO_RANGE(i, 0x0C, 0x3F); + if (uvd->vpic_old.brightness == i) + break; + uvd->vpic_old.brightness = i; + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x0036, i); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_veio(uvd, 0, 0x0001, 0x0113); + break; + } + case IBMCAM_MODEL_4: + { + /* Model 4: Brightness range 'i' in [0x04..0xb4] */ + unsigned short i = 0x04 + (uvd->vpic.brightness / (0xFFFF / (0xb4 - 0x04 + 1))); + RESTRICT_TO_RANGE(i, 0x04, 0xb4); + if (uvd->vpic_old.brightness == i) + break; + uvd->vpic_old.brightness = i; + ibmcam_model4_BrightnessPacket(uvd, i); + break; + } + default: + break; } } -static void usb_ibmcam_model2_set_hue(struct usb_ibmcam *ibmcam) +static void ibmcam_set_hue(uvd_t *uvd) { - unsigned short hue = ibmcam->vpic.hue >> 9; /* 0 .. 7F */ + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_2: + { + unsigned short hue = uvd->vpic.hue >> 9; /* 0 .. 7F */ + if (uvd->vpic_old.hue == hue) + return; + uvd->vpic_old.hue = hue; + ibmcam_model2_Packet1(uvd, mod2_hue, hue); + /* ibmcam_model2_Packet1(uvd, mod2_saturation, sat); */ + break; + } + case IBMCAM_MODEL_3: + { +#if 0 /* This seems not to work. No problem, will fix programmatically */ + unsigned short hue = 0x05 + (uvd->vpic.hue / (0xFFFF / (0x37 - 0x05 + 1))); + RESTRICT_TO_RANGE(hue, 0x05, 0x37); + if (uvd->vpic_old.hue == hue) + return; + uvd->vpic_old.hue = hue; + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x007e, hue); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_veio(uvd, 0, 0x0001, 0x0113); +#endif + break; + } + case IBMCAM_MODEL_4: + { + unsigned short r_gain, g_gain, b_gain, hue; + + /* + * I am not sure r/g/b_gain variables exactly control gain + * of those channels. Most likely they subtly change some + * very internal image processing settings in the camera. + * In any case, here is what they do, and feel free to tweak: + * + * r_gain: seriously affects red gain + * g_gain: seriously affects green gain + * b_gain: seriously affects blue gain + * hue: changes average color from violet (0) to red (0xFF) + * + * These settings are preset for a decent white balance in + * 320x240, 352x288 modes. Low-res modes exhibit higher contrast + * and therefore may need different values here. + */ + hue = 20 + (uvd->vpic.hue >> 9); + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + r_gain = 90; + g_gain = 166; + b_gain = 175; + break; + case VIDEOSIZE_160x120: + r_gain = 70; + g_gain = 166; + b_gain = 185; + break; + case VIDEOSIZE_176x144: + r_gain = 160; + g_gain = 175; + b_gain = 185; + break; + default: + r_gain = 120; + g_gain = 166; + b_gain = 175; + break; + } + RESTRICT_TO_RANGE(hue, 1, 0x7f); - usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg, hue); - /* usb_ibmcam_model2_Packet1(ibmcam, mod2_saturation, sat); */ + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, g_gain, 0x0127); /* Green gain */ + ibmcam_veio(uvd, 0, r_gain, 0x012e); /* Red gain */ + ibmcam_veio(uvd, 0, b_gain, 0x0130); /* Blue gain */ + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, hue, 0x012d); /* Hue */ + ibmcam_veio(uvd, 0, 0xf545, 0x0124); + break; + } + default: + break; + } } /* - * usb_ibmcam_adjust_picture() + * ibmcam_adjust_picture() * * This procedure gets called from V4L interface to update picture settings. * Here we change brightness and contrast. */ -static void usb_ibmcam_adjust_picture(struct usb_ibmcam *ibmcam) +static void ibmcam_adjust_picture(uvd_t *uvd) { - usb_ibmcam_adjust_contrast(ibmcam); - usb_ibmcam_set_brightness(ibmcam); - if (ibmcam->camera_model == IBMCAM_MODEL_2) { - usb_ibmcam_model2_set_hue(ibmcam); - } + ibmcam_adjust_contrast(uvd); + ibmcam_set_brightness(uvd); + ibmcam_set_hue(uvd); } -static int usb_ibmcam_model1_setup(struct usb_ibmcam *ibmcam) +static int ibmcam_model1_setup(uvd_t *uvd) { const int ntries = 5; int i; - usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0128); - usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); - usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100); /* LED On */ - usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); - usb_ibmcam_veio(ibmcam, 0, 0x81, 0x0100); /* LED Off */ - usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); - usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100); /* LED On */ - usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0108); - - usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0112); - usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0115); - usb_ibmcam_veio(ibmcam, 0, 0x06, 0x0115); - usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0116); - usb_ibmcam_veio(ibmcam, 0, 0x44, 0x0116); - usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0116); - usb_ibmcam_veio(ibmcam, 0, 0x40, 0x0116); - usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0115); - usb_ibmcam_veio(ibmcam, 0, 0x0e, 0x0115); - usb_ibmcam_veio(ibmcam, 0, 0x19, 0x012c); - - usb_ibmcam_Packet_Format1(ibmcam, 0x00, 0x1e); - usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x0d); - usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x09); - usb_ibmcam_Packet_Format1(ibmcam, 0x3b, 0x00); - usb_ibmcam_Packet_Format1(ibmcam, 0x28, 0x22); - usb_ibmcam_Packet_Format1(ibmcam, light_27, 0); - usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1f); - usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x08); + ibmcam_veio(uvd, 1, 0x00, 0x0128); + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */ + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x81, 0x0100); /* LED Off */ + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */ + ibmcam_veio(uvd, 0, 0x01, 0x0108); + + ibmcam_veio(uvd, 0, 0x03, 0x0112); + ibmcam_veio(uvd, 1, 0x00, 0x0115); + ibmcam_veio(uvd, 0, 0x06, 0x0115); + ibmcam_veio(uvd, 1, 0x00, 0x0116); + ibmcam_veio(uvd, 0, 0x44, 0x0116); + ibmcam_veio(uvd, 1, 0x00, 0x0116); + ibmcam_veio(uvd, 0, 0x40, 0x0116); + ibmcam_veio(uvd, 1, 0x00, 0x0115); + ibmcam_veio(uvd, 0, 0x0e, 0x0115); + ibmcam_veio(uvd, 0, 0x19, 0x012c); + + ibmcam_Packet_Format1(uvd, 0x00, 0x1e); + ibmcam_Packet_Format1(uvd, 0x39, 0x0d); + ibmcam_Packet_Format1(uvd, 0x39, 0x09); + ibmcam_Packet_Format1(uvd, 0x3b, 0x00); + ibmcam_Packet_Format1(uvd, 0x28, 0x22); + ibmcam_Packet_Format1(uvd, light_27, 0); + ibmcam_Packet_Format1(uvd, 0x2b, 0x1f); + ibmcam_Packet_Format1(uvd, 0x39, 0x08); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x2c, 0x00); + ibmcam_Packet_Format1(uvd, 0x2c, 0x00); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x30, 0x14); + ibmcam_Packet_Format1(uvd, 0x30, 0x14); - usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02); - usb_ibmcam_PacketFormat2(ibmcam, 0x01, 0xe1); - usb_ibmcam_PacketFormat2(ibmcam, 0x02, 0xcd); - usb_ibmcam_PacketFormat2(ibmcam, 0x03, 0xcd); - usb_ibmcam_PacketFormat2(ibmcam, 0x04, 0xfa); - usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff); - usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00); - - usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02); - usb_ibmcam_PacketFormat2(ibmcam, 0x0a, 0x37); - usb_ibmcam_PacketFormat2(ibmcam, 0x0b, 0xb8); - usb_ibmcam_PacketFormat2(ibmcam, 0x0c, 0xf3); - usb_ibmcam_PacketFormat2(ibmcam, 0x0d, 0xe3); - usb_ibmcam_PacketFormat2(ibmcam, 0x0e, 0x0d); - usb_ibmcam_PacketFormat2(ibmcam, 0x0f, 0xf2); - usb_ibmcam_PacketFormat2(ibmcam, 0x10, 0xd5); - usb_ibmcam_PacketFormat2(ibmcam, 0x11, 0xba); - usb_ibmcam_PacketFormat2(ibmcam, 0x12, 0x53); - usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff); - usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00); - - usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02); - usb_ibmcam_PacketFormat2(ibmcam, 0x16, 0x00); - usb_ibmcam_PacketFormat2(ibmcam, 0x17, 0x28); - usb_ibmcam_PacketFormat2(ibmcam, 0x18, 0x7d); - usb_ibmcam_PacketFormat2(ibmcam, 0x19, 0xbe); - usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff); - usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00); + ibmcam_PacketFormat2(uvd, 0x39, 0x02); + ibmcam_PacketFormat2(uvd, 0x01, 0xe1); + ibmcam_PacketFormat2(uvd, 0x02, 0xcd); + ibmcam_PacketFormat2(uvd, 0x03, 0xcd); + ibmcam_PacketFormat2(uvd, 0x04, 0xfa); + ibmcam_PacketFormat2(uvd, 0x3f, 0xff); + ibmcam_PacketFormat2(uvd, 0x39, 0x00); + + ibmcam_PacketFormat2(uvd, 0x39, 0x02); + ibmcam_PacketFormat2(uvd, 0x0a, 0x37); + ibmcam_PacketFormat2(uvd, 0x0b, 0xb8); + ibmcam_PacketFormat2(uvd, 0x0c, 0xf3); + ibmcam_PacketFormat2(uvd, 0x0d, 0xe3); + ibmcam_PacketFormat2(uvd, 0x0e, 0x0d); + ibmcam_PacketFormat2(uvd, 0x0f, 0xf2); + ibmcam_PacketFormat2(uvd, 0x10, 0xd5); + ibmcam_PacketFormat2(uvd, 0x11, 0xba); + ibmcam_PacketFormat2(uvd, 0x12, 0x53); + ibmcam_PacketFormat2(uvd, 0x3f, 0xff); + ibmcam_PacketFormat2(uvd, 0x39, 0x00); + + ibmcam_PacketFormat2(uvd, 0x39, 0x02); + ibmcam_PacketFormat2(uvd, 0x16, 0x00); + ibmcam_PacketFormat2(uvd, 0x17, 0x28); + ibmcam_PacketFormat2(uvd, 0x18, 0x7d); + ibmcam_PacketFormat2(uvd, 0x19, 0xbe); + ibmcam_PacketFormat2(uvd, 0x3f, 0xff); + ibmcam_PacketFormat2(uvd, 0x39, 0x00); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x00, 0x18); + ibmcam_Packet_Format1(uvd, 0x00, 0x18); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x13, 0x18); + ibmcam_Packet_Format1(uvd, 0x13, 0x18); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x14, 0x06); + ibmcam_Packet_Format1(uvd, 0x14, 0x06); /* This is default brightness */ for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x31, 0x37); + ibmcam_Packet_Format1(uvd, 0x31, 0x37); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x32, 0x46); + ibmcam_Packet_Format1(uvd, 0x32, 0x46); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x33, 0x55); + ibmcam_Packet_Format1(uvd, 0x33, 0x55); - usb_ibmcam_Packet_Format1(ibmcam, 0x2e, 0x04); + ibmcam_Packet_Format1(uvd, 0x2e, 0x04); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x2d, 0x04); + ibmcam_Packet_Format1(uvd, 0x2d, 0x04); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x29, 0x80); - usb_ibmcam_Packet_Format1(ibmcam, 0x2c, 0x01); - usb_ibmcam_Packet_Format1(ibmcam, 0x30, 0x17); - usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x08); + ibmcam_Packet_Format1(uvd, 0x29, 0x80); + ibmcam_Packet_Format1(uvd, 0x2c, 0x01); + ibmcam_Packet_Format1(uvd, 0x30, 0x17); + ibmcam_Packet_Format1(uvd, 0x39, 0x08); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x34, 0x00); + ibmcam_Packet_Format1(uvd, 0x34, 0x00); - usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0101); - usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010a); + ibmcam_veio(uvd, 0, 0x00, 0x0101); + ibmcam_veio(uvd, 0, 0x00, 0x010a); - switch (videosize) { + switch (uvd->videosize) { case VIDEOSIZE_128x96: - usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0103); - usb_ibmcam_veio(ibmcam, 0, 0x60, 0x0105); - usb_ibmcam_veio(ibmcam, 0, 0x0c, 0x010b); - usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x0b, 0x011d); - usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0129); + ibmcam_veio(uvd, 0, 0x80, 0x0103); + ibmcam_veio(uvd, 0, 0x60, 0x0105); + ibmcam_veio(uvd, 0, 0x0c, 0x010b); + ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x0b, 0x011d); + ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x00, 0x0129); break; case VIDEOSIZE_176x144: - usb_ibmcam_veio(ibmcam, 0, 0xb0, 0x0103); - usb_ibmcam_veio(ibmcam, 0, 0x8f, 0x0105); - usb_ibmcam_veio(ibmcam, 0, 0x06, 0x010b); - usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x0d, 0x011d); - usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0129); + ibmcam_veio(uvd, 0, 0xb0, 0x0103); + ibmcam_veio(uvd, 0, 0x8f, 0x0105); + ibmcam_veio(uvd, 0, 0x06, 0x010b); + ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x0d, 0x011d); + ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x03, 0x0129); break; case VIDEOSIZE_352x288: - usb_ibmcam_veio(ibmcam, 0, 0xb0, 0x0103); - usb_ibmcam_veio(ibmcam, 0, 0x90, 0x0105); - usb_ibmcam_veio(ibmcam, 0, 0x02, 0x010b); - usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x05, 0x011d); - usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0129); + ibmcam_veio(uvd, 0, 0xb0, 0x0103); + ibmcam_veio(uvd, 0, 0x90, 0x0105); + ibmcam_veio(uvd, 0, 0x02, 0x010b); + ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x05, 0x011d); + ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x00, 0x0129); break; } - usb_ibmcam_veio(ibmcam, 0, 0xff, 0x012b); + ibmcam_veio(uvd, 0, 0xff, 0x012b); /* This is another brightness - don't know why */ for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x31, 0xc3); + ibmcam_Packet_Format1(uvd, 0x31, 0xc3); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x32, 0xd2); + ibmcam_Packet_Format1(uvd, 0x32, 0xd2); for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, 0x33, 0xe1); + ibmcam_Packet_Format1(uvd, 0x33, 0xe1); /* Default contrast */ for (i=0; i < ntries; i++) - usb_ibmcam_Packet_Format1(ibmcam, contrast_14, 0x0a); + ibmcam_Packet_Format1(uvd, contrast_14, 0x0a); /* Default sharpness */ for (i=0; i < 2; i++) - usb_ibmcam_PacketFormat2(ibmcam, sharp_13, 0x1a); /* Level 4 FIXME */ + ibmcam_PacketFormat2(uvd, sharp_13, 0x1a); /* Level 4 FIXME */ /* Default lighting conditions */ - usb_ibmcam_Packet_Format1(ibmcam, light_27, lighting); /* 0=Bright 2=Low */ + ibmcam_Packet_Format1(uvd, light_27, lighting); /* 0=Bright 2=Low */ /* Assorted init */ - switch (videosize) { + switch (uvd->videosize) { case VIDEOSIZE_128x96: - usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1e); - usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x36, 0x0102); - usb_ibmcam_veio(ibmcam, 0, 0x1a, 0x0104); - usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x2b, 0x011c); - usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a); /* Same everywhere */ + ibmcam_Packet_Format1(uvd, 0x2b, 0x1e); + ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x36, 0x0102); + ibmcam_veio(uvd, 0, 0x1a, 0x0104); + ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x2b, 0x011c); + ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */ #if 0 - usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0106); - usb_ibmcam_veio(ibmcam, 0, 0x38, 0x0107); + ibmcam_veio(uvd, 0, 0x00, 0x0106); + ibmcam_veio(uvd, 0, 0x38, 0x0107); #else - usb_ibmcam_veio(ibmcam, 0, 0x02, 0x0106); - usb_ibmcam_veio(ibmcam, 0, 0x2a, 0x0107); + ibmcam_veio(uvd, 0, 0x02, 0x0106); + ibmcam_veio(uvd, 0, 0x2a, 0x0107); #endif break; case VIDEOSIZE_176x144: - usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1e); - usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x04, 0x0102); - usb_ibmcam_veio(ibmcam, 0, 0x02, 0x0104); - usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x2b, 0x011c); - usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0106); - usb_ibmcam_veio(ibmcam, 0, 0xca, 0x0107); + ibmcam_Packet_Format1(uvd, 0x2b, 0x1e); + ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x04, 0x0102); + ibmcam_veio(uvd, 0, 0x02, 0x0104); + ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x2b, 0x011c); + ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x01, 0x0106); + ibmcam_veio(uvd, 0, 0xca, 0x0107); break; case VIDEOSIZE_352x288: - usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1f); - usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x08, 0x0102); - usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0104); - usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x2f, 0x011c); - usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a); /* Same everywhere */ - usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0106); - usb_ibmcam_veio(ibmcam, 0, 0xf6, 0x0107); - break; - } - return IBMCAM_IS_OPERATIONAL(ibmcam); -} - -static int usb_ibmcam_model2_setup(struct usb_ibmcam *ibmcam) -{ - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0100); /* LED on */ - usb_ibmcam_veio(ibmcam, 1, 0x0000, 0x0116); - usb_ibmcam_veio(ibmcam, 0, 0x0060, 0x0116); - usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0112); - usb_ibmcam_veio(ibmcam, 0, 0x00bc, 0x012c); - usb_ibmcam_veio(ibmcam, 0, 0x0008, 0x012b); - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0108); - usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0133); - usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0102); - switch (videosize) { + ibmcam_Packet_Format1(uvd, 0x2b, 0x1f); + ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x08, 0x0102); + ibmcam_veio(uvd, 0, 0x01, 0x0104); + ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x2f, 0x011c); + ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x03, 0x0106); + ibmcam_veio(uvd, 0, 0xf6, 0x0107); + break; + } + return (CAMERA_IS_OPERATIONAL(uvd) ? 0 : -EFAULT); +} + +static int ibmcam_model2_setup(uvd_t *uvd) +{ + ibmcam_veio(uvd, 0, 0x0000, 0x0100); /* LED on */ + ibmcam_veio(uvd, 1, 0x0000, 0x0116); + ibmcam_veio(uvd, 0, 0x0060, 0x0116); + ibmcam_veio(uvd, 0, 0x0002, 0x0112); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0008, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + switch (uvd->videosize) { case VIDEOSIZE_176x144: - usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103); /* All except 320x240 */ - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ - usb_ibmcam_veio(ibmcam, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ - usb_ibmcam_veio(ibmcam, 0, 0x00b9, 0x010a); /* Unique to this mode */ - usb_ibmcam_veio(ibmcam, 0, 0x0038, 0x0119); /* Unique to this mode */ - usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ - usb_ibmcam_veio(ibmcam, 0, 0x0090, 0x0107); /* Unique to every mode*/ + ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ + ibmcam_veio(uvd, 0, 0x00b9, 0x010a); /* Unique to this mode */ + ibmcam_veio(uvd, 0, 0x0038, 0x0119); /* Unique to this mode */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x0090, 0x0107); /* Unique to every mode*/ break; case VIDEOSIZE_320x240: - usb_ibmcam_veio(ibmcam, 0, 0x0028, 0x0103); /* Unique to this mode */ - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ - usb_ibmcam_veio(ibmcam, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ - usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a); /* All except 176x144 */ - usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119); /* All except 176x144 */ - usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ - usb_ibmcam_veio(ibmcam, 0, 0x0098, 0x0107); /* Unique to every mode*/ + ibmcam_veio(uvd, 0, 0x0028, 0x0103); /* Unique to this mode */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ + ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x0098, 0x0107); /* Unique to every mode*/ break; case VIDEOSIZE_352x240: - usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103); /* All except 320x240 */ - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ - usb_ibmcam_veio(ibmcam, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ - usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a); /* All except 176x144 */ - usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119); /* All except 176x144 */ - usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ - usb_ibmcam_veio(ibmcam, 0, 0x00da, 0x0107); /* Unique to every mode*/ + ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ + ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x00da, 0x0107); /* Unique to every mode*/ break; case VIDEOSIZE_352x288: - usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103); /* All except 320x240 */ - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ - usb_ibmcam_veio(ibmcam, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ - usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a); /* All except 176x144 */ - usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119); /* All except 176x144 */ - usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ - usb_ibmcam_veio(ibmcam, 0, 0x00fe, 0x0107); /* Unique to every mode*/ + ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ + ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x00fe, 0x0107); /* Unique to every mode*/ break; } - return IBMCAM_IS_OPERATIONAL(ibmcam); + return (CAMERA_IS_OPERATIONAL(uvd) ? 0 : -EFAULT); } /* - * usb_ibmcam_model1_setup_after_video_if() + * ibmcam_model1_setup_after_video_if() * * This code adds finishing touches to the video data interface. * Here we configure the frame rate and turn on the LED. */ -static void usb_ibmcam_model1_setup_after_video_if(struct usb_ibmcam *ibmcam) +static void ibmcam_model1_setup_after_video_if(uvd_t *uvd) { unsigned short internal_frame_rate; RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX); internal_frame_rate = FRAMERATE_MAX - framerate; /* 0=Fast 6=Slow */ - usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100); /* LED On */ - usb_ibmcam_veio(ibmcam, 0, internal_frame_rate, 0x0111); - usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0114); - usb_ibmcam_veio(ibmcam, 0, 0xc0, 0x010c); + ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */ + ibmcam_veio(uvd, 0, internal_frame_rate, 0x0111); + ibmcam_veio(uvd, 0, 0x01, 0x0114); + ibmcam_veio(uvd, 0, 0xc0, 0x010c); } -static void usb_ibmcam_model2_setup_after_video_if(struct usb_ibmcam *ibmcam) +static void ibmcam_model2_setup_after_video_if(uvd_t *uvd) { - unsigned short setup_model2_rg, setup_model2_rg2, setup_model2_sat, setup_model2_yb; + unsigned short setup_model2_rg2, setup_model2_sat, setup_model2_yb; - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0100); /* LED on */ + ibmcam_veio(uvd, 0, 0x0000, 0x0100); /* LED on */ - switch (videosize) { + switch (uvd->videosize) { case VIDEOSIZE_176x144: - usb_ibmcam_veio(ibmcam, 0, 0x0050, 0x0111); - usb_ibmcam_veio(ibmcam, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x0050, 0x0111); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); break; case VIDEOSIZE_320x240: case VIDEOSIZE_352x240: case VIDEOSIZE_352x288: - usb_ibmcam_veio(ibmcam, 0, 0x0040, 0x0111); - usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x0040, 0x0111); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); break; } - usb_ibmcam_veio(ibmcam, 0, 0x009b, 0x010f); - usb_ibmcam_veio(ibmcam, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); /* * Hardware settings, may affect CMOS sensor; not user controls! @@ -1939,52 +2008,52 @@ * 0x002c: hardware setting (related to scan lines) * 0x002e: stops video stream, probably important h/w setting */ - usb_ibmcam_model2_Packet1(ibmcam, 0x000a, 0x005c); - usb_ibmcam_model2_Packet1(ibmcam, 0x0004, 0x0000); - usb_ibmcam_model2_Packet1(ibmcam, 0x0006, 0x00fb); - usb_ibmcam_model2_Packet1(ibmcam, 0x0008, 0x0000); - usb_ibmcam_model2_Packet1(ibmcam, 0x000c, 0x0009); - usb_ibmcam_model2_Packet1(ibmcam, 0x0012, 0x000a); - usb_ibmcam_model2_Packet1(ibmcam, 0x002a, 0x0000); - usb_ibmcam_model2_Packet1(ibmcam, 0x002c, 0x0000); - usb_ibmcam_model2_Packet1(ibmcam, 0x002e, 0x0008); + ibmcam_model2_Packet1(uvd, 0x000a, 0x005c); + ibmcam_model2_Packet1(uvd, 0x0004, 0x0000); + ibmcam_model2_Packet1(uvd, 0x0006, 0x00fb); + ibmcam_model2_Packet1(uvd, 0x0008, 0x0000); + ibmcam_model2_Packet1(uvd, 0x000c, 0x0009); + ibmcam_model2_Packet1(uvd, 0x0012, 0x000a); + ibmcam_model2_Packet1(uvd, 0x002a, 0x0000); + ibmcam_model2_Packet1(uvd, 0x002c, 0x0000); + ibmcam_model2_Packet1(uvd, 0x002e, 0x0008); /* * Function 0x0030 pops up all over the place. Apparently * it is a hardware control register, with every bit assigned to * do something. */ - usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0000); + ibmcam_model2_Packet1(uvd, 0x0030, 0x0000); /* * Magic control of CMOS sensor. Only lower values like * 0-3 work, and picture shifts left or right. Don't change. */ - switch (videosize) { + switch (uvd->videosize) { case VIDEOSIZE_176x144: - usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0002); - usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0002); /* Horizontal shift */ - usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x004a); /* Another hardware setting */ + ibmcam_model2_Packet1(uvd, 0x0014, 0x0002); + ibmcam_model2_Packet1(uvd, 0x0016, 0x0002); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x004a); /* Another hardware setting */ break; case VIDEOSIZE_320x240: - usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0009); - usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0005); /* Horizontal shift */ - usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x0044); /* Another hardware setting */ + ibmcam_model2_Packet1(uvd, 0x0014, 0x0009); + ibmcam_model2_Packet1(uvd, 0x0016, 0x0005); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x0044); /* Another hardware setting */ break; case VIDEOSIZE_352x240: /* This mode doesn't work as Windows programs it; changed to work */ - usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0009); /* Windows sets this to 8 */ - usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0003); /* Horizontal shift */ - usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x0044); /* Windows sets this to 0x0045 */ + ibmcam_model2_Packet1(uvd, 0x0014, 0x0009); /* Windows sets this to 8 */ + ibmcam_model2_Packet1(uvd, 0x0016, 0x0003); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x0044); /* Windows sets this to 0x0045 */ break; case VIDEOSIZE_352x288: - usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0003); - usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0002); /* Horizontal shift */ - usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x004a); /* Another hardware setting */ + ibmcam_model2_Packet1(uvd, 0x0014, 0x0003); + ibmcam_model2_Packet1(uvd, 0x0016, 0x0002); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x004a); /* Another hardware setting */ break; } - usb_ibmcam_model2_Packet1(ibmcam, mod2_brightness, 0x005a); + ibmcam_model2_Packet1(uvd, mod2_brightness, 0x005a); /* * We have our own frame rate setting varying from 0 (slowest) to 6 (fastest). @@ -2008,7 +2077,7 @@ RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX); i_framerate = FRAMERATE_MAX - framerate + FRAMERATE_MIN; - switch (videosize) { + switch (uvd->videosize) { case VIDEOSIZE_176x144: hw_fps = 6 + i_framerate*4; break; @@ -2022,10 +2091,10 @@ hw_fps = 28 + i_framerate/2; break; } - if (debug > 0) - printk(KERN_DEBUG "Framerate (hardware): %hd.\n", hw_fps); + if (uvd->debug > 0) + info("Framerate (hardware): %hd.", hw_fps); RESTRICT_TO_RANGE(hw_fps, 0, 31); - usb_ibmcam_model2_Packet1(ibmcam, mod2_set_framerate, hw_fps); + ibmcam_model2_Packet1(uvd, mod2_set_framerate, hw_fps); } /* @@ -2034,28 +2103,22 @@ * does not allow arbitrary values and apparently is a bit mask, to * be activated only at appropriate time. Don't change it randomly! */ - switch (videosize) { + switch (uvd->videosize) { case VIDEOSIZE_176x144: - usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x00c2); + ibmcam_model2_Packet1(uvd, 0x0026, 0x00c2); break; case VIDEOSIZE_320x240: - usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0044); + ibmcam_model2_Packet1(uvd, 0x0026, 0x0044); break; case VIDEOSIZE_352x240: - usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0046); + ibmcam_model2_Packet1(uvd, 0x0026, 0x0046); break; case VIDEOSIZE_352x288: - usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0048); + ibmcam_model2_Packet1(uvd, 0x0026, 0x0048); break; } - usb_ibmcam_model2_Packet1(ibmcam, mod2_sensitivity, lighting); - - if (init_model2_rg >= 0) { - RESTRICT_TO_RANGE(init_model2_rg, 0, 255); - setup_model2_rg = init_model2_rg; - } else - setup_model2_rg = 0x0070; + ibmcam_model2_Packet1(uvd, mod2_sensitivity, lighting); if (init_model2_rg2 >= 0) { RESTRICT_TO_RANGE(init_model2_rg2, 0, 255); @@ -2075,801 +2138,1469 @@ } else setup_model2_yb = 0x00a0; - usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg2, setup_model2_rg2); - usb_ibmcam_model2_Packet1(ibmcam, mod2_saturation, setup_model2_sat); - usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_yb, setup_model2_yb); - usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg, setup_model2_rg); + ibmcam_model2_Packet1(uvd, mod2_color_balance_rg2, setup_model2_rg2); + ibmcam_model2_Packet1(uvd, mod2_saturation, setup_model2_sat); + ibmcam_model2_Packet1(uvd, mod2_color_balance_yb, setup_model2_yb); + ibmcam_model2_Packet1(uvd, mod2_hue, uvd->vpic.hue >> 9); /* 0 .. 7F */; /* Hardware control command */ - usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0004); + ibmcam_model2_Packet1(uvd, 0x0030, 0x0004); - usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c); /* Go camera, go! */ - usb_clear_halt(ibmcam->dev, ibmcam->video_endp); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go camera, go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); } -/* - * usb_ibmcam_setup_video_stop() - * - * This code tells camera to stop streaming. The interface remains - * configured and bandwidth - claimed. - */ -static void usb_ibmcam_setup_video_stop(struct usb_ibmcam *ibmcam) -{ - if (ibmcam->camera_model == IBMCAM_MODEL_1) { - usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c); - usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c); - usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0114); - usb_ibmcam_veio(ibmcam, 0, 0xc0, 0x010c); - usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c); - usb_ibmcam_send_FF_04_02(ibmcam); - usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); - usb_ibmcam_veio(ibmcam, 0, 0x81, 0x0100); /* LED Off */ - } else if (ibmcam->camera_model == IBMCAM_MODEL_2) { - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x010c); /* Stop the camera */ - - usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0004); - - usb_ibmcam_veio(ibmcam, 0, 0x0080, 0x0100); /* LED Off */ - usb_ibmcam_veio(ibmcam, 0, 0x0020, 0x0111); - usb_ibmcam_veio(ibmcam, 0, 0x00a0, 0x0111); - - usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0002); - - usb_ibmcam_veio(ibmcam, 0, 0x0020, 0x0111); - usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0112); - } -} - -/* - * usb_ibmcam_reinit_iso() - * - * This procedure sends couple of commands to the camera and then - * resets the video pipe. This sequence was observed to reinit the - * camera or, at least, to initiate ISO data stream. - * - * History: - * 1/2/00 Created. - */ -static void usb_ibmcam_reinit_iso(struct usb_ibmcam *ibmcam, int do_stop) +static void ibmcam_model4_setup_after_video_if(uvd_t *uvd) { - if (ibmcam->camera_model == IBMCAM_MODEL_1) { - if (do_stop) - usb_ibmcam_setup_video_stop(ibmcam); - usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0114); - usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c); - usb_clear_halt(ibmcam->dev, ibmcam->video_endp); - usb_ibmcam_model1_setup_after_video_if(ibmcam); - } else if (ibmcam->camera_model == IBMCAM_MODEL_2) { - usb_ibmcam_model2_setup_after_video_if(ibmcam); + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0070, 0x0119); + ibmcam_veio(uvd, 0, 0x00d2, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x005e, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x0039, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x0028, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x001e, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x000a, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005a, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0043, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00eb, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x0031, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x012d); + ibmcam_veio(uvd, 0, 0x0078, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_160x120: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0038, 0x0119); + ibmcam_veio(uvd, 0, 0x00d8, 0x0107); + ibmcam_veio(uvd, 0, 0x0002, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x00b9, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x0028, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x001e, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x000b, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005a, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0043, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00c7, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0025, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0048, 0x0127); + ibmcam_veio(uvd, 0, 0x0035, 0x012e); + ibmcam_veio(uvd, 0, 0x00d0, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0048, 0x012d); + ibmcam_veio(uvd, 0, 0x0090, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_176x144: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0038, 0x0119); + ibmcam_veio(uvd, 0, 0x00d6, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x0018, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x00b9, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x002c, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x0024, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0007, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0001, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005e, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0049, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00c7, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0028, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x002a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x012d); + ibmcam_veio(uvd, 0, 0x006d, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_320x240: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0070, 0x0119); + ibmcam_veio(uvd, 0, 0x00d2, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x005e, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x0039, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x0028, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x001e, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x000a, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005a, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0043, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00eb, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x0031, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x012d); + ibmcam_veio(uvd, 0, 0x0078, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_352x288: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0070, 0x0119); + ibmcam_veio(uvd, 0, 0x00f2, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x008c, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x0039, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x002c, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x0024, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0006, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0002, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005e, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0049, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00cf, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x0025, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x012d); + ibmcam_veio(uvd, 0, 0x0048, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; } + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); } -/* - * ibmcam_init_isoc() - * - * History: - * 1/27/00 Used ibmcam->iface, ibmcam->ifaceAltActive instead of hardcoded values. - * Simplified by using for loop, allowed any number of URBs. - */ -static int ibmcam_init_isoc(struct usb_ibmcam *ibmcam) +static void ibmcam_model3_setup_after_video_if(uvd_t *uvd) { - struct usb_device *dev = ibmcam->dev; - int i, err; - - if (!IBMCAM_IS_OPERATIONAL(ibmcam)) - return -EFAULT; - - ibmcam->compress = 0; - ibmcam->curframe = -1; - ibmcam->cursbuf = 0; - ibmcam->scratchlen = 0; - - /* Alternate interface 1 is is the biggest frame size */ - i = usb_set_interface(dev, ibmcam->iface, ibmcam->ifaceAltActive); - if (i < 0) { - printk(KERN_ERR "usb_set_interface error\n"); - ibmcam->last_error = i; - return -EBUSY; - } - usb_ibmcam_change_lighting_conditions(ibmcam); - usb_ibmcam_set_sharpness(ibmcam); - usb_ibmcam_reinit_iso(ibmcam, 0); - - /* We double buffer the Iso lists */ - - for (i=0; i < IBMCAM_NUMSBUF; i++) { - int j, k; - urb_t *urb; - - urb = usb_alloc_urb(FRAMES_PER_DESC); - if (urb == NULL) { - printk(KERN_ERR "ibmcam_init_isoc: usb_init_isoc() failed.\n"); - return -ENOMEM; - } - ibmcam->sbuf[i].urb = urb; - urb->dev = dev; - urb->context = ibmcam; - urb->pipe = usb_rcvisocpipe(dev, ibmcam->video_endp); - urb->transfer_flags = USB_ISO_ASAP; - urb->transfer_buffer = ibmcam->sbuf[i].data; - urb->complete = ibmcam_isoc_irq; - urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = ibmcam->iso_packet_len * FRAMES_PER_DESC; - for (j=k=0; j < FRAMES_PER_DESC; j++, k += ibmcam->iso_packet_len) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = ibmcam->iso_packet_len; - } - } - - /* Link URBs into a ring so that they invoke each other infinitely */ - for (i=0; i < IBMCAM_NUMSBUF; i++) { - if ((i+1) < IBMCAM_NUMSBUF) - ibmcam->sbuf[i].urb->next = ibmcam->sbuf[i+1].urb; - else - ibmcam->sbuf[i].urb->next = ibmcam->sbuf[0].urb; - } - - /* Submit all URBs */ - for (i=0; i < IBMCAM_NUMSBUF; i++) { - err = usb_submit_urb(ibmcam->sbuf[i].urb); - if (err) - printk(KERN_ERR "ibmcam_init_isoc: usb_run_isoc(%d) ret %d\n", - i, err); - } + int i; + /* + * 01.01.08 - Added for RCA video in support -LO + * This struct is used to init the Model3 cam to use the RCA video in port + * instead of the CCD sensor. + */ + static const struct struct_initData initData[] = { + {0, 0x0000, 0x010c}, + {0, 0x0006, 0x012c}, + {0, 0x0078, 0x012d}, + {0, 0x0046, 0x012f}, + {0, 0xd141, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfea8, 0x0124}, + {1, 0x0000, 0x0116}, + {0, 0x0064, 0x0116}, + {1, 0x0000, 0x0115}, + {0, 0x0003, 0x0115}, + {0, 0x0008, 0x0123}, + {0, 0x0000, 0x0117}, + {0, 0x0000, 0x0112}, + {0, 0x0080, 0x0100}, + {0, 0x0000, 0x0100}, + {1, 0x0000, 0x0116}, + {0, 0x0060, 0x0116}, + {0, 0x0002, 0x0112}, + {0, 0x0000, 0x0123}, + {0, 0x0001, 0x0117}, + {0, 0x0040, 0x0108}, + {0, 0x0019, 0x012c}, + {0, 0x0040, 0x0116}, + {0, 0x000a, 0x0115}, + {0, 0x000b, 0x0115}, + {0, 0x0078, 0x012d}, + {0, 0x0046, 0x012f}, + {0, 0xd141, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfea8, 0x0124}, + {0, 0x0064, 0x0116}, + {0, 0x0000, 0x0115}, + {0, 0x0001, 0x0115}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00aa, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f2, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x000f, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f8, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00fc, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f9, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x003c, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0027, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0019, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0021, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0006, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0045, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002a, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x000e, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002b, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f4, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002c, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0004, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002d, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0014, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002e, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0003, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002f, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0003, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0014, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0053, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0x0000, 0x0101}, + {0, 0x00a0, 0x0103}, + {0, 0x0078, 0x0105}, + {0, 0x0000, 0x010a}, + {0, 0x0024, 0x010b}, + {0, 0x0028, 0x0119}, + {0, 0x0088, 0x011b}, + {0, 0x0002, 0x011d}, + {0, 0x0003, 0x011e}, + {0, 0x0000, 0x0129}, + {0, 0x00fc, 0x012b}, + {0, 0x0008, 0x0102}, + {0, 0x0000, 0x0104}, + {0, 0x0008, 0x011a}, + {0, 0x0028, 0x011c}, + {0, 0x0021, 0x012a}, + {0, 0x0000, 0x0118}, + {0, 0x0000, 0x0132}, + {0, 0x0000, 0x0109}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0031, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00dc, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0032, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0020, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0030, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0008, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0x0003, 0x0106}, + {0, 0x0062, 0x0107}, + {0, 0x0003, 0x0111}, + }; +#define NUM_INIT_DATA - ibmcam->streaming = 1; - /* printk(KERN_DEBUG "streaming=1 ibmcam->video_endp=$%02x\n", ibmcam->video_endp); */ - return 0; -} + unsigned short compression = 0; /* 0=none, 7=best frame rate */ + int f_rate; /* 0=Fastest 7=slowest */ -/* - * ibmcam_stop_isoc() - * - * This procedure stops streaming and deallocates URBs. Then it - * activates zero-bandwidth alt. setting of the video interface. - * - * History: - * 1/22/00 Corrected order of actions to work after surprise removal. - * 1/27/00 Used ibmcam->iface, ibmcam->ifaceAltInactive instead of hardcoded values. - */ -static void ibmcam_stop_isoc(struct usb_ibmcam *ibmcam) -{ - static const char proc[] = "ibmcam_stop_isoc"; - int i, j; - - if (!ibmcam->streaming || (ibmcam->dev == NULL)) + if (IBMCAM_T(uvd)->initialized) return; - /* Unschedule all of the iso td's */ - for (i=0; i < IBMCAM_NUMSBUF; i++) { - j = usb_unlink_urb(ibmcam->sbuf[i].urb); - if (j < 0) - printk(KERN_ERR "%s: usb_unlink_urb() error %d.\n", proc, j); - } - /* printk(KERN_DEBUG "streaming=0\n"); */ - ibmcam->streaming = 0; - - /* Delete them all */ - for (i=0; i < IBMCAM_NUMSBUF; i++) - usb_free_urb(ibmcam->sbuf[i].urb); - - if (!ibmcam->remove_pending) { - usb_ibmcam_setup_video_stop(ibmcam); - - /* Set packet size to 0 */ - j = usb_set_interface(ibmcam->dev, ibmcam->iface, ibmcam->ifaceAltInactive); - if (j < 0) { - printk(KERN_ERR "%s: usb_set_interface() error %d.\n", proc, j); - ibmcam->last_error = j; - } - } -} - -/* - * ibmcam_new_frame() - * - * History: - * 29-Mar-00 Added copying of previous frame into the current one. - */ -static int ibmcam_new_frame(struct usb_ibmcam *ibmcam, int framenum) -{ - struct ibmcam_frame *frame; - int n, width, height; - - /* If we're not grabbing a frame right now and the other frame is */ - /* ready to be grabbed into, then use it instead */ - if (ibmcam->curframe != -1) - return 0; - - n = (framenum - 1 + IBMCAM_NUMFRAMES) % IBMCAM_NUMFRAMES; - if (ibmcam->frame[n].grabstate == FRAME_READY) - framenum = n; - - frame = &ibmcam->frame[framenum]; - - frame->grabstate = FRAME_GRABBING; - frame->scanstate = STATE_SCANNING; - frame->scanlength = 0; /* Accumulated in ibmcam_parse_data() */ - ibmcam->curframe = framenum; - - /* - * Normally we would want to copy previous frame into the current one - * before we even start filling it with data; this allows us to stop - * filling at any moment; top portion of the frame will be new and - * bottom portion will stay as it was in previous frame. If we don't - * do that then missing chunks of video stream will result in flickering - * portions of old data whatever it was before. - * - * If we choose not to copy previous frame (to, for example, save few - * bus cycles - the frame can be pretty large!) then we have an option - * to clear the frame before using. If we experience losses in this - * mode then missing picture will be black (no flickering). - * - * Finally, if user chooses not to clean the current frame before - * filling it with data then the old data will be visible if we fail - * to refill entire frame with new data. - */ - if (!(flags & FLAGS_SEPARATE_FRAMES)) { - /* This copies previous frame into this one to mask losses */ - memmove(frame->data, ibmcam->frame[1-framenum].data, MAX_FRAME_SIZE); - } else { - if (flags & FLAGS_CLEAN_FRAMES) { - /* This provides a "clean" frame but slows things down */ - memset(frame->data, 0, MAX_FRAME_SIZE); - } - } - switch (videosize) { - case VIDEOSIZE_128x96: - frame->frmwidth = 128; - frame->frmheight = 96; - frame->order_uv = 1; /* U Y V Y ... */ - frame->hdr_sig = 0x06; /* 00 FF 00 06 */ + /* Internal frame rate is controlled by f_rate value */ + f_rate = 7 - framerate; + RESTRICT_TO_RANGE(f_rate, 0, 7); + + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 1, 0x0000, 0x0116); + ibmcam_veio(uvd, 0, 0x0060, 0x0116); + ibmcam_veio(uvd, 0, 0x0002, 0x0112); + ibmcam_veio(uvd, 0, 0x0000, 0x0123); + ibmcam_veio(uvd, 0, 0x0001, 0x0117); + ibmcam_veio(uvd, 0, 0x0040, 0x0108); + ibmcam_veio(uvd, 0, 0x0019, 0x012c); + ibmcam_veio(uvd, 0, 0x0060, 0x0116); + ibmcam_veio(uvd, 0, 0x0002, 0x0115); + ibmcam_veio(uvd, 0, 0x0003, 0x0115); + ibmcam_veio(uvd, 1, 0x0000, 0x0115); + ibmcam_veio(uvd, 0, 0x000b, 0x0115); + ibmcam_model3_Packet1(uvd, 0x000a, 0x0040); + ibmcam_model3_Packet1(uvd, 0x000b, 0x00f6); + ibmcam_model3_Packet1(uvd, 0x000c, 0x0002); + ibmcam_model3_Packet1(uvd, 0x000d, 0x0020); + ibmcam_model3_Packet1(uvd, 0x000e, 0x0033); + ibmcam_model3_Packet1(uvd, 0x000f, 0x0007); + ibmcam_model3_Packet1(uvd, 0x0010, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0011, 0x0070); + ibmcam_model3_Packet1(uvd, 0x0012, 0x0030); + ibmcam_model3_Packet1(uvd, 0x0013, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0014, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0015, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0016, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0017, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0018, 0x0000); + ibmcam_model3_Packet1(uvd, 0x001e, 0x00c3); + ibmcam_model3_Packet1(uvd, 0x0020, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0028, 0x0010); + ibmcam_model3_Packet1(uvd, 0x0029, 0x0054); + ibmcam_model3_Packet1(uvd, 0x002a, 0x0013); + ibmcam_model3_Packet1(uvd, 0x002b, 0x0007); + ibmcam_model3_Packet1(uvd, 0x002d, 0x0028); + ibmcam_model3_Packet1(uvd, 0x002e, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0031, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0032, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0033, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0034, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0035, 0x0038); + ibmcam_model3_Packet1(uvd, 0x003a, 0x0001); + ibmcam_model3_Packet1(uvd, 0x003c, 0x001e); + ibmcam_model3_Packet1(uvd, 0x003f, 0x000a); + ibmcam_model3_Packet1(uvd, 0x0041, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0046, 0x003f); + ibmcam_model3_Packet1(uvd, 0x0047, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0050, 0x0005); + ibmcam_model3_Packet1(uvd, 0x0052, 0x001a); + ibmcam_model3_Packet1(uvd, 0x0053, 0x0003); + ibmcam_model3_Packet1(uvd, 0x005a, 0x006b); + ibmcam_model3_Packet1(uvd, 0x005d, 0x001e); + ibmcam_model3_Packet1(uvd, 0x005e, 0x0030); + ibmcam_model3_Packet1(uvd, 0x005f, 0x0041); + ibmcam_model3_Packet1(uvd, 0x0064, 0x0008); + ibmcam_model3_Packet1(uvd, 0x0065, 0x0015); + ibmcam_model3_Packet1(uvd, 0x0068, 0x000f); + ibmcam_model3_Packet1(uvd, 0x0079, 0x0000); + ibmcam_model3_Packet1(uvd, 0x007a, 0x0000); + ibmcam_model3_Packet1(uvd, 0x007c, 0x003f); + ibmcam_model3_Packet1(uvd, 0x0082, 0x000f); + ibmcam_model3_Packet1(uvd, 0x0085, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0099, 0x0000); + ibmcam_model3_Packet1(uvd, 0x009b, 0x0023); + ibmcam_model3_Packet1(uvd, 0x009c, 0x0022); + ibmcam_model3_Packet1(uvd, 0x009d, 0x0096); + ibmcam_model3_Packet1(uvd, 0x009e, 0x0096); + ibmcam_model3_Packet1(uvd, 0x009f, 0x000a); + + switch (uvd->videosize) { + case VIDEOSIZE_160x120: + ibmcam_veio(uvd, 0, 0x0000, 0x0101); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x00a0, 0x0103); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0078, 0x0105); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */ + ibmcam_veio(uvd, 0, 0x0024, 0x010b); /* Differs everywhere */ + ibmcam_veio(uvd, 0, 0x00a9, 0x0119); + ibmcam_veio(uvd, 0, 0x0016, 0x011b); + ibmcam_veio(uvd, 0, 0x0002, 0x011d); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0003, 0x011e); /* Same on 176x144, 640x480 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */ + ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */ + ibmcam_veio(uvd, 0, 0x0018, 0x0102); + ibmcam_veio(uvd, 0, 0x0004, 0x0104); + ibmcam_veio(uvd, 0, 0x0004, 0x011a); + ibmcam_veio(uvd, 0, 0x0028, 0x011c); + ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */ + ibmcam_veio(uvd, 0, 0x0000, 0x0118); + ibmcam_veio(uvd, 0, 0x0000, 0x0132); + ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */ + ibmcam_veio(uvd, 0, compression, 0x0109); break; - case VIDEOSIZE_176x144: - frame->frmwidth = 176; - frame->frmheight = 144; - frame->order_uv = 1; /* U Y V Y ... */ - frame->hdr_sig = 0x0E; /* 00 FF 00 0E */ - break; - case VIDEOSIZE_320x240: /* For model 2 only */ - frame->frmwidth = 320; - frame->frmheight = 240; - break; - case VIDEOSIZE_352x240: /* For model 2 only */ - frame->frmwidth = 352; - frame->frmheight = 240; + case VIDEOSIZE_320x240: + ibmcam_veio(uvd, 0, 0x0000, 0x0101); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x00a0, 0x0103); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0078, 0x0105); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */ + ibmcam_veio(uvd, 0, 0x0028, 0x010b); /* Differs everywhere */ + ibmcam_veio(uvd, 0, 0x0002, 0x011d); /* Same */ + ibmcam_veio(uvd, 0, 0x0000, 0x011e); + ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */ + ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */ + /* 4 commands from 160x120 skipped */ + ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */ + ibmcam_veio(uvd, 0, compression, 0x0109); + ibmcam_veio(uvd, 0, 0x00d9, 0x0119); + ibmcam_veio(uvd, 0, 0x0006, 0x011b); + ibmcam_veio(uvd, 0, 0x0021, 0x0102); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0010, 0x0104); + ibmcam_veio(uvd, 0, 0x0004, 0x011a); + ibmcam_veio(uvd, 0, 0x003f, 0x011c); + ibmcam_veio(uvd, 0, 0x001c, 0x0118); + ibmcam_veio(uvd, 0, 0x0000, 0x0132); + break; + case VIDEOSIZE_640x480: + ibmcam_veio(uvd, 0, 0x00f0, 0x0105); + ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */ + ibmcam_veio(uvd, 0, 0x0038, 0x010b); /* Differs everywhere */ + ibmcam_veio(uvd, 0, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0006, 0x011b); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0004, 0x011d); /* NC */ + ibmcam_veio(uvd, 0, 0x0003, 0x011e); /* Same on 176x144, 640x480 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */ + ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */ + ibmcam_veio(uvd, 0, 0x0021, 0x0102); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0016, 0x0104); /* NC */ + ibmcam_veio(uvd, 0, 0x0004, 0x011a); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x003f, 0x011c); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */ + ibmcam_veio(uvd, 0, 0x001c, 0x0118); /* Same on 320x240, 640x480 */ + ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */ + ibmcam_veio(uvd, 0, compression, 0x0109); + ibmcam_veio(uvd, 0, 0x0040, 0x0101); + ibmcam_veio(uvd, 0, 0x0040, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0132); /* Same on 320x240, 640x480 */ + break; + } + ibmcam_model3_Packet1(uvd, 0x007e, 0x000e); /* Hue */ + ibmcam_model3_Packet1(uvd, 0x0036, 0x0011); /* Brightness */ + ibmcam_model3_Packet1(uvd, 0x0060, 0x0002); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0061, 0x0004); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0062, 0x0005); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0063, 0x0014); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0096, 0x00a0); /* Red gain */ + ibmcam_model3_Packet1(uvd, 0x0097, 0x0096); /* Blue gain */ + ibmcam_model3_Packet1(uvd, 0x0067, 0x0001); /* Contrast */ + ibmcam_model3_Packet1(uvd, 0x005b, 0x000c); /* Contrast */ + ibmcam_model3_Packet1(uvd, 0x005c, 0x0016); /* Contrast */ + ibmcam_model3_Packet1(uvd, 0x0098, 0x000b); + ibmcam_model3_Packet1(uvd, 0x002c, 0x0003); /* Was 1, broke 640x480 */ + ibmcam_model3_Packet1(uvd, 0x002f, 0x002a); + ibmcam_model3_Packet1(uvd, 0x0030, 0x0029); + ibmcam_model3_Packet1(uvd, 0x0037, 0x0002); + ibmcam_model3_Packet1(uvd, 0x0038, 0x0059); + ibmcam_model3_Packet1(uvd, 0x003d, 0x002e); + ibmcam_model3_Packet1(uvd, 0x003e, 0x0028); + ibmcam_model3_Packet1(uvd, 0x0078, 0x0005); + ibmcam_model3_Packet1(uvd, 0x007b, 0x0011); + ibmcam_model3_Packet1(uvd, 0x007d, 0x004b); + ibmcam_model3_Packet1(uvd, 0x007f, 0x0022); + ibmcam_model3_Packet1(uvd, 0x0080, 0x000c); + ibmcam_model3_Packet1(uvd, 0x0081, 0x000b); + ibmcam_model3_Packet1(uvd, 0x0083, 0x00fd); + ibmcam_model3_Packet1(uvd, 0x0086, 0x000b); + ibmcam_model3_Packet1(uvd, 0x0087, 0x000b); + ibmcam_model3_Packet1(uvd, 0x007e, 0x000e); + ibmcam_model3_Packet1(uvd, 0x0096, 0x00a0); /* Red gain */ + ibmcam_model3_Packet1(uvd, 0x0097, 0x0096); /* Blue gain */ + ibmcam_model3_Packet1(uvd, 0x0098, 0x000b); + + switch (uvd->videosize) { + case VIDEOSIZE_160x120: + ibmcam_veio(uvd, 0, 0x0002, 0x0106); + ibmcam_veio(uvd, 0, 0x0008, 0x0107); + ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */ + ibmcam_model3_Packet1(uvd, 0x001f, 0x0000); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0039, 0x001f); /* Same */ + ibmcam_model3_Packet1(uvd, 0x003b, 0x003c); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0040, 0x000a); + ibmcam_model3_Packet1(uvd, 0x0051, 0x000a); break; - case VIDEOSIZE_352x288: - frame->frmwidth = 352; - frame->frmheight = 288; - frame->order_uv = 0; /* V Y U Y ... */ - frame->hdr_sig = 0x00; /* 00 FF 00 00 */ + case VIDEOSIZE_320x240: + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x0062, 0x0107); + ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */ + ibmcam_model3_Packet1(uvd, 0x001f, 0x0000); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0039, 0x001f); /* Same */ + ibmcam_model3_Packet1(uvd, 0x003b, 0x003c); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0040, 0x0008); + ibmcam_model3_Packet1(uvd, 0x0051, 0x000b); + break; + case VIDEOSIZE_640x480: + ibmcam_veio(uvd, 0, 0x0002, 0x0106); /* Adjustments */ + ibmcam_veio(uvd, 0, 0x00b4, 0x0107); /* Adjustments */ + ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */ + ibmcam_model3_Packet1(uvd, 0x001f, 0x0002); /* !Same */ + ibmcam_model3_Packet1(uvd, 0x0039, 0x003e); /* !Same */ + ibmcam_model3_Packet1(uvd, 0x0040, 0x0008); + ibmcam_model3_Packet1(uvd, 0x0051, 0x000a); break; } - frame->order_yc = (ibmcam->camera_model == IBMCAM_MODEL_2); - - width = frame->width; - RESTRICT_TO_RANGE(width, min_imgwidth, imgwidth); - width &= ~7; /* Multiple of 8 */ - height = frame->height; - RESTRICT_TO_RANGE(height, min_imgheight, imgheight); - height &= ~3; /* Multiple of 4 */ - - return 0; -} - -/* - * ibmcam_open() - * - * This is part of Video 4 Linux API. The driver can be opened by one - * client only (checks internal counter 'ibmcam->user'). The procedure - * then allocates buffers needed for video processing. - * - * History: - * 1/22/00 Rewrote, moved scratch buffer allocation here. Now the - * camera is also initialized here (once per connect), at - * expense of V4L client (it waits on open() call). - * 1/27/00 Used IBMCAM_NUMSBUF as number of URB buffers. - * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). - */ -static int ibmcam_open(struct video_device *dev, int flags) -{ - struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; - const int sb_size = FRAMES_PER_DESC * ibmcam->iso_packet_len; - int i, err = 0; - - MOD_INC_USE_COUNT; - down(&ibmcam->lock); - - if (ibmcam->user) - err = -EBUSY; - else { - /* Clean pointers so we know if we allocated something */ - for (i=0; i < IBMCAM_NUMSBUF; i++) - ibmcam->sbuf[i].data = NULL; - - /* Allocate memory for the frame buffers */ - ibmcam->fbuf_size = IBMCAM_NUMFRAMES * MAX_FRAME_SIZE; - ibmcam->fbuf = rvmalloc(ibmcam->fbuf_size); - ibmcam->scratch = kmalloc(scratchbufsize, GFP_KERNEL); - ibmcam->scratchlen = 0; - if ((ibmcam->fbuf == NULL) || (ibmcam->scratch == NULL)) - err = -ENOMEM; - else { - /* Allocate all buffers */ - for (i=0; i < IBMCAM_NUMFRAMES; i++) { - ibmcam->frame[i].grabstate = FRAME_UNUSED; - ibmcam->frame[i].data = ibmcam->fbuf + i*MAX_FRAME_SIZE; - /* - * Set default sizes in case IOCTL (VIDIOCMCAPTURE) - * is not used (using read() instead). - */ - ibmcam->frame[i].width = imgwidth; - ibmcam->frame[i].height = imgheight; - ibmcam->frame[i].bytes_read = 0; - } - for (i=0; i < IBMCAM_NUMSBUF; i++) { - ibmcam->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); - if (ibmcam->sbuf[i].data == NULL) { - err = -ENOMEM; - break; - } - } - } - if (err) { - /* Have to free all that memory */ - if (ibmcam->fbuf != NULL) { - rvfree(ibmcam->fbuf, ibmcam->fbuf_size); - ibmcam->fbuf = NULL; - } - if (ibmcam->scratch != NULL) { - kfree(ibmcam->scratch); - ibmcam->scratch = NULL; - } - for (i=0; i < IBMCAM_NUMSBUF; i++) { - if (ibmcam->sbuf[i].data != NULL) { - kfree (ibmcam->sbuf[i].data); - ibmcam->sbuf[i].data = NULL; - } - } + /* 01.01.08 - Added for RCA video in support -LO */ + if(init_model3_input) { + if (debug > 0) + info("Setting input to RCA."); + for (i=0; i < (sizeof(initData)/sizeof(initData[0])); i++) { + ibmcam_veio(uvd, initData[i].req, initData[i].value, initData[i].index); } } - /* If so far no errors then we shall start the camera */ - if (!err) { - err = ibmcam_init_isoc(ibmcam); - if (!err) { - /* Send init sequence only once, it's large! */ - if (!ibmcam->initialized) { - int setup_ok = 0; - if (ibmcam->camera_model == IBMCAM_MODEL_1) - setup_ok = usb_ibmcam_model1_setup(ibmcam); - else if (ibmcam->camera_model == IBMCAM_MODEL_2) - setup_ok = usb_ibmcam_model2_setup(ibmcam); - if (setup_ok) - ibmcam->initialized = 1; - else - err = -EBUSY; - } - if (!err) - ibmcam->user++; - } - } - up(&ibmcam->lock); - if (err) - MOD_DEC_USE_COUNT; - return err; + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); } /* - * ibmcam_close() - * - * This is part of Video 4 Linux API. The procedure - * stops streaming and deallocates all buffers that were earlier - * allocated in ibmcam_open(). + * ibmcam_video_stop() * - * History: - * 1/22/00 Moved scratch buffer deallocation here. - * 1/27/00 Used IBMCAM_NUMSBUF as number of URB buffers. - * 5/24/00 Moved MOD_DEC_USE_COUNT outside of code that can sleep. + * This code tells camera to stop streaming. The interface remains + * configured and bandwidth - claimed. */ -static void ibmcam_close(struct video_device *dev) +static void ibmcam_video_stop(uvd_t *uvd) { - struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; - int i; - - down(&ibmcam->lock); + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + ibmcam_veio(uvd, 0, 0x00, 0x010c); + ibmcam_veio(uvd, 0, 0x00, 0x010c); + ibmcam_veio(uvd, 0, 0x01, 0x0114); + ibmcam_veio(uvd, 0, 0xc0, 0x010c); + ibmcam_veio(uvd, 0, 0x00, 0x010c); + ibmcam_send_FF_04_02(uvd); + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x81, 0x0100); /* LED Off */ + break; + case IBMCAM_MODEL_2: +case IBMCAM_MODEL_4: + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop the camera */ + + ibmcam_model2_Packet1(uvd, 0x0030, 0x0004); + + ibmcam_veio(uvd, 0, 0x0080, 0x0100); /* LED Off */ + ibmcam_veio(uvd, 0, 0x0020, 0x0111); + ibmcam_veio(uvd, 0, 0x00a0, 0x0111); - ibmcam_stop_isoc(ibmcam); + ibmcam_model2_Packet1(uvd, 0x0030, 0x0002); - rvfree(ibmcam->fbuf, ibmcam->fbuf_size); - kfree(ibmcam->scratch); - for (i=0; i < IBMCAM_NUMSBUF; i++) - kfree(ibmcam->sbuf[i].data); + ibmcam_veio(uvd, 0, 0x0020, 0x0111); + ibmcam_veio(uvd, 0, 0x0000, 0x0112); + break; + case IBMCAM_MODEL_3: +#if 1 + ibmcam_veio(uvd, 0, 0x0000, 0x010c); - ibmcam->user--; + /* Here we are supposed to select video interface alt. setting 0 */ + ibmcam_veio(uvd, 0, 0x0006, 0x012c); - if (ibmcam->remove_pending) { - printk(KERN_INFO "ibmcam_close: Final disconnect.\n"); - usb_ibmcam_release(ibmcam); - } - up(&ibmcam->lock); - MOD_DEC_USE_COUNT; -} + ibmcam_model3_Packet1(uvd, 0x0046, 0x0000); -static long ibmcam_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) -{ - return -EINVAL; + ibmcam_veio(uvd, 1, 0x0000, 0x0116); + ibmcam_veio(uvd, 0, 0x0064, 0x0116); + ibmcam_veio(uvd, 1, 0x0000, 0x0115); + ibmcam_veio(uvd, 0, 0x0003, 0x0115); + ibmcam_veio(uvd, 0, 0x0008, 0x0123); + ibmcam_veio(uvd, 0, 0x0000, 0x0117); + ibmcam_veio(uvd, 0, 0x0000, 0x0112); + ibmcam_veio(uvd, 0, 0x0080, 0x0100); + IBMCAM_T(uvd)->initialized = 0; +#endif + break; + } /* switch */ } /* - * ibmcam_ioctl() + * ibmcam_reinit_iso() * - * This is part of Video 4 Linux API. The procedure handles ioctl() calls. + * This procedure sends couple of commands to the camera and then + * resets the video pipe. This sequence was observed to reinit the + * camera or, at least, to initiate ISO data stream. * * History: - * 1/22/00 Corrected VIDIOCSPICT to reject unsupported settings. + * 1/2/00 Created. */ -static int ibmcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +static void ibmcam_reinit_iso(uvd_t *uvd, int do_stop) { - struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; - - if (!IBMCAM_IS_OPERATIONAL(ibmcam)) - return -EFAULT; - - switch (cmd) { - case VIDIOCGCAP: - { - if (copy_to_user(arg, &ibmcam->vcap, sizeof(ibmcam->vcap))) - return -EFAULT; - return 0; - } - case VIDIOCGCHAN: - { - if (copy_to_user(arg, &ibmcam->vchan, sizeof(ibmcam->vchan))) - return -EFAULT; - return 0; - } - case VIDIOCSCHAN: - { - int v; - - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if ((v < 0) || (v >= 3)) /* 3 grades of lighting conditions */ - return -EINVAL; - if (v != ibmcam->vchan.channel) { - ibmcam->vchan.channel = v; - usb_ibmcam_change_lighting_conditions(ibmcam); - } - return 0; - } - case VIDIOCGPICT: - { - if (copy_to_user(arg, &ibmcam->vpic, sizeof(ibmcam->vpic))) - return -EFAULT; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture tmp; - /* - * Use temporary 'video_picture' structure to preserve our - * own settings (such as color depth, palette) that we - * aren't allowing everyone (V4L client) to change. - */ - if (copy_from_user(&tmp, arg, sizeof(tmp))) - return -EFAULT; - ibmcam->vpic.brightness = tmp.brightness; - ibmcam->vpic.hue = tmp.hue; - ibmcam->vpic.colour = tmp.colour; - ibmcam->vpic.contrast = tmp.contrast; - usb_ibmcam_adjust_picture(ibmcam); - return 0; - } - case VIDIOCSWIN: - { - struct video_window vw; - - if (copy_from_user(&vw, arg, sizeof(vw))) - return -EFAULT; - if (vw.flags) - return -EINVAL; - if (vw.clipcount) - return -EINVAL; - if (vw.height != imgheight) - return -EINVAL; - if (vw.width != imgwidth) - return -EINVAL; - - ibmcam->compress = 0; - - return 0; - } - case VIDIOCGWIN: - { - struct video_window vw; - - vw.x = 0; - vw.y = 0; - vw.width = imgwidth; - vw.height = imgheight; - vw.chromakey = 0; - vw.flags = usb_ibmcam_calculate_fps(); - - if (copy_to_user(arg, &vw, sizeof(vw))) - return -EFAULT; - - return 0; - } - case VIDIOCGMBUF: - { - struct video_mbuf vm; - - memset(&vm, 0, sizeof(vm)); - vm.size = MAX_FRAME_SIZE * 2; - vm.frames = 2; - vm.offsets[0] = 0; - vm.offsets[1] = MAX_FRAME_SIZE; - - if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) - return -EFAULT; - - return 0; - } - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - - if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) - return -EFAULT; - - if (debug >= 1) - printk(KERN_DEBUG "frame: %d, size: %dx%d, format: %d\n", - vm.frame, vm.width, vm.height, vm.format); - - if (vm.format != VIDEO_PALETTE_RGB24) - return -EINVAL; - - if ((vm.frame != 0) && (vm.frame != 1)) - return -EINVAL; - - if (ibmcam->frame[vm.frame].grabstate == FRAME_GRABBING) - return -EBUSY; - - /* Don't compress if the size changed */ - if ((ibmcam->frame[vm.frame].width != vm.width) || - (ibmcam->frame[vm.frame].height != vm.height)) - ibmcam->compress = 0; - - ibmcam->frame[vm.frame].width = vm.width; - ibmcam->frame[vm.frame].height = vm.height; - - /* Mark it as ready */ - ibmcam->frame[vm.frame].grabstate = FRAME_READY; - - return ibmcam_new_frame(ibmcam, vm.frame); - } - case VIDIOCSYNC: - { - int frame; - - if (copy_from_user((void *)&frame, arg, sizeof(int))) - return -EFAULT; - - if (debug >= 1) - printk(KERN_DEBUG "ibmcam: syncing to frame %d\n", frame); - - switch (ibmcam->frame[frame].grabstate) { - case FRAME_UNUSED: - return -EINVAL; - case FRAME_READY: - case FRAME_GRABBING: - case FRAME_ERROR: - { - int ntries; - redo: - if (!IBMCAM_IS_OPERATIONAL(ibmcam)) - return -EIO; - ntries = 0; - do { - interruptible_sleep_on(&ibmcam->frame[frame].wq); - if (signal_pending(current)) { - if (flags & FLAGS_RETRY_VIDIOCSYNC) { - /* Polling apps will destroy frames with that! */ - ibmcam_new_frame(ibmcam, frame); - usb_ibmcam_testpattern(ibmcam, 1, 0); - ibmcam->curframe = -1; - ibmcam->frame_num++; - - /* This will request another frame. */ - if (waitqueue_active(&ibmcam->frame[frame].wq)) - wake_up_interruptible(&ibmcam->frame[frame].wq); - return 0; - } else { - /* Standard answer: not ready yet! */ - return -EINTR; - } - } - } while (ibmcam->frame[frame].grabstate == FRAME_GRABBING); - - if (ibmcam->frame[frame].grabstate == FRAME_ERROR) { - int ret = ibmcam_new_frame(ibmcam, frame); - if (ret < 0) - return ret; - goto redo; - } - } - case FRAME_DONE: - ibmcam->frame[frame].grabstate = FRAME_UNUSED; - break; - } - - ibmcam->frame[frame].grabstate = FRAME_UNUSED; - - return 0; - } - case VIDIOCGFBUF: - { - struct video_buffer vb; - - memset(&vb, 0, sizeof(vb)); - vb.base = NULL; /* frame buffer not supported, not used */ - - if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) - return -EFAULT; - - return 0; - } - case VIDIOCKEY: - return 0; - - case VIDIOCCAPTURE: - return -EINVAL; - - case VIDIOCSFBUF: - - case VIDIOCGTUNER: - case VIDIOCSTUNER: - - case VIDIOCGFREQ: - case VIDIOCSFREQ: - - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - - default: - return -ENOIOCTLCMD; + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + if (do_stop) + ibmcam_video_stop(uvd); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_model1_setup_after_video_if(uvd); + break; + case IBMCAM_MODEL_2: + ibmcam_model2_setup_after_video_if(uvd); + break; + case IBMCAM_MODEL_3: + ibmcam_video_stop(uvd); + ibmcam_model3_setup_after_video_if(uvd); + break; + case IBMCAM_MODEL_4: + ibmcam_model4_setup_after_video_if(uvd); + break; } - return 0; } -static long ibmcam_read(struct video_device *dev, char *buf, unsigned long count, int noblock) +static void ibmcam_video_start(uvd_t *uvd) { - struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; - int frmx = -1; - volatile struct ibmcam_frame *frame; - - if (debug >= 1) - printk(KERN_DEBUG "ibmcam_read: %ld bytes, noblock=%d\n", count, noblock); - - if (!IBMCAM_IS_OPERATIONAL(ibmcam) || (buf == NULL)) - return -EFAULT; - - /* See if a frame is completed, then use it. */ - if (ibmcam->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ - frmx = 0; - else if (ibmcam->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */ - frmx = 1; - - if (noblock && (frmx == -1)) - return -EAGAIN; - - /* If no FRAME_DONE, look for a FRAME_GRABBING state. */ - /* See if a frame is in process (grabbing), then use it. */ - if (frmx == -1) { - if (ibmcam->frame[0].grabstate == FRAME_GRABBING) - frmx = 0; - else if (ibmcam->frame[1].grabstate == FRAME_GRABBING) - frmx = 1; - } - - /* If no frame is active, start one. */ - if (frmx == -1) - ibmcam_new_frame(ibmcam, frmx = 0); - - frame = &ibmcam->frame[frmx]; - -restart: - if (!IBMCAM_IS_OPERATIONAL(ibmcam)) - return -EIO; - while (frame->grabstate == FRAME_GRABBING) { - interruptible_sleep_on((void *)&frame->wq); - if (signal_pending(current)) - return -EINTR; - } - - if (frame->grabstate == FRAME_ERROR) { - frame->bytes_read = 0; - if (ibmcam_new_frame(ibmcam, frmx)) - printk(KERN_ERR "ibmcam_read: ibmcam_new_frame error\n"); - goto restart; - } - - if (debug >= 1) - printk(KERN_DEBUG "ibmcam_read: frmx=%d, bytes_read=%ld, scanlength=%ld\n", - frmx, frame->bytes_read, frame->scanlength); - - /* copy bytes to user space; we allow for partials reads */ - if ((count + frame->bytes_read) > frame->scanlength) - count = frame->scanlength - frame->bytes_read; - - if (copy_to_user(buf, frame->data + frame->bytes_read, count)) - return -EFAULT; - - frame->bytes_read += count; - if (debug >= 1) - printk(KERN_DEBUG "ibmcam_read: {copy} count used=%ld, new bytes_read=%ld\n", - count, frame->bytes_read); - - if (frame->bytes_read >= frame->scanlength) { /* All data has been read */ - frame->bytes_read = 0; - - /* Mark it as available to be used again. */ - ibmcam->frame[frmx].grabstate = FRAME_UNUSED; - if (ibmcam_new_frame(ibmcam, frmx ? 0 : 1)) - printk(KERN_ERR "ibmcam_read: ibmcam_new_frame returned error\n"); - } - - return count; + ibmcam_change_lighting_conditions(uvd); + ibmcam_set_sharpness(uvd); + ibmcam_reinit_iso(uvd, 0); } -static int ibmcam_mmap(struct video_device *dev, const char *adr, unsigned long size) +/* + * Return negative code on failure, 0 on success. + */ +static int ibmcam_setup_on_open(uvd_t *uvd) { - struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; - unsigned long start = (unsigned long)adr; - unsigned long page, pos; - - if (!IBMCAM_IS_OPERATIONAL(ibmcam)) - return -EFAULT; - - if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) - return -EINVAL; - - pos = (unsigned long)ibmcam->fbuf; - while (size > 0) { - page = kvirt_to_pa(pos); - if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + int setup_ok = 0; /* Success by default */ + /* Send init sequence only once, it's large! */ + if (!IBMCAM_T(uvd)->initialized) { /* FIXME rename */ + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + setup_ok = ibmcam_model1_setup(uvd); + break; + case IBMCAM_MODEL_2: + setup_ok = ibmcam_model2_setup(uvd); + break; + case IBMCAM_MODEL_3: + case IBMCAM_MODEL_4: + /* We do all setup when Isoc stream is requested */ + break; + } + IBMCAM_T(uvd)->initialized = (setup_ok != 0); } - - return 0; + return setup_ok; } -static struct video_device ibmcam_template = { - name: "CPiA USB Camera", - type: VID_TYPE_CAPTURE, - hardware: VID_HARDWARE_CPIA, - open: ibmcam_open, - close: ibmcam_close, - read: ibmcam_read, - write: ibmcam_write, - ioctl: ibmcam_ioctl, - mmap: ibmcam_mmap, -}; - -static void usb_ibmcam_configure_video(struct usb_ibmcam *ibmcam) +static void ibmcam_configure_video(uvd_t *uvd) { - if (ibmcam == NULL) + if (uvd == NULL) return; RESTRICT_TO_RANGE(init_brightness, 0, 255); @@ -2878,289 +3609,320 @@ RESTRICT_TO_RANGE(init_hue, 0, 255); RESTRICT_TO_RANGE(hue_correction, 0, 255); - memset(&ibmcam->vpic, 0, sizeof(ibmcam->vpic)); - memset(&ibmcam->vpic_old, 0x55, sizeof(ibmcam->vpic_old)); + memset(&uvd->vpic, 0, sizeof(uvd->vpic)); + memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); - ibmcam->vpic.colour = init_color << 8; - ibmcam->vpic.hue = init_hue << 8; - ibmcam->vpic.brightness = init_brightness << 8; - ibmcam->vpic.contrast = init_contrast << 8; - ibmcam->vpic.whiteness = 105 << 8; /* This one isn't used */ - ibmcam->vpic.depth = 24; - ibmcam->vpic.palette = VIDEO_PALETTE_RGB24; - - memset(&ibmcam->vcap, 0, sizeof(ibmcam->vcap)); - strcpy(ibmcam->vcap.name, "IBM USB Camera"); - ibmcam->vcap.type = VID_TYPE_CAPTURE; - ibmcam->vcap.channels = 1; - ibmcam->vcap.audios = 0; - ibmcam->vcap.maxwidth = imgwidth; - ibmcam->vcap.maxheight = imgheight; - ibmcam->vcap.minwidth = min_imgwidth; - ibmcam->vcap.minheight = min_imgheight; - - memset(&ibmcam->vchan, 0, sizeof(ibmcam->vchan)); - ibmcam->vchan.flags = 0; - ibmcam->vchan.tuners = 0; - ibmcam->vchan.channel = 0; - ibmcam->vchan.type = VIDEO_TYPE_CAMERA; - strcpy(ibmcam->vchan.name, "Camera"); + uvd->vpic.colour = init_color << 8; + uvd->vpic.hue = init_hue << 8; + uvd->vpic.brightness = init_brightness << 8; + uvd->vpic.contrast = init_contrast << 8; + uvd->vpic.whiteness = 105 << 8; /* This one isn't used */ + uvd->vpic.depth = 24; + uvd->vpic.palette = VIDEO_PALETTE_RGB24; + + memset(&uvd->vcap, 0, sizeof(uvd->vcap)); + strcpy(uvd->vcap.name, "IBM USB Camera"); + uvd->vcap.type = VID_TYPE_CAPTURE; + uvd->vcap.channels = 1; + uvd->vcap.audios = 0; + uvd->vcap.maxwidth = VIDEOSIZE_X(uvd->canvas); + uvd->vcap.maxheight = VIDEOSIZE_Y(uvd->canvas); + uvd->vcap.minwidth = min_canvasWidth; + uvd->vcap.minheight = min_canvasHeight; + + memset(&uvd->vchan, 0, sizeof(uvd->vchan)); + uvd->vchan.flags = 0; + uvd->vchan.tuners = 0; + uvd->vchan.channel = 0; + uvd->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(uvd->vchan.name, "Camera"); } /* - * ibmcam_find_struct() - * - * This code searches the array of preallocated (static) structures - * and returns index of the first one that isn't in use. Returns -1 - * if there are no free structures. - * - * History: - * 1/27/00 Created. - */ -static int ibmcam_find_struct(void) -{ - int i, u; - - for (u = 0; u < MAX_IBMCAM; u++) { - struct usb_ibmcam *ibmcam = &cams[u]; - if (!ibmcam->ibmcam_used) /* This one is free */ - { - ibmcam->ibmcam_used = 1; /* In use now */ - for (i=0; i < IBMCAM_NUMFRAMES; i++) - init_waitqueue_head(&ibmcam->frame[i].wq); - init_MUTEX(&ibmcam->lock); /* to 1 == available */ - ibmcam->dev = NULL; - memcpy(&ibmcam->vdev, &ibmcam_template, sizeof(ibmcam_template)); - return u; - } - } - return -1; -} - -/* - * usb_ibmcam_probe() + * ibmcam_probe() * * This procedure queries device descriptor and accepts the interface * if it looks like IBM C-it camera. * * History: - * 1/22/00 Moved camera init code to ibmcam_open() - * 1/27/00 Changed to use static structures, added locking. - * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). - * 7/3/00 Fixed endianness bug. - */ -static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum, - const struct usb_device_id *id) -{ - struct usb_ibmcam *ibmcam = NULL; - const struct usb_interface_descriptor *interface; - const struct usb_endpoint_descriptor *endpoint; - int devnum, model=0; + * 22-Jan-2000 Moved camera init code to ibmcam_open() + * 27=Jan-2000 Changed to use static structures, added locking. + * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + * 03-Jul-2000 Fixed endianness bug. + * 12-Nov-2000 Reworked to comply with new probe() signature. + * 23-Jan-2001 Added compatibility with 2.2.x kernels. + */ +static void *ibmcam_probe(struct usb_device *dev, unsigned int ifnum +#if defined(usb_device_id_ver) + ,const struct usb_device_id *devid +#endif + ) +{ + uvd_t *uvd = NULL; + int i, nas, model=0, canvasX=0, canvasY=0; + int actInterface=-1, inactInterface=-1, maxPS=0; + unsigned char video_ep = 0; if (debug >= 1) - printk(KERN_DEBUG "ibmcam_probe(%p,%u.)\n", dev, ifnum); + info("ibmcam_probe(%p,%u.)", dev, ifnum); /* We don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) return NULL; + /* Is it an IBM camera? */ + if (dev->descriptor.idVendor != IBMCAM_VENDOR_ID) + return NULL; + if ((dev->descriptor.idProduct != IBMCAM_PRODUCT_ID) && + (dev->descriptor.idProduct != NETCAM_PRODUCT_ID)) + return NULL; + /* Check the version/revision */ switch (dev->descriptor.bcdDevice) { case 0x0002: if (ifnum != 2) return NULL; - printk(KERN_INFO "IBM USB camera found (model 1, rev. 0x%04x).\n", - dev->descriptor.bcdDevice); model = IBMCAM_MODEL_1; break; case 0x030A: if (ifnum != 0) return NULL; - printk(KERN_INFO "IBM USB camera found (model 2, rev. 0x%04x).\n", - dev->descriptor.bcdDevice); - model = IBMCAM_MODEL_2; + if (dev->descriptor.idProduct == NETCAM_PRODUCT_ID) + model = IBMCAM_MODEL_4; + else + model = IBMCAM_MODEL_2; break; - - /* ibmcam_table contents prevents any other values from ever - being passed to us, so no need for "default" case. */ + case 0x0301: + if (ifnum != 0) + return NULL; + model = IBMCAM_MODEL_3; + break; + default: + err("IBM camera with revision 0x%04x is not supported.", + dev->descriptor.bcdDevice); + return NULL; } + info("IBM USB camera found (model %d, rev. 0x%04x)", + model, dev->descriptor.bcdDevice); /* Validate found interface: must have one ISO endpoint */ - interface = &dev->actconfig->interface[ifnum].altsetting[0]; - if (interface->bNumEndpoints != 1) { - printk(KERN_ERR "IBM camera: interface %d. has %u. endpoints!\n", - ifnum, (unsigned)(interface->bNumEndpoints)); + nas = dev->actconfig->interface[ifnum].num_altsetting; + if (debug > 0) + info("Number of alternate settings=%d.", nas); + if (nas < 2) { + err("Too few alternate settings for this camera!"); return NULL; } - endpoint = &interface->endpoint[0]; - if ((endpoint->bmAttributes & 0x03) != 0x01) { - printk(KERN_ERR "IBM camera: interface %d. has non-ISO endpoint!\n", ifnum); - return NULL; + /* Validate all alternate settings */ + for (i=0; i < nas; i++) { + const struct usb_interface_descriptor *interface; + const struct usb_endpoint_descriptor *endpoint; + + interface = &dev->actconfig->interface[ifnum].altsetting[i]; + if (interface->bNumEndpoints != 1) { + err("Interface %d. has %u. endpoints!", + ifnum, (unsigned)(interface->bNumEndpoints)); + return NULL; + } + endpoint = &interface->endpoint[0]; + if (video_ep == 0) + video_ep = endpoint->bEndpointAddress; + else if (video_ep != endpoint->bEndpointAddress) { + err("Alternate settings have different endpoint addresses!"); + return NULL; + } + if ((endpoint->bmAttributes & 0x03) != 0x01) { + err("Interface %d. has non-ISO endpoint!", ifnum); + return NULL; + } + if ((endpoint->bEndpointAddress & 0x80) == 0) { + err("Interface %d. has ISO OUT endpoint!", ifnum); + return NULL; + } + if (endpoint->wMaxPacketSize == 0) { + if (inactInterface < 0) + inactInterface = i; + else { + err("More than one inactive alt. setting!"); + return NULL; + } + } else { + if (actInterface < 0) { + actInterface = i; + maxPS = endpoint->wMaxPacketSize; + if (debug > 0) + info("Active setting=%d. maxPS=%d.", i, maxPS); + } else + err("More than one active alt. setting! Ignoring #%d.", i); + } } - if ((endpoint->bEndpointAddress & 0x80) == 0) { - printk(KERN_ERR "IBM camera: interface %d. has ISO OUT endpoint!\n", ifnum); + if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) { + err("Failed to recognize the camera!"); return NULL; } /* Validate options */ - if (model == IBMCAM_MODEL_1) { + switch (model) { + case IBMCAM_MODEL_1: RESTRICT_TO_RANGE(lighting, 0, 2); - RESTRICT_TO_RANGE(videosize, VIDEOSIZE_128x96, VIDEOSIZE_352x288); - } else { + RESTRICT_TO_RANGE(size, SIZE_128x96, SIZE_352x288); + if (framerate < 0) + framerate = 2; + canvasX = 352; + canvasY = 288; + break; + case IBMCAM_MODEL_2: RESTRICT_TO_RANGE(lighting, 0, 15); - RESTRICT_TO_RANGE(videosize, VIDEOSIZE_176x144, VIDEOSIZE_352x240); + RESTRICT_TO_RANGE(size, SIZE_176x144, SIZE_352x240); + if (framerate < 0) + framerate = 2; + canvasX = 352; + canvasY = 240; + break; + case IBMCAM_MODEL_3: + RESTRICT_TO_RANGE(lighting, 0, 15); /* FIXME */ + switch (size) { + case SIZE_160x120: + canvasX = 160; + canvasY = 120; + if (framerate < 0) + framerate = 2; + RESTRICT_TO_RANGE(framerate, 0, 5); + break; + default: + info("IBM camera: using 320x240"); + size = SIZE_320x240; + /* No break here */ + case SIZE_320x240: + canvasX = 320; + canvasY = 240; + if (framerate < 0) + framerate = 3; + RESTRICT_TO_RANGE(framerate, 0, 5); + break; + case SIZE_640x480: + canvasX = 640; + canvasY = 480; + framerate = 0; /* Slowest, and maybe even that is too fast */ + break; + } + break; + case IBMCAM_MODEL_4: + RESTRICT_TO_RANGE(lighting, 0, 2); + switch (size) { + case SIZE_128x96: + canvasX = 128; + canvasY = 96; + break; + case SIZE_160x120: + canvasX = 160; + canvasY = 120; + break; + default: + info("IBM NetCamera: using 176x144"); + size = SIZE_176x144; + /* No break here */ + case SIZE_176x144: + canvasX = 176; + canvasY = 144; + break; + case SIZE_320x240: + canvasX = 320; + canvasY = 240; + break; + case SIZE_352x288: + canvasX = 352; + canvasY = 288; + break; + } + break; + default: + err("IBM camera: Model %d. not supported!", model); + return NULL; } /* Code below may sleep, need to lock module while we are here */ MOD_INC_USE_COUNT; - - devnum = ibmcam_find_struct(); - if (devnum == -1) { - printk(KERN_INFO "IBM USB camera driver: Too many devices!\n"); - ibmcam = NULL; /* Do not free, it's preallocated */ - goto probe_done; - } - ibmcam = &cams[devnum]; - - down(&ibmcam->lock); - ibmcam->camera_model = model; - ibmcam->remove_pending = 0; - ibmcam->last_error = 0; - ibmcam->dev = dev; - ibmcam->iface = ifnum; - ibmcam->ifaceAltInactive = 0; - ibmcam->ifaceAltActive = 1; - ibmcam->video_endp = endpoint->bEndpointAddress; - ibmcam->iso_packet_len = 1014; - ibmcam->compress = 0; - ibmcam->user=0; - - usb_ibmcam_configure_video(ibmcam); - up (&ibmcam->lock); - - if (video_register_device(&ibmcam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { - printk(KERN_ERR "video_register_device failed\n"); - ibmcam = NULL; /* Do not free, it's preallocated */ - } - if (debug > 1) - printk(KERN_DEBUG "video_register_device() successful\n"); -probe_done: - MOD_DEC_USE_COUNT; - return ibmcam; -} - -/* - * usb_ibmcam_release() - * - * This code does final release of struct usb_ibmcam. This happens - * after the device is disconnected -and- all clients closed their files. - * - * History: - * 1/27/00 Created. - */ -static void usb_ibmcam_release(struct usb_ibmcam *ibmcam) -{ - video_unregister_device(&ibmcam->vdev); - if (debug > 0) - printk(KERN_DEBUG "usb_ibmcam_release: Video unregistered.\n"); - ibmcam->ibmcam_used = 0; - ibmcam->initialized = 0; -} - -/* - * usb_ibmcam_disconnect() - * - * This procedure stops all driver activity, deallocates interface-private - * structure (pointed by 'ptr') and after that driver should be removable - * with no ill consequences. - * - * This code handles surprise removal. The ibmcam->user is a counter which - * increments on open() and decrements on close(). If we see here that - * this counter is not 0 then we have a client who still has us opened. - * We set ibmcam->remove_pending flag as early as possible, and after that - * all access to the camera will gracefully fail. These failures should - * prompt client to (eventually) close the video device, and then - in - * ibmcam_close() - we decrement ibmcam->ibmcam_used and usage counter. - * - * History: - * 1/22/00 Added polling of MOD_IN_USE to delay removal until all users gone. - * 1/27/00 Reworked to allow pending disconnects; see ibmcam_close() - * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). - */ -static void usb_ibmcam_disconnect(struct usb_device *dev, void *ptr) -{ - static const char proc[] = "usb_ibmcam_disconnect"; - struct usb_ibmcam *ibmcam = (struct usb_ibmcam *) ptr; - - MOD_INC_USE_COUNT; - - if (debug > 0) - printk(KERN_DEBUG "%s(%p,%p.)\n", proc, dev, ptr); - - down(&ibmcam->lock); - ibmcam->remove_pending = 1; /* Now all ISO data will be ignored */ - - /* At this time we ask to cancel outstanding URBs */ - ibmcam_stop_isoc(ibmcam); - - ibmcam->dev = NULL; /* USB device is no more */ - - if (ibmcam->user) - printk(KERN_INFO "%s: In use, disconnect pending.\n", proc); - else - usb_ibmcam_release(ibmcam); - up(&ibmcam->lock); - printk(KERN_INFO "IBM USB camera disconnected.\n"); - + uvd = usbvideo_AllocateDevice(cams); + if (uvd != NULL) { + /* Here uvd is a fully allocated uvd_t object */ + uvd->flags = flags; + uvd->debug = debug; + uvd->dev = dev; + uvd->iface = ifnum; + uvd->ifaceAltInactive = inactInterface; + uvd->ifaceAltActive = actInterface; + uvd->video_endp = video_ep; + uvd->iso_packet_len = maxPS; + uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24; + uvd->defaultPalette = VIDEO_PALETTE_RGB24; + uvd->canvas = VIDEOSIZE(canvasX, canvasY); + uvd->videosize = ibmcam_size_to_videosize(size); + + /* Initialize ibmcam-specific data */ + assert(IBMCAM_T(uvd) != NULL); + IBMCAM_T(uvd)->camera_model = model; + IBMCAM_T(uvd)->initialized = 0; + + ibmcam_configure_video(uvd); + + i = usbvideo_RegisterVideoDevice(uvd); + if (i != 0) { + err("usbvideo_RegisterVideoDevice() failed."); + uvd = NULL; + } + } MOD_DEC_USE_COUNT; + return uvd; } -static struct usb_device_id ibmcam_table [] = { - { USB_DEVICE_VER(0x0545, 0x8080, 0x0002, 0x0002) }, - { USB_DEVICE_VER(0x0545, 0x8080, 0x030a, 0x030a) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, ibmcam_table); - -static struct usb_driver ibmcam_driver = { - name: "ibmcam", - probe: usb_ibmcam_probe, - disconnect: usb_ibmcam_disconnect, - id_table: ibmcam_table, -}; - /* - * usb_ibmcam_init() + * ibmcam_init() * * This code is run to initialize the driver. * * History: - * 1/27/00 Reworked to use statically allocated usb_ibmcam structures. + * 1/27/00 Reworked to use statically allocated ibmcam structures. + * 21/10/00 Completely redesigned to use usbvideo services. */ -static int __init usb_ibmcam_init(void) +static int __init ibmcam_init(void) { - unsigned u; - - /* Initialize struct */ - for (u = 0; u < MAX_IBMCAM; u++) { - struct usb_ibmcam *ibmcam = &cams[u]; - memset (ibmcam, 0, sizeof(struct usb_ibmcam)); - } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); - return usb_register(&ibmcam_driver); -} - -static void __exit usb_ibmcam_cleanup(void) -{ - usb_deregister(&ibmcam_driver); -} - -module_init(usb_ibmcam_init); -module_exit(usb_ibmcam_cleanup); - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); + usbvideo_cb_t cbTbl; + memset(&cbTbl, 0, sizeof(cbTbl)); + cbTbl.probe = ibmcam_probe; + cbTbl.setupOnOpen = ibmcam_setup_on_open; + cbTbl.videoStart = ibmcam_video_start; + cbTbl.videoStop = ibmcam_video_stop; + cbTbl.processData = ibmcam_ProcessIsocData; + cbTbl.postProcess = usbvideo_DeinterlaceFrame; + cbTbl.adjustPicture = ibmcam_adjust_picture; + cbTbl.getFPS = ibmcam_calculate_fps; + return usbvideo_register( + &cams, + MAX_IBMCAM, + sizeof(ibmcam_t), + "ibmcam", + &cbTbl, + THIS_MODULE); +} + +static void __exit ibmcam_cleanup(void) +{ + usbvideo_Deregister(&cams); +} + +#if defined(usb_device_id_ver) + +static __devinitdata struct usb_device_id id_table[] = { + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x00, 0x02) }, /* Model 1 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x03, 0x0a) }, /* Model 2 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x03, 0x01) }, /* Model 3 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, NETCAM_PRODUCT_ID, 0x03, 0x0a) }, /* Model 4 */ + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, id_table); +#endif /* defined(usb_device_id_ver) */ +module_init(ibmcam_init); +module_exit(ibmcam_cleanup); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/inode.c linux.ac/drivers/usb/inode.c --- linux.vanilla/drivers/usb/inode.c Sat May 26 16:53:14 2001 +++ linux.ac/drivers/usb/inode.c Wed May 23 00:35:59 2001 @@ -260,11 +260,15 @@ struct list_head *list; struct usb_bus *bus; + read_lock_irq (&usb_bus_list_lock); for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) { bus = list_entry(list, struct usb_bus, bus_list); - if (bus->busnum == busnr) + if (bus->busnum == busnr) { + read_unlock_irq (&usb_bus_list_lock); return bus; + } } + read_unlock_irq (&usb_bus_list_lock); return NULL; } @@ -412,7 +416,7 @@ if (i < 2+NRSPECIAL) return 0; i -= 2+NRSPECIAL; - lock_kernel(); + read_lock_irq (&usb_bus_list_lock); for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) { if (i > 0) { i--; @@ -424,7 +428,7 @@ break; filp->f_pos++; } - unlock_kernel(); + read_unlock_irq (&usb_bus_list_lock); return 0; } } @@ -635,13 +639,13 @@ list_add_tail(&inode->u.usbdev_i.slist, &s->u.usbdevfs_sb.ilist); list_add_tail(&inode->u.usbdev_i.dlist, &special[i].inodes); } - lock_kernel(); + read_lock_irq (&usb_bus_list_lock); for (blist = usb_bus_list.next; blist != &usb_bus_list; blist = blist->next) { bus = list_entry(blist, struct usb_bus, bus_list); new_bus_inode(bus, s); recurse_new_dev_inode(bus->root_hub, s); } - unlock_kernel(); + read_unlock_irq (&usb_bus_list_lock); return s; out_no_root: diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/kaweth.c linux.ac/drivers/usb/kaweth.c --- linux.vanilla/drivers/usb/kaweth.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/kaweth.c Sun Apr 22 01:58:23 2001 @@ -0,0 +1,1053 @@ +/**************************************************************** + * + * kaweth.c - driver for KL5KUSB101 based USB->Ethernet + * + * (c) 2000 Interlan Communications + * (c) 2000 Stephane Alnet + * (C) 2001 Brad Hards + * + * Original author: The Zapman + * Inspired by, and much credit goes to Michael Rothwell + * for the test equipment, help, and patience + * Based off of (and with thanks to) Petko Manolov's pegaus.c driver. + * Also many thanks to Joel Silverman and Ed Surprenant at Kawasaki + * for providing the firmware and driver resources. + * + * 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, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ****************************************************************/ + +/* TODO: + * Fix in_interrupt() problem + * Develop test procedures for USB net interfaces + * Run test procedures + * Fix bugs from previous two steps + * Snoop other OSs for any tricks we're not doing + * SMP locking + * Reduce arbitrary timeouts + * Smart multicast support + * Temporary MAC change support + * Tunable SOFs parameter - ioctl()? + * Ethernet stats collection + * Code formatting improvements + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#ifdef DEBUG +#define kaweth_dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" ,##arg) +#else +#define kaweth_dbg(format, arg...) do {} while (0) +#endif +#define kaweth_err(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" ,##arg) +#define kaweth_info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" , ##arg) +#define kaweth_warn(format, arg...) printk(KERN_WARNING __FILE__ ": " format "\n" , ##arg) + + +#include "kawethfw.h" + +#define KAWETH_MTU 1514 +#define KAWETH_BUF_SIZE 1664 +#define KAWETH_TX_TIMEOUT (5 * HZ) +#define KAWETH_FIRMWARE_BUF_SIZE 4096 +#define KAWETH_CONTROL_TIMEOUT (30 * HZ) + +#define KAWETH_STATUS_BROKEN 0x0000001 +#define KAWETH_STATUS_CLOSING 0x0000002 + +#define KAWETH_PACKET_FILTER_PROMISCUOUS 0x01 +#define KAWETH_PACKET_FILTER_ALL_MULTICAST 0x02 +#define KAWETH_PACKET_FILTER_DIRECTED 0x04 +#define KAWETH_PACKET_FILTER_BROADCAST 0x08 +#define KAWETH_PACKET_FILTER_MULTICAST 0x10 + +/* Table 7 */ +#define KAWETH_COMMAND_GET_ETHERNET_DESC 0x00 +#define KAWETH_COMMAND_MULTICAST_FILTERS 0x01 +#define KAWETH_COMMAND_SET_PACKET_FILTER 0x02 +#define KAWETH_COMMAND_STATISTICS 0x03 +#define KAWETH_COMMAND_SET_TEMP_MAC 0x06 +#define KAWETH_COMMAND_GET_TEMP_MAC 0x07 +#define KAWETH_COMMAND_SET_URB_SIZE 0x08 +#define KAWETH_COMMAND_SET_SOFS_WAIT 0x09 +#define KAWETH_COMMAND_SCAN 0xFF + +#define KAWETH_SOFS_TO_WAIT 0x05 + + +MODULE_AUTHOR("Michael Zappe , Stephane Alnet and Brad Hards "); +MODULE_DESCRIPTION("KL5USB101 USB Ethernet driver"); + +static void *kaweth_probe( + struct usb_device *dev, /* the device */ + unsigned ifnum, /* what interface */ + const struct usb_device_id *id /* from id_table */ + ); +static void kaweth_disconnect(struct usb_device *dev, void *ptr); +int kaweth_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, + devrequest *cmd, void *data, int len, + int timeout); + +/**************************************************************** + * usb_device_id + ****************************************************************/ +static struct usb_device_id usb_klsi_table[] = { + { USB_DEVICE(0x03e8, 0x0008) }, /* AOX Endpoints USB Ethernet */ + { USB_DEVICE(0x0506, 0x03e8) }, /* 3Com 3C19250 */ + { USB_DEVICE(0x0557, 0x2002) }, /* ATEN USB Ethernet */ + { USB_DEVICE(0x0557, 0x4000) }, /* D-Link DSB-650C */ + { USB_DEVICE(0x0565, 0x0002) }, /* Peracom Enet */ + { USB_DEVICE(0x0565, 0x0005) }, /* Peracom Enet2 */ + { USB_DEVICE(0x05e9, 0x0008) }, /* KLSI KL5KUSB101B */ + { USB_DEVICE(0x05e9, 0x0009) }, /* KLSI KL5KUSB101B (Board change) */ + { USB_DEVICE(0x066b, 0x2202) }, /* Linksys USB10T */ + { USB_DEVICE(0x06e1, 0x0008) }, /* ADS USB-10BT */ + { USB_DEVICE(0x06e1, 0x0009) }, /* ADS USB-10BT */ + { USB_DEVICE(0x0707, 0x0100) }, /* SMC 2202USB */ + { USB_DEVICE(0x07aa, 0x0001) }, /* Correga K.K. */ + { USB_DEVICE(0x07b8, 0x4000) }, /* D-Link DU-E10 */ + { USB_DEVICE(0x0846, 0x1001) }, /* NetGear EA-101 */ + { USB_DEVICE(0x0846, 0x1002) }, /* NetGear EA-101 */ + { USB_DEVICE(0x085a, 0x0008) }, /* PortGear Ethernet Adapter */ + { USB_DEVICE(0x085a, 0x0009) }, /* PortGear Ethernet Adapter */ + { USB_DEVICE(0x087d, 0x5704) }, /* Jaton USB Ethernet Device Adapter */ + { USB_DEVICE(0x0951, 0x0008) }, /* Kingston Technology USB Ethernet Adapter */ + { USB_DEVICE(0x095a, 0x3003) }, /* Portsmith Express Ethernet Adapter */ + { USB_DEVICE(0x10bd, 0x1427) }, /* ASANTE USB To Ethernet Adapter */ + { USB_DEVICE(0x1342, 0x0204) }, /* Mobility USB-Ethernet Adapter */ + { USB_DEVICE(0x13d2, 0x0400) }, /* Shark Pocket Adapter */ + { USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */ + { USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */ + { USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */ + { USB_DEVICE(0x2001, 0x4000) }, /* D-link DSB-650C */ + {} /* Null terminator */ +}; + +MODULE_DEVICE_TABLE (usb, usb_klsi_table); + +/**************************************************************** + * kaweth_driver + ****************************************************************/ +static struct usb_driver kaweth_driver = { + name: "kaweth", + probe: kaweth_probe, + disconnect: kaweth_disconnect, + id_table: usb_klsi_table, +}; + +typedef __u8 eth_addr_t[6]; + +/**************************************************************** + * usb_eth_dev + ****************************************************************/ +struct usb_eth_dev { + char *name; + __u16 vendor; + __u16 device; + void *pdata; +}; + +/**************************************************************** + * kaweth_ethernet_configuration + * Refer Table 8 + ****************************************************************/ +struct kaweth_ethernet_configuration +{ + __u8 size; + __u8 reserved1; + __u8 reserved2; + eth_addr_t hw_addr; + __u32 statistics_mask; + __u16 segment_size; + __u16 max_multicast_filters; + __u8 reserved3; +} __attribute__ ((packed)); + +/**************************************************************** + * kaweth_device + ****************************************************************/ +struct kaweth_device +{ + spinlock_t device_lock; + + __u32 status; + + struct usb_device *dev; + struct net_device *net; + wait_queue_head_t control_wait; + + struct urb *rx_urb; + struct urb *tx_urb; + + __u8 firmware_buf[KAWETH_FIRMWARE_BUF_SIZE]; + __u8 tx_buf[KAWETH_BUF_SIZE]; + __u8 rx_buf[KAWETH_BUF_SIZE]; + __u16 packet_filter_bitmap; + + struct kaweth_ethernet_configuration configuration; + + struct net_device_stats stats; +} __attribute__ ((packed)); + + +/**************************************************************** + * kaweth_control + ****************************************************************/ +static int kaweth_control(struct kaweth_device *kaweth, + unsigned int pipe, + __u8 request, + __u8 requesttype, + __u16 value, + __u16 index, + void *data, + __u16 size, + int timeout) +{ + devrequest *dr; + + kaweth_dbg("kaweth_control()"); + + if(in_interrupt()) { + kaweth_dbg("in_interrupt()"); + return -EBUSY; + } + + dr = kmalloc(sizeof(devrequest), + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + + if(!dr) + { + kaweth_dbg("kmalloc() failed"); + return -ENOMEM; + } + + dr->requesttype = requesttype; + dr->request = request; + dr->value = value; + dr->index = index; + dr->length = size; + + return kaweth_internal_control_msg(kaweth->dev, + pipe, + dr, + data, + size, + timeout); +} + +/**************************************************************** + * kaweth_read_configuration + ****************************************************************/ +static int kaweth_read_configuration(struct kaweth_device *kaweth) +{ + int retval; + + kaweth_dbg("Reading kaweth configuration"); + + retval = kaweth_control(kaweth, + usb_rcvctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_GET_ETHERNET_DESC, + USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, + 0, + 0, + (void *)&kaweth->configuration, + sizeof(kaweth->configuration), + KAWETH_CONTROL_TIMEOUT); + + return retval; +} + +/**************************************************************** + * kaweth_set_urb_size + ****************************************************************/ +static int kaweth_set_urb_size(struct kaweth_device *kaweth, __u16 urb_size) +{ + int retval; + + kaweth_dbg("Setting URB size to %d", (unsigned)urb_size); + + retval = kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SET_URB_SIZE, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + urb_size, + 0, + (void *)&kaweth->firmware_buf, + 0, + KAWETH_CONTROL_TIMEOUT); + + return retval; +} + +/**************************************************************** + * kaweth_set_sofs_wait + ****************************************************************/ +static int kaweth_set_sofs_wait(struct kaweth_device *kaweth, __u16 sofs_wait) +{ + int retval; + + kaweth_dbg("Set SOFS wait to %d", (unsigned)sofs_wait); + + retval = kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SET_SOFS_WAIT, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + sofs_wait, + 0, + (void *)&kaweth->firmware_buf, + 0, + KAWETH_CONTROL_TIMEOUT); + + return retval; +} + +/**************************************************************** + * kaweth_set_receive_filter + ****************************************************************/ +static int kaweth_set_receive_filter(struct kaweth_device *kaweth, + __u16 receive_filter) +{ + int retval; + + kaweth_dbg("Set receive filter to %d", (unsigned)receive_filter); + + retval = kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SET_PACKET_FILTER, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + receive_filter, + 0, + (void *)&kaweth->firmware_buf, + 0, + KAWETH_CONTROL_TIMEOUT); + + return retval; +} + +/**************************************************************** + * kaweth_download_firmware + ****************************************************************/ +static int kaweth_download_firmware(struct kaweth_device *kaweth, + __u8 *data, + __u16 data_len, + __u8 interrupt, + __u8 type) +{ + if(data_len > KAWETH_FIRMWARE_BUF_SIZE) { + kaweth_err("Firmware too big: %d", data_len); + return -ENOSPC; + } + + memcpy(kaweth->firmware_buf, data, data_len); + + kaweth->firmware_buf[2] = (data_len & 0xFF) - 7; + kaweth->firmware_buf[3] = data_len >> 8; + kaweth->firmware_buf[4] = type; + kaweth->firmware_buf[5] = interrupt; + + kaweth_dbg("Downloading firmware at %x to kaweth device at %x", + (int)data, + (int)kaweth); + kaweth_dbg("Firmware length: %d", data_len); + + return kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SCAN, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + 0, + 0, + (void *)&kaweth->firmware_buf, + data_len, + KAWETH_CONTROL_TIMEOUT); +} + +/**************************************************************** + * kaweth_trigger_firmware + ****************************************************************/ +static int kaweth_trigger_firmware(struct kaweth_device *kaweth, + __u8 interrupt) +{ + kaweth->firmware_buf[0] = 0xB6; + kaweth->firmware_buf[1] = 0xC3; + kaweth->firmware_buf[2] = 0x01; + kaweth->firmware_buf[3] = 0x00; + kaweth->firmware_buf[4] = 0x06; + kaweth->firmware_buf[5] = interrupt; + kaweth->firmware_buf[6] = 0x00; + kaweth->firmware_buf[7] = 0x00; + + kaweth_dbg("Triggering firmware"); + + return kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SCAN, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + 0, + 0, + (void *)&kaweth->firmware_buf, + 8, + KAWETH_CONTROL_TIMEOUT); +} + +/**************************************************************** + * kaweth_reset + ****************************************************************/ +static int kaweth_reset(struct kaweth_device *kaweth) +{ + int result; + + kaweth_dbg("kaweth_reset(%p)", kaweth); + result = kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + USB_REQ_SET_CONFIGURATION, + 0, + kaweth->dev->config[0].bConfigurationValue, + 0, + NULL, + 0, + KAWETH_CONTROL_TIMEOUT); + + udelay(10000); + + kaweth_dbg("kaweth_reset() returns %d.",result); + + return result; +} + +static void kaweth_usb_receive(struct urb *); + +/**************************************************************** + * kaweth_resubmit_rx_urb + ****************************************************************/ +static inline void kaweth_resubmit_rx_urb(struct kaweth_device *kaweth) +{ + int result; + + memset(kaweth->rx_urb, 0, sizeof(*kaweth->rx_urb)); + + FILL_BULK_URB(kaweth->rx_urb, + kaweth->dev, + usb_rcvbulkpipe(kaweth->dev, 1), + kaweth->rx_buf, + KAWETH_BUF_SIZE, + kaweth_usb_receive, + kaweth); + + if((result = usb_submit_urb(kaweth->rx_urb))) { + kaweth_err("resubmitting rx_urb %d failed", result); + } +} + +static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth); + +/**************************************************************** + * kaweth_usb_receive + ****************************************************************/ +static void kaweth_usb_receive(struct urb *urb) +{ + struct kaweth_device *kaweth = urb->context; + struct net_device *net = kaweth->net; + + int count = urb->actual_length; + int count2 = urb->transfer_buffer_length; + + __u16 pkt_len = *(__u16 *)kaweth->rx_buf; + + struct sk_buff *skb; + + if(kaweth->status & KAWETH_STATUS_CLOSING) { + return; + } + + if(urb->status && urb->status != -EREMOTEIO && count != 1) { + kaweth_err("%s RX status: %d count: %d packet_len: %d", + net->name, + urb->status, + count, + (int)pkt_len); + kaweth_resubmit_rx_urb(kaweth); + return; + } + + if(kaweth->net && (count > 2)) { + if(pkt_len > (count - 2)) { + kaweth_err("Packet length too long for USB frame (pkt_len: %x, count: %x)",pkt_len, count); + kaweth_err("Packet len & 2047: %x", pkt_len & 2047); + kaweth_err("Count 2: %x", count2); + kaweth_resubmit_rx_urb(kaweth); + return; + } + + if(!(skb = dev_alloc_skb(pkt_len+2))) { + kaweth_resubmit_rx_urb(kaweth); + return; + } + + skb->dev = net; + + eth_copy_and_sum(skb, kaweth->rx_buf + 2, pkt_len, 0); + + skb_put(skb, pkt_len); + + skb->protocol = eth_type_trans(skb, net); + + netif_rx(skb); + + kaweth->stats.rx_packets++; + kaweth->stats.rx_bytes += pkt_len; + } + + kaweth_resubmit_rx_urb(kaweth); +} + +/**************************************************************** + * kaweth_open + ****************************************************************/ +static int kaweth_open(struct net_device *net) +{ + struct kaweth_device *kaweth = (struct kaweth_device *)net->priv; + + kaweth_dbg("Dev usage: %d", kaweth->dev->refcnt.counter); + + kaweth_dbg("Opening network device."); + + kaweth_resubmit_rx_urb(kaweth); + + netif_start_queue(net); + + MOD_INC_USE_COUNT; + + kaweth_async_set_rx_mode(kaweth); + return 0; +} + +/**************************************************************** + * kaweth_close + ****************************************************************/ +static int kaweth_close(struct net_device *net) +{ + struct kaweth_device *kaweth = net->priv; + + netif_stop_queue(net); + + kaweth->status |= KAWETH_STATUS_CLOSING; + + usb_unlink_urb(kaweth->rx_urb); + + kaweth->status &= ~KAWETH_STATUS_CLOSING; + + MOD_DEC_USE_COUNT; + + printk("Dev usage: %d", kaweth->dev->refcnt.counter); + + return 0; +} + +/**************************************************************** + * kaweth_ioctl + ****************************************************************/ +static int kaweth_ioctl(struct net_device *net, struct ifreq *rq, int cmd) +{ + return -EOPNOTSUPP; +} + +/**************************************************************** + * kaweth_usb_transmit_complete + ****************************************************************/ +static void kaweth_usb_transmit_complete(struct urb *urb) +{ + struct kaweth_device *kaweth = urb->context; + + spin_lock(&kaweth->device_lock); + + if (urb->status) + kaweth_dbg("%s: TX status %d.", kaweth->net->name, urb->status); + + netif_wake_queue(kaweth->net); + + spin_unlock(&kaweth->device_lock); +} + +/**************************************************************** + * kaweth_start_xmit + ****************************************************************/ +static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net) +{ + struct kaweth_device *kaweth = net->priv; + int count = skb->len; + + int res; + + spin_lock(&kaweth->device_lock); + + kaweth_async_set_rx_mode(kaweth); + netif_stop_queue(net); + + *((__u16 *)kaweth->tx_buf) = skb->len; + + memcpy(kaweth->tx_buf + 2, skb->data, skb->len); + + memset(kaweth->tx_urb, 0, sizeof(*kaweth->tx_urb)); + + FILL_BULK_URB(kaweth->tx_urb, + kaweth->dev, + usb_sndbulkpipe(kaweth->dev, 2), + kaweth->tx_buf, + count + 2, + kaweth_usb_transmit_complete, + kaweth); + + if((res = usb_submit_urb(kaweth->tx_urb))) + { + kaweth_warn("kaweth failed tx_urb %d", res); + kaweth->stats.tx_errors++; + + netif_start_queue(net); + } + else + { + kaweth->stats.tx_packets++; + kaweth->stats.tx_bytes += skb->len; + net->trans_start = jiffies; + } + + dev_kfree_skb(skb); + + spin_unlock(&kaweth->device_lock); + + return 0; +} + +/**************************************************************** + * kaweth_set_rx_mode + ****************************************************************/ +static void kaweth_set_rx_mode(struct net_device *net) +{ + struct kaweth_device *kaweth = net->priv; + + __u16 packet_filter_bitmap = KAWETH_PACKET_FILTER_DIRECTED | + KAWETH_PACKET_FILTER_BROADCAST | + KAWETH_PACKET_FILTER_MULTICAST; + + kaweth_dbg("Setting Rx mode to %d", packet_filter_bitmap); + + netif_stop_queue(net); + + if (net->flags & IFF_PROMISC) { + packet_filter_bitmap |= KAWETH_PACKET_FILTER_PROMISCUOUS; + } + else if ((net->mc_count) || (net->flags & IFF_ALLMULTI)) { + packet_filter_bitmap |= KAWETH_PACKET_FILTER_ALL_MULTICAST; + } + + kaweth->packet_filter_bitmap = packet_filter_bitmap; + netif_wake_queue(net); +} + +/**************************************************************** + * kaweth_async_set_rx_mode + ****************************************************************/ +static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth) +{ + __u16 packet_filter_bitmap = kaweth->packet_filter_bitmap; + kaweth->packet_filter_bitmap = 0; + if(packet_filter_bitmap == 0) return; + + { + int result; + result = kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SET_PACKET_FILTER, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + packet_filter_bitmap, + 0, + (void *)&kaweth->firmware_buf, + 0, + KAWETH_CONTROL_TIMEOUT); + + if(result < 0) { + kaweth_err("Failed to set Rx mode: %d", result); + } + else { + kaweth_dbg("Set Rx mode to %d", packet_filter_bitmap); + } + } +} + +/**************************************************************** + * kaweth_netdev_stats + ****************************************************************/ +static struct net_device_stats *kaweth_netdev_stats(struct net_device *dev) +{ + return &((struct kaweth_device *)dev->priv)->stats; +} + +/**************************************************************** + * kaweth_tx_timeout + ****************************************************************/ +static void kaweth_tx_timeout(struct net_device *net) +{ + struct kaweth_device *kaweth = net->priv; + + kaweth_warn("%s: Tx timed out. Resetting.", net->name); + kaweth->stats.tx_errors++; + net->trans_start = jiffies; + + usb_unlink_urb(kaweth->tx_urb); + + netif_wake_queue(net); +} + +/**************************************************************** + * kaweth_probe + ****************************************************************/ +static void *kaweth_probe( + struct usb_device *dev, /* the device */ + unsigned ifnum, /* what interface */ + const struct usb_device_id *id /* from id_table */ + ) +{ + struct kaweth_device *kaweth; + const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + int result = 0; + + kaweth_dbg("Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x", + dev->devnum, + (int)dev->descriptor.idVendor, + (int)dev->descriptor.idProduct, + (int)dev->descriptor.bcdDevice); + + kaweth_dbg("Device at %p", dev); + + kaweth_dbg("Descriptor length: %x type: %x", + (int)dev->descriptor.bLength, + (int)dev->descriptor.bDescriptorType); + + if(!(kaweth = kmalloc(sizeof(struct kaweth_device), GFP_KERNEL))) { + kaweth_dbg("out of memory allocating device structure\n"); + return NULL; + } + + memset(kaweth, 0, sizeof(struct kaweth_device)); + + kaweth->dev = dev; + kaweth->status = 0; + kaweth->net = NULL; + kaweth->device_lock = SPIN_LOCK_UNLOCKED; + + kaweth_dbg("Resetting."); + + kaweth_reset(kaweth); + + /* + * If high byte of bcdDevice is nonzero, firmware is already + * downloaded. Don't try to do it again, or we'll hang the device. + */ + + if (dev->descriptor.bcdDevice >> 8) { + kaweth_info("Firmware present in device."); + } else { + /* Download the firmware */ + kaweth_info("Downloading firmware..."); + if ((result = kaweth_download_firmware(kaweth, + kaweth_new_code, + len_kaweth_new_code, + 100, + 2)) < 0) { + kaweth_err("Error downloading firmware (%d)", result); + kfree(kaweth); + return NULL; + } + + if ((result = kaweth_download_firmware(kaweth, + kaweth_new_code_fix, + len_kaweth_new_code_fix, + 100, + 3)) < 0) { + kaweth_err("Error downloading firmware fix (%d)", result); + kfree(kaweth); + return NULL; + } + + if ((result = kaweth_download_firmware(kaweth, + kaweth_trigger_code, + len_kaweth_trigger_code, + 126, + 2)) < 0) { + kaweth_err("Error downloading trigger code (%d)", result); + kfree(kaweth); + return NULL; + } + + if ((result = kaweth_download_firmware(kaweth, + kaweth_trigger_code_fix, + len_kaweth_trigger_code_fix, + 126, + 3)) < 0) { + kaweth_err("Error downloading trigger code fix (%d)", result); + kfree(kaweth); + return NULL; + } + + + if ((result = kaweth_trigger_firmware(kaweth, 126)) < 0) { + kaweth_err("Error triggering firmware (%d)", result); + kfree(kaweth); + return NULL; + } + + /* Device will now disappear for a moment... */ + kaweth_info("Firmware loaded. I'll be back..."); + return NULL; + } + + result = kaweth_read_configuration(kaweth); + + if(result < 0) { + kaweth_err("Error reading configuration (%d), no net device created", result); + kfree(kaweth); + return NULL; + } + + kaweth_info("Statistics collection: %x", kaweth->configuration.statistics_mask); + kaweth_info("Multicast filter limit: %x", kaweth->configuration.max_multicast_filters & ((1 << 15) - 1)); + kaweth_info("MTU: %d", kaweth->configuration.segment_size); + kaweth_info("Read MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", + (int)kaweth->configuration.hw_addr[0], + (int)kaweth->configuration.hw_addr[1], + (int)kaweth->configuration.hw_addr[2], + (int)kaweth->configuration.hw_addr[3], + (int)kaweth->configuration.hw_addr[4], + (int)kaweth->configuration.hw_addr[5]); + + if(!memcmp(&kaweth->configuration.hw_addr, + &bcast_addr, + sizeof(bcast_addr))) { + kaweth_err("Firmware not functioning properly, no net device created"); + kfree(kaweth); + return NULL; + } + + if(kaweth_set_urb_size(kaweth, KAWETH_BUF_SIZE) < 0) { + kaweth_dbg("Error setting URB size"); + return kaweth; + } + + if(kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT) < 0) { + kaweth_err("Error setting SOFS wait"); + return kaweth; + } + + result = kaweth_set_receive_filter(kaweth, + KAWETH_PACKET_FILTER_DIRECTED | + KAWETH_PACKET_FILTER_BROADCAST | + KAWETH_PACKET_FILTER_MULTICAST); + + if(result < 0) { + kaweth_err("Error setting receive filter"); + return kaweth; + } + + kaweth_dbg("Initializing net device."); + + kaweth->tx_urb = usb_alloc_urb(0); + kaweth->rx_urb = usb_alloc_urb(0); + + kaweth->net = init_etherdev(0, 0); + + memcpy(kaweth->net->broadcast, &bcast_addr, sizeof(bcast_addr)); + memcpy(kaweth->net->dev_addr, + &kaweth->configuration.hw_addr, + sizeof(kaweth->configuration.hw_addr)); + + kaweth->net->priv = kaweth; + kaweth->net->open = kaweth_open; + kaweth->net->stop = kaweth_close; + + kaweth->net->watchdog_timeo = KAWETH_TX_TIMEOUT; + kaweth->net->tx_timeout = kaweth_tx_timeout; + + kaweth->net->do_ioctl = kaweth_ioctl; + kaweth->net->hard_start_xmit = kaweth_start_xmit; + kaweth->net->set_multicast_list = kaweth_set_rx_mode; + kaweth->net->get_stats = kaweth_netdev_stats; + kaweth->net->mtu = kaweth->configuration.segment_size; + + memset(&kaweth->stats, 0, sizeof(kaweth->stats)); + + kaweth_info("kaweth interface created at %s", kaweth->net->name); + + kaweth_dbg("Kaweth probe returning."); + + return kaweth; +} + +/**************************************************************** + * kaweth_disconnect + ****************************************************************/ +static void kaweth_disconnect(struct usb_device *dev, void *ptr) +{ + struct kaweth_device *kaweth = ptr; + + kaweth_info("Unregistering"); + + if (!kaweth) { + kaweth_warn("unregistering non-existant device"); + return; + } + + if(kaweth->net) { + if(kaweth->net->flags & IFF_UP) { + kaweth_dbg("Closing net device"); + dev_close(kaweth->net); + } + + kaweth_dbg("Unregistering net device"); + unregister_netdev(kaweth->net); + } + + usb_free_urb(kaweth->rx_urb); + usb_free_urb(kaweth->tx_urb); + + kfree(kaweth); +} + + +/*-------------------------------------------------------------------* + * 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); + } + +} + +/*-------------------------------------------------------------------* + * COMPATIBILITY STUFF * + *-------------------------------------------------------------------*/ + +// Starts urb and waits for completion or timeout +static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length) +{ + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data awd; + int status; + + awd.wakeup = &wqh; + init_waitqueue_head(&wqh); + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&wqh, &wait); + urb->context = &awd; + status = usb_submit_urb(urb); + if (status) { + // something went wrong + usb_free_urb(urb); + current->state = TASK_RUNNING; + remove_wait_queue(&wqh, &wait); + return status; + } + + if (urb->status == -EINPROGRESS) { + while (timeout && urb->status == -EINPROGRESS) + status = timeout = schedule_timeout(timeout); + } + else { + status = 1; + } + + current->state = TASK_RUNNING; + remove_wait_queue(&wqh, &wait); + + if (!status) { + // timeout + kaweth_warn("usb_control/bulk_msg: timeout"); + usb_unlink_urb(urb); // remove urb safely + status = -ETIMEDOUT; + } + else { + status = urb->status; + } + + if (actual_length) { + *actual_length = urb->actual_length; + } + + usb_free_urb(urb); + return status; +} + +/*-------------------------------------------------------------------*/ +// returns status (negative) or length (positive) +int kaweth_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, + devrequest *cmd, void *data, int len, int timeout) +{ + urb_t *urb; + int retv; + int length; + + urb = usb_alloc_urb(0); + if (!urb) + return -ENOMEM; + + FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, + len, (usb_complete_t)usb_api_blocking_completion,0); + + retv = usb_start_wait_urb(urb, timeout, &length); + if (retv < 0) { + return retv; + } + else { + return length; + } +} + + +/**************************************************************** + * kaweth_init + ****************************************************************/ +int __init kaweth_init(void) +{ + kaweth_dbg("Driver loading"); + return usb_register(&kaweth_driver); +} + +/**************************************************************** + * kaweth_exit + ****************************************************************/ +void __exit kaweth_exit(void) +{ + usb_deregister(&kaweth_driver); +} + +module_init(kaweth_init); +module_exit(kaweth_exit); + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/kawethfw.h linux.ac/drivers/usb/kawethfw.h --- linux.vanilla/drivers/usb/kawethfw.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/kawethfw.h Sun Apr 22 01:58:23 2001 @@ -0,0 +1,557 @@ +/******************************************/ +/* NOTE: B6/C3 is data header signature */ +/* 0xAA/0xBB is data length = total */ +/* bytes - 7, 0xCC is type, 0xDD is */ +/* interrupt to use. */ +/******************************************/ + +/**************************************************************** + * kaweth_trigger_code + ****************************************************************/ +static __u8 kaweth_trigger_code[] = +{ + 0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD, + 0xc8, 0x07, 0xa0, 0x00, 0xf0, 0x07, 0x5e, 0x00, + 0x06, 0x00, 0xf0, 0x07, 0x0a, 0x00, 0x08, 0x00, + 0xf0, 0x09, 0x00, 0x00, 0x02, 0x00, 0xe7, 0x07, + 0x36, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, + 0x04, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x10, 0xc0, + 0xf0, 0x09, 0x0e, 0xc0, 0x00, 0x00, 0xe7, 0x87, + 0x01, 0x00, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x07, 0xa0, 0x00, + 0xe7, 0x17, 0x50, 0xc3, 0x10, 0xc0, 0x30, 0xd8, + 0x04, 0x00, 0x30, 0x5c, 0x08, 0x00, 0x04, 0x00, + 0xb0, 0xc0, 0x06, 0x00, 0xc8, 0x05, 0xe7, 0x05, + 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0x49, 0xaf, + 0xc0, 0x07, 0x00, 0x00, 0x60, 0xaf, 0x4a, 0xaf, + 0x00, 0x0c, 0x0c, 0x00, 0x40, 0xd2, 0x00, 0x1c, + 0x0c, 0x00, 0x40, 0xd2, 0x30, 0x00, 0x08, 0x00, + 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, 0xf0, 0x07, + 0x86, 0x00, 0x06, 0x00, 0x67, 0xcf, 0x27, 0x0c, + 0x02, 0x00, 0x00, 0x00, 0x27, 0x0c, 0x00, 0x00, + 0x0e, 0xc0, 0x49, 0xaf, 0x64, 0xaf, 0xc0, 0x07, + 0x00, 0x00, 0x4b, 0xaf, 0x4a, 0xaf, 0x5a, 0xcf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, + 0x00, 0x00 +}; +/**************************************************************** + * kaweth_trigger_code_fix + ****************************************************************/ +static __u8 kaweth_trigger_code_fix[] = +{ + 0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD, + 0x02, 0x00, 0x06, 0x00, 0x18, 0x00, 0x3e, 0x00, + 0x80, 0x00, 0x98, 0x00, 0xaa, 0x00, + 0x00, 0x00 +}; + +/**************************************************************** + * kaweth_new_code + ****************************************************************/ +static __u8 kaweth_new_code[] = +{ + 0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD, + 0x9f, 0xcf, 0xde, 0x06, 0xe7, 0x57, 0x00, 0x00, + 0xc4, 0x06, 0x97, 0xc1, 0xe7, 0x67, 0xff, 0x1f, + 0x28, 0xc0, 0xe7, 0x87, 0x00, 0x04, 0x24, 0xc0, + 0xe7, 0x67, 0xff, 0xf9, 0x22, 0xc0, 0x97, 0xcf, + 0xd7, 0x09, 0x00, 0xc0, 0xe7, 0x09, 0xa2, 0xc0, + 0xbe, 0x06, 0x9f, 0xaf, 0x36, 0x00, 0xe7, 0x05, + 0x00, 0xc0, 0xa7, 0xcf, 0xbc, 0x06, 0x97, 0xcf, + 0xe7, 0x57, 0x00, 0x00, 0xb8, 0x06, 0xa7, 0xa1, + 0xb8, 0x06, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, + 0x14, 0x08, 0x0a, 0xc0, 0xe7, 0x57, 0x00, 0x00, + 0xa4, 0xc0, 0xa7, 0xc0, 0x7a, 0x06, 0x9f, 0xaf, + 0x92, 0x07, 0xe7, 0x07, 0x00, 0x00, 0x14, 0x08, + 0xe7, 0x57, 0xff, 0xff, 0xba, 0x06, 0x9f, 0xa0, + 0x38, 0x00, 0xe7, 0x59, 0xba, 0x06, 0xbe, 0x06, + 0x9f, 0xa0, 0x38, 0x00, 0xc8, 0x09, 0xca, 0x06, + 0x08, 0x62, 0x9f, 0xa1, 0x36, 0x08, 0xc0, 0x09, + 0x76, 0x06, 0x00, 0x60, 0xa7, 0xc0, 0x7a, 0x06, + 0x9f, 0xaf, 0xcc, 0x02, 0xe7, 0x57, 0x00, 0x00, + 0xb8, 0x06, 0xa7, 0xc1, 0x7a, 0x06, 0x9f, 0xaf, + 0x04, 0x00, 0xe7, 0x57, 0x00, 0x00, 0x8e, 0x06, + 0x0a, 0xc1, 0xe7, 0x09, 0x20, 0xc0, 0x10, 0x08, + 0xe7, 0xd0, 0x10, 0x08, 0xe7, 0x67, 0x40, 0x00, + 0x10, 0x08, 0x9f, 0xaf, 0x92, 0x0c, 0xc0, 0x09, + 0xd0, 0x06, 0x00, 0x60, 0x05, 0xc4, 0xc0, 0x59, + 0xbe, 0x06, 0x02, 0xc0, 0x9f, 0xaf, 0xec, 0x00, + 0x9f, 0xaf, 0x34, 0x02, 0xe7, 0x57, 0x00, 0x00, + 0xa6, 0x06, 0x9f, 0xa0, 0x7a, 0x02, 0xa7, 0xcf, + 0x7a, 0x06, 0x48, 0x02, 0xe7, 0x09, 0xbe, 0x06, + 0xd0, 0x06, 0xc8, 0x37, 0x04, 0x00, 0x9f, 0xaf, + 0x08, 0x03, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, + 0xce, 0x06, 0x97, 0xc0, 0xd7, 0x09, 0x00, 0xc0, + 0xc1, 0xdf, 0xc8, 0x09, 0xc6, 0x06, 0x08, 0x62, + 0x14, 0xc0, 0x27, 0x04, 0xc6, 0x06, 0x10, 0x94, + 0xf0, 0x07, 0x10, 0x08, 0x02, 0x00, 0xc1, 0x07, + 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0xf0, 0x07, + 0x30, 0x01, 0x06, 0x00, 0x50, 0xaf, 0xe7, 0x07, + 0xff, 0xff, 0xd0, 0x06, 0xe7, 0x07, 0x00, 0x00, + 0xce, 0x06, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x48, 0x02, + 0xd0, 0x09, 0xc6, 0x06, 0x27, 0x02, 0xc6, 0x06, + 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0x48, 0x02, + 0xc8, 0x37, 0x04, 0x00, 0x00, 0x0c, 0x0c, 0x00, + 0x00, 0x60, 0x21, 0xc0, 0xc0, 0x37, 0x3e, 0x00, + 0x23, 0xc9, 0xc0, 0x57, 0xb4, 0x05, 0x1b, 0xc8, + 0xc0, 0x17, 0x3f, 0x00, 0xc0, 0x67, 0xc0, 0xff, + 0x30, 0x00, 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x02, 0xc0, 0x17, 0x4c, 0x00, + 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07, 0xa0, 0x01, + 0x0a, 0x00, 0x48, 0x02, 0xc1, 0x07, 0x02, 0x00, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x51, 0xaf, + 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf, + 0x08, 0x03, 0x9f, 0xaf, 0x7a, 0x02, 0x97, 0xcf, + 0x9f, 0xaf, 0x7a, 0x02, 0xc9, 0x37, 0x04, 0x00, + 0xc1, 0xdf, 0xc8, 0x09, 0xa2, 0x06, 0x50, 0x02, + 0x67, 0x02, 0xa2, 0x06, 0xd1, 0x07, 0x00, 0x00, + 0x27, 0xd8, 0xaa, 0x06, 0xc0, 0xdf, 0x9f, 0xaf, + 0xc4, 0x01, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, + 0xd2, 0x06, 0x97, 0xc1, 0xe7, 0x57, 0x01, 0x00, + 0xa8, 0x06, 0x97, 0xc0, 0xc8, 0x09, 0xa0, 0x06, + 0x08, 0x62, 0x97, 0xc0, 0x00, 0x02, 0xc0, 0x17, + 0x0e, 0x00, 0x27, 0x00, 0x34, 0x01, 0x27, 0x0c, + 0x0c, 0x00, 0x36, 0x01, 0xe7, 0x07, 0x50, 0xc3, + 0x12, 0xc0, 0xe7, 0x07, 0xcc, 0x0b, 0x02, 0x00, + 0xe7, 0x07, 0x01, 0x00, 0xa8, 0x06, 0xe7, 0x07, + 0x05, 0x00, 0x90, 0xc0, 0x97, 0xcf, 0xc8, 0x09, + 0xa4, 0x06, 0x08, 0x62, 0x02, 0xc0, 0x10, 0x64, + 0x07, 0xc1, 0xe7, 0x07, 0x00, 0x00, 0x9e, 0x06, + 0xe7, 0x07, 0x72, 0x04, 0x24, 0x00, 0x97, 0xcf, + 0x27, 0x04, 0xa4, 0x06, 0xc8, 0x17, 0x0e, 0x00, + 0x27, 0x02, 0x9e, 0x06, 0xe7, 0x07, 0x80, 0x04, + 0x24, 0x00, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, + 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00, 0x90, 0x06, + 0x13, 0xc1, 0x9f, 0xaf, 0x06, 0x02, 0xe7, 0x57, + 0x00, 0x00, 0x9e, 0x06, 0x13, 0xc0, 0xe7, 0x09, + 0x9e, 0x06, 0x30, 0x01, 0xe7, 0x07, 0xf2, 0x05, + 0x32, 0x01, 0xe7, 0x07, 0x10, 0x00, 0x96, 0xc0, + 0xe7, 0x09, 0x9e, 0x06, 0x90, 0x06, 0x04, 0xcf, + 0xe7, 0x57, 0x00, 0x00, 0x9e, 0x06, 0x02, 0xc1, + 0x9f, 0xaf, 0x06, 0x02, 0xe7, 0x05, 0x00, 0xc0, + 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, + 0x08, 0x92, 0xe7, 0x57, 0x02, 0x00, 0xaa, 0x06, + 0x02, 0xc3, 0xc8, 0x09, 0xa4, 0x06, 0x27, 0x02, + 0xa6, 0x06, 0x08, 0x62, 0x03, 0xc1, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0x27, 0x04, 0xa4, 0x06, + 0xe7, 0x05, 0x00, 0xc0, 0xf0, 0x07, 0x40, 0x00, + 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00, + 0x06, 0x00, 0xf0, 0x07, 0x46, 0x01, 0x0a, 0x00, + 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x02, 0x00, + 0x51, 0xaf, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, + 0x96, 0x06, 0x97, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, + 0x96, 0x06, 0x27, 0x04, 0x96, 0x06, 0x27, 0x52, + 0x98, 0x06, 0x03, 0xc1, 0xe7, 0x07, 0x96, 0x06, + 0x98, 0x06, 0xc0, 0xdf, 0x17, 0x02, 0xc8, 0x17, + 0x0e, 0x00, 0x9f, 0xaf, 0xba, 0x03, 0xc8, 0x05, + 0x00, 0x60, 0x03, 0xc0, 0x9f, 0xaf, 0x24, 0x03, + 0x97, 0xcf, 0x9f, 0xaf, 0x08, 0x03, 0x97, 0xcf, + 0x57, 0x02, 0xc9, 0x07, 0xa4, 0x06, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0x08, 0x62, 0x1b, 0xc0, + 0x50, 0x04, 0x11, 0x02, 0xe7, 0x05, 0x00, 0xc0, + 0xc9, 0x05, 0x97, 0xcf, 0x97, 0x02, 0xca, 0x09, + 0xd6, 0x06, 0xf2, 0x17, 0x01, 0x00, 0x04, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x06, 0x00, 0xca, 0x17, + 0x2c, 0x00, 0xf8, 0x77, 0x01, 0x00, 0x0e, 0x00, + 0x06, 0xc0, 0xca, 0xd9, 0xf8, 0x57, 0xff, 0x00, + 0x0e, 0x00, 0x01, 0xc1, 0xca, 0xd9, 0x22, 0x1c, + 0x0c, 0x00, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17, + 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0xca, 0x05, + 0x00, 0x0c, 0x0c, 0x00, 0xc0, 0x17, 0x41, 0x00, + 0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00, 0x08, 0x00, + 0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00, + 0x06, 0x00, 0xf0, 0x07, 0xda, 0x00, 0x0a, 0x00, + 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0c, + 0x08, 0x00, 0x40, 0xd1, 0x01, 0x00, 0xc0, 0x19, + 0xce, 0x06, 0xc0, 0x59, 0xc2, 0x06, 0x04, 0xc9, + 0x49, 0xaf, 0x9f, 0xaf, 0xec, 0x00, 0x4a, 0xaf, + 0x67, 0x10, 0xce, 0x06, 0xc8, 0x17, 0x04, 0x00, + 0xc1, 0x07, 0x01, 0x00, 0xd7, 0x09, 0x00, 0xc0, + 0xc1, 0xdf, 0x50, 0xaf, 0xe7, 0x05, 0x00, 0xc0, + 0x97, 0xcf, 0xc0, 0x07, 0x01, 0x00, 0xc1, 0x09, + 0xac, 0x06, 0xc1, 0x77, 0x01, 0x00, 0x97, 0xc1, + 0xd8, 0x77, 0x01, 0x00, 0x12, 0xc0, 0xc9, 0x07, + 0x6a, 0x06, 0x9f, 0xaf, 0x08, 0x04, 0x04, 0xc1, + 0xc1, 0x77, 0x08, 0x00, 0x13, 0xc0, 0x97, 0xcf, + 0xc1, 0x77, 0x02, 0x00, 0x97, 0xc1, 0xc1, 0x77, + 0x10, 0x00, 0x0c, 0xc0, 0x9f, 0xaf, 0x2c, 0x04, + 0x97, 0xcf, 0xc1, 0x77, 0x04, 0x00, 0x06, 0xc0, + 0xc9, 0x07, 0x70, 0x06, 0x9f, 0xaf, 0x08, 0x04, + 0x97, 0xc0, 0x00, 0xcf, 0x00, 0x90, 0x97, 0xcf, + 0x50, 0x54, 0x97, 0xc1, 0x70, 0x5c, 0x02, 0x00, + 0x02, 0x00, 0x97, 0xc1, 0x70, 0x5c, 0x04, 0x00, + 0x04, 0x00, 0x97, 0xcf, 0x80, 0x01, 0xc0, 0x00, + 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x0c, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xcb, 0x09, 0xb2, 0x06, + 0xcc, 0x09, 0xb4, 0x06, 0x0b, 0x53, 0x11, 0xc0, + 0xc9, 0x02, 0xca, 0x07, 0x1c, 0x04, 0x9f, 0xaf, + 0x08, 0x04, 0x97, 0xc0, 0x0a, 0xc8, 0x82, 0x08, + 0x0a, 0xcf, 0x82, 0x08, 0x9f, 0xaf, 0x08, 0x04, + 0x97, 0xc0, 0x05, 0xc2, 0x89, 0x30, 0x82, 0x60, + 0x78, 0xc1, 0x00, 0x90, 0x97, 0xcf, 0x89, 0x10, + 0x09, 0x53, 0x79, 0xc2, 0x89, 0x30, 0x82, 0x08, + 0x7a, 0xcf, 0xc0, 0xdf, 0x97, 0xcf, 0xc0, 0xdf, + 0x97, 0xcf, 0xe7, 0x09, 0x96, 0xc0, 0x92, 0x06, + 0xe7, 0x09, 0x98, 0xc0, 0x94, 0x06, 0x0f, 0xcf, + 0xe7, 0x09, 0x96, 0xc0, 0x92, 0x06, 0xe7, 0x09, + 0x98, 0xc0, 0x94, 0x06, 0xe7, 0x09, 0x9e, 0x06, + 0x30, 0x01, 0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01, + 0xe7, 0x07, 0x10, 0x00, 0x96, 0xc0, 0xd7, 0x09, + 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0x90, 0x06, + 0xc8, 0x37, 0x0e, 0x00, 0xe7, 0x77, 0x2a, 0x00, + 0x92, 0x06, 0x30, 0xc0, 0x97, 0x02, 0xca, 0x09, + 0xd6, 0x06, 0xe7, 0x77, 0x20, 0x00, 0x92, 0x06, + 0x0e, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x10, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x12, 0x00, 0xe7, 0x77, + 0x0a, 0x00, 0x92, 0x06, 0xca, 0x05, 0x1e, 0xc0, + 0x97, 0x02, 0xca, 0x09, 0xd6, 0x06, 0xf2, 0x17, + 0x01, 0x00, 0x0c, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x0e, 0x00, 0xe7, 0x77, 0x02, 0x00, 0x92, 0x06, + 0x07, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x44, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x46, 0x00, 0x06, 0xcf, + 0xf2, 0x17, 0x01, 0x00, 0x60, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x62, 0x00, 0xca, 0x05, 0x9f, 0xaf, + 0x08, 0x03, 0x0f, 0xcf, 0x57, 0x02, 0x09, 0x02, + 0xf1, 0x09, 0x94, 0x06, 0x0c, 0x00, 0xf1, 0xda, + 0x0c, 0x00, 0xc8, 0x09, 0x98, 0x06, 0x50, 0x02, + 0x67, 0x02, 0x98, 0x06, 0xd1, 0x07, 0x00, 0x00, + 0xc9, 0x05, 0xe7, 0x09, 0x9e, 0x06, 0x90, 0x06, + 0xe7, 0x57, 0x00, 0x00, 0x90, 0x06, 0x02, 0xc0, + 0x9f, 0xaf, 0x06, 0x02, 0xc8, 0x05, 0xe7, 0x05, + 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0x17, 0x00, 0x17, 0x02, 0x97, 0x02, + 0xc0, 0x09, 0x92, 0xc0, 0xe7, 0x07, 0x04, 0x00, + 0x90, 0xc0, 0xca, 0x09, 0xd6, 0x06, 0xe7, 0x07, + 0x00, 0x00, 0xa8, 0x06, 0xe7, 0x07, 0x6a, 0x04, + 0x02, 0x00, 0xc0, 0x77, 0x02, 0x00, 0x08, 0xc0, + 0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x52, 0x00, 0x9f, 0xcf, 0x24, 0x06, + 0xc0, 0x77, 0x10, 0x00, 0x06, 0xc0, 0xf2, 0x17, + 0x01, 0x00, 0x58, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x5a, 0x00, 0xc0, 0x77, 0x80, 0x00, 0x06, 0xc0, + 0xf2, 0x17, 0x01, 0x00, 0x70, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x72, 0x00, 0xc0, 0x77, 0x08, 0x00, + 0x1d, 0xc1, 0xf2, 0x17, 0x01, 0x00, 0x08, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x0a, 0x00, 0xc0, 0x77, + 0x00, 0x02, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, + 0x64, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x66, 0x00, + 0xc0, 0x77, 0x40, 0x00, 0x06, 0xc0, 0xf2, 0x17, + 0x01, 0x00, 0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x5e, 0x00, 0xc0, 0x77, 0x01, 0x00, 0x01, 0xc0, + 0x1b, 0xcf, 0x1a, 0xcf, 0xf2, 0x17, 0x01, 0x00, + 0x00, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x02, 0x00, + 0xc8, 0x09, 0x34, 0x01, 0xca, 0x17, 0x14, 0x00, + 0xd8, 0x77, 0x01, 0x00, 0x05, 0xc0, 0xca, 0xd9, + 0xd8, 0x57, 0xff, 0x00, 0x01, 0xc0, 0xca, 0xd9, + 0xe2, 0x19, 0x94, 0xc0, 0xe2, 0x27, 0x00, 0x00, + 0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, + 0x9f, 0xaf, 0x40, 0x06, 0x9f, 0xaf, 0xc4, 0x01, + 0xe7, 0x57, 0x00, 0x00, 0xd2, 0x06, 0x9f, 0xa1, + 0x0e, 0x0a, 0xca, 0x05, 0xc8, 0x05, 0xc0, 0x05, + 0xe7, 0x05, 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, + 0xc8, 0x09, 0xa0, 0x06, 0x08, 0x62, 0x97, 0xc0, + 0x27, 0x04, 0xa0, 0x06, 0x27, 0x52, 0xa2, 0x06, + 0x03, 0xc1, 0xe7, 0x07, 0xa0, 0x06, 0xa2, 0x06, + 0x9f, 0xaf, 0x08, 0x03, 0xe7, 0x57, 0x00, 0x00, + 0xaa, 0x06, 0x02, 0xc0, 0x27, 0xda, 0xaa, 0x06, + 0x97, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xfb, 0x13, 0xe7, 0x57, + 0x00, 0x80, 0xb2, 0x00, 0x06, 0xc2, 0xe7, 0x07, + 0xee, 0x0b, 0x12, 0x00, 0xe7, 0x07, 0x34, 0x0c, + 0xb2, 0x00, 0xe7, 0x07, 0xc6, 0x07, 0xf2, 0x02, + 0xc8, 0x09, 0xb4, 0x00, 0xf8, 0x07, 0x02, 0x00, + 0x0d, 0x00, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, + 0x00, 0x00, 0x0e, 0xc0, 0xc8, 0x09, 0xde, 0x00, + 0xc8, 0x17, 0x09, 0x00, 0xc9, 0x07, 0xda, 0x06, + 0xc0, 0x07, 0x04, 0x00, 0x68, 0x0a, 0x00, 0xda, + 0x7d, 0xc1, 0xe7, 0x09, 0xc0, 0x00, 0x7c, 0x06, + 0xe7, 0x09, 0xbe, 0x00, 0x78, 0x06, 0xe7, 0x09, + 0x10, 0x00, 0xbc, 0x06, 0xc8, 0x07, 0xd6, 0x07, + 0x9f, 0xaf, 0xae, 0x07, 0x9f, 0xaf, 0x00, 0x0a, + 0xc8, 0x09, 0xde, 0x00, 0x00, 0x0e, 0x0f, 0x00, + 0x41, 0x90, 0x9f, 0xde, 0x06, 0x00, 0x44, 0xaf, + 0x27, 0x00, 0xb2, 0x06, 0x27, 0x00, 0xb4, 0x06, + 0x27, 0x00, 0xb6, 0x06, 0xc0, 0x07, 0x74, 0x00, + 0x44, 0xaf, 0x27, 0x00, 0xd6, 0x06, 0x08, 0x00, + 0x00, 0x90, 0xc1, 0x07, 0x3a, 0x00, 0x20, 0x00, + 0x01, 0xda, 0x7d, 0xc1, 0x9f, 0xaf, 0xba, 0x09, + 0xc0, 0x07, 0x44, 0x00, 0x48, 0xaf, 0x27, 0x00, + 0x7a, 0x06, 0x9f, 0xaf, 0x96, 0x0a, 0xe7, 0x07, + 0x01, 0x00, 0xc0, 0x06, 0xe7, 0x05, 0x0e, 0xc0, + 0x97, 0xcf, 0x49, 0xaf, 0xe7, 0x87, 0x43, 0x00, + 0x0e, 0xc0, 0xe7, 0x07, 0xff, 0xff, 0xbe, 0x06, + 0x9f, 0xaf, 0xae, 0x0a, 0xc0, 0x07, 0x01, 0x00, + 0x60, 0xaf, 0x4a, 0xaf, 0x97, 0xcf, 0x00, 0x08, + 0x09, 0x08, 0x11, 0x08, 0x00, 0xda, 0x7c, 0xc1, + 0x97, 0xcf, 0x67, 0x04, 0xcc, 0x02, 0xc0, 0xdf, + 0x51, 0x94, 0xb1, 0xaf, 0x06, 0x00, 0xc1, 0xdf, + 0xc9, 0x09, 0xcc, 0x02, 0x49, 0x62, 0x75, 0xc1, + 0xc0, 0xdf, 0xa7, 0xcf, 0xd6, 0x02, 0x0e, 0x00, + 0x24, 0x00, 0x80, 0x04, 0x22, 0x00, 0x4e, 0x05, + 0xd0, 0x00, 0x0e, 0x0a, 0xaa, 0x00, 0x30, 0x08, + 0xbe, 0x00, 0x4a, 0x0a, 0x10, 0x00, 0x20, 0x00, + 0x04, 0x00, 0x6e, 0x04, 0x02, 0x00, 0x6a, 0x04, + 0x06, 0x00, 0x00, 0x00, 0x24, 0xc0, 0x04, 0x04, + 0x28, 0xc0, 0xfe, 0xfb, 0x1e, 0xc0, 0x00, 0x04, + 0x22, 0xc0, 0xff, 0xf4, 0xc0, 0x00, 0x90, 0x09, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x56, 0x08, + 0x60, 0x08, 0xd0, 0x08, 0xda, 0x08, 0x00, 0x09, + 0x04, 0x09, 0x08, 0x09, 0x32, 0x09, 0x42, 0x09, + 0x50, 0x09, 0x52, 0x09, 0x5a, 0x09, 0x5a, 0x09, + 0x27, 0x02, 0xca, 0x06, 0x97, 0xcf, 0xe7, 0x07, + 0x00, 0x00, 0xca, 0x06, 0x0a, 0x0e, 0x01, 0x00, + 0xca, 0x57, 0x0e, 0x00, 0x9f, 0xc3, 0x5a, 0x09, + 0xca, 0x37, 0x00, 0x00, 0x9f, 0xc2, 0x5a, 0x09, + 0x0a, 0xd2, 0xb2, 0xcf, 0x16, 0x08, 0xc8, 0x09, + 0xde, 0x00, 0x07, 0x06, 0x9f, 0xcf, 0x6c, 0x09, + 0x17, 0x02, 0xc8, 0x09, 0xde, 0x00, 0x00, 0x0e, + 0x0f, 0x00, 0x41, 0x90, 0x9f, 0xde, 0x06, 0x00, + 0xc8, 0x05, 0x30, 0x50, 0x06, 0x00, 0x9f, 0xc8, + 0x5a, 0x09, 0x27, 0x0c, 0x02, 0x00, 0xb0, 0x06, + 0xc0, 0x09, 0xb2, 0x06, 0x27, 0x00, 0xb4, 0x06, + 0xe7, 0x07, 0x00, 0x00, 0xae, 0x06, 0x27, 0x00, + 0x80, 0x06, 0x00, 0x1c, 0x06, 0x00, 0x27, 0x00, + 0xb6, 0x06, 0x41, 0x90, 0x67, 0x50, 0xb0, 0x06, + 0x0d, 0xc0, 0x67, 0x00, 0x7e, 0x06, 0x27, 0x0c, + 0x06, 0x00, 0x82, 0x06, 0xe7, 0x07, 0xbc, 0x08, + 0x84, 0x06, 0xc8, 0x07, 0x7e, 0x06, 0x41, 0x90, + 0x51, 0xaf, 0x97, 0xcf, 0x9f, 0xaf, 0x48, 0x0c, + 0xe7, 0x09, 0xb6, 0x06, 0xb4, 0x06, 0xe7, 0x09, + 0xb0, 0x06, 0xae, 0x06, 0x59, 0xaf, 0x97, 0xcf, + 0x27, 0x0c, 0x02, 0x00, 0xac, 0x06, 0x59, 0xaf, + 0x97, 0xcf, 0x09, 0x0c, 0x02, 0x00, 0x09, 0xda, + 0x49, 0xd2, 0xc9, 0x19, 0xd6, 0x06, 0xc8, 0x07, + 0x7e, 0x06, 0xe0, 0x07, 0x00, 0x00, 0x60, 0x02, + 0xe0, 0x07, 0x04, 0x00, 0xd0, 0x07, 0xcc, 0x08, + 0x48, 0xdb, 0x41, 0x90, 0x50, 0xaf, 0x97, 0xcf, + 0x59, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, + 0xf0, 0x57, 0x06, 0x00, 0x06, 0x00, 0x25, 0xc1, + 0xe7, 0x07, 0x70, 0x06, 0x80, 0x06, 0x41, 0x90, + 0x67, 0x00, 0x7e, 0x06, 0x27, 0x0c, 0x06, 0x00, + 0x82, 0x06, 0xe7, 0x07, 0x8c, 0x09, 0x84, 0x06, + 0xc8, 0x07, 0x7e, 0x06, 0x41, 0x90, 0x51, 0xaf, + 0x97, 0xcf, 0x07, 0x0c, 0x06, 0x00, 0xc7, 0x57, + 0x06, 0x00, 0x0f, 0xc1, 0xc8, 0x07, 0x70, 0x06, + 0x15, 0xcf, 0x00, 0x0c, 0x02, 0x00, 0x00, 0xda, + 0x40, 0xd1, 0x27, 0x00, 0xc2, 0x06, 0x1e, 0xcf, + 0x1d, 0xcf, 0x27, 0x0c, 0x02, 0x00, 0xcc, 0x06, + 0x19, 0xcf, 0x27, 0x02, 0x20, 0x01, 0xe7, 0x07, + 0x08, 0x00, 0x22, 0x01, 0xe7, 0x07, 0x13, 0x00, + 0xb0, 0xc0, 0x97, 0xcf, 0x41, 0x90, 0x67, 0x00, + 0x7e, 0x06, 0xe7, 0x01, 0x82, 0x06, 0x27, 0x02, + 0x80, 0x06, 0xe7, 0x07, 0x8c, 0x09, 0x84, 0x06, + 0xc8, 0x07, 0x7e, 0x06, 0xc1, 0x07, 0x00, 0x80, + 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, + 0x00, 0x60, 0x05, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0xc4, 0x06, 0xa7, 0xcf, 0x7c, 0x06, 0x9f, 0xaf, + 0x00, 0x0a, 0xe7, 0x07, 0x01, 0x00, 0xc4, 0x06, + 0x49, 0xaf, 0xd7, 0x09, 0x00, 0xc0, 0x07, 0xaf, + 0xe7, 0x05, 0x00, 0xc0, 0x4a, 0xaf, 0xa7, 0xcf, + 0x7c, 0x06, 0xc0, 0x07, 0xfe, 0x7f, 0x44, 0xaf, + 0x40, 0x00, 0xc0, 0x37, 0x00, 0x01, 0x41, 0x90, + 0xc0, 0x37, 0x08, 0x00, 0xdf, 0xde, 0x50, 0x06, + 0xc0, 0x57, 0x10, 0x00, 0x02, 0xc2, 0xc0, 0x07, + 0x10, 0x00, 0x27, 0x00, 0x9a, 0x06, 0x41, 0x90, + 0x9f, 0xde, 0x40, 0x06, 0x44, 0xaf, 0x27, 0x00, + 0x9c, 0x06, 0xc0, 0x09, 0x9a, 0x06, 0x41, 0x90, + 0x00, 0xd2, 0x00, 0xd8, 0x9f, 0xde, 0x08, 0x00, + 0x44, 0xaf, 0x27, 0x00, 0xc8, 0x06, 0x97, 0xcf, + 0xe7, 0x87, 0x00, 0x84, 0x28, 0xc0, 0xe7, 0x67, + 0xff, 0xfb, 0x24, 0xc0, 0x97, 0xcf, 0xe7, 0x87, + 0x01, 0x00, 0xd2, 0x06, 0xe7, 0x57, 0x00, 0x00, + 0xa8, 0x06, 0x97, 0xc1, 0x9f, 0xaf, 0x00, 0x0a, + 0xe7, 0x87, 0x00, 0x06, 0x22, 0xc0, 0xe7, 0x07, + 0x00, 0x00, 0x90, 0xc0, 0xe7, 0x67, 0xfe, 0xff, + 0x3e, 0xc0, 0xe7, 0x07, 0x26, 0x00, 0x0a, 0xc0, + 0xe7, 0x87, 0x01, 0x00, 0x3e, 0xc0, 0xe7, 0x07, + 0xff, 0xff, 0xbe, 0x06, 0x9f, 0xaf, 0x10, 0x0b, + 0x97, 0xcf, 0x17, 0x00, 0xa7, 0xaf, 0x78, 0x06, + 0xc0, 0x05, 0x27, 0x00, 0x76, 0x06, 0xe7, 0x87, + 0x01, 0x00, 0xd2, 0x06, 0x9f, 0xaf, 0x00, 0x0a, + 0xe7, 0x07, 0x0c, 0x00, 0x40, 0xc0, 0x9f, 0xaf, + 0x10, 0x0b, 0x00, 0x90, 0x27, 0x00, 0xa6, 0x06, + 0x27, 0x00, 0xaa, 0x06, 0xe7, 0x09, 0xb2, 0x06, + 0xb4, 0x06, 0x27, 0x00, 0xae, 0x06, 0x27, 0x00, + 0xac, 0x06, 0x9f, 0xaf, 0xae, 0x0a, 0xc0, 0x07, + 0x00, 0x00, 0x27, 0x00, 0xb2, 0x02, 0x27, 0x00, + 0xb4, 0x02, 0x27, 0x00, 0x8e, 0x06, 0xc0, 0x07, + 0x06, 0x00, 0xc8, 0x09, 0xde, 0x00, 0xc8, 0x17, + 0x03, 0x00, 0xc9, 0x07, 0x70, 0x06, 0x29, 0x0a, + 0x00, 0xda, 0x7d, 0xc1, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0x00, 0x90, 0x27, 0x00, + 0x96, 0x06, 0xe7, 0x07, 0x96, 0x06, 0x98, 0x06, + 0x27, 0x00, 0xa0, 0x06, 0xe7, 0x07, 0xa0, 0x06, + 0xa2, 0x06, 0x27, 0x00, 0xa6, 0x06, 0x27, 0x00, + 0x90, 0x06, 0x27, 0x00, 0x9e, 0x06, 0xc8, 0x09, + 0x9c, 0x06, 0xc1, 0x09, 0x9a, 0x06, 0xc9, 0x07, + 0xa4, 0x06, 0x11, 0x02, 0x09, 0x02, 0xc8, 0x17, + 0x40, 0x06, 0x01, 0xda, 0x7a, 0xc1, 0x51, 0x94, + 0xc8, 0x09, 0xc8, 0x06, 0xc9, 0x07, 0xc6, 0x06, + 0xc1, 0x09, 0x9a, 0x06, 0x11, 0x02, 0x09, 0x02, + 0xc8, 0x17, 0x08, 0x00, 0x01, 0xda, 0x7a, 0xc1, + 0x51, 0x94, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, + 0xe7, 0x57, 0x00, 0x00, 0x76, 0x06, 0x97, 0xc0, + 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x09, 0xbe, 0x06, + 0xba, 0x06, 0xe7, 0x57, 0xff, 0xff, 0xba, 0x06, + 0x04, 0xc1, 0xe7, 0x07, 0x10, 0x0b, 0xb8, 0x06, + 0x97, 0xcf, 0xe7, 0x17, 0x32, 0x00, 0xba, 0x06, + 0xe7, 0x67, 0xff, 0x07, 0xba, 0x06, 0xe7, 0x07, + 0x46, 0x0b, 0xb8, 0x06, 0x97, 0xcf, 0xe7, 0x57, + 0x00, 0x00, 0xc0, 0x06, 0x23, 0xc0, 0xe7, 0x07, + 0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x80, + 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, + 0xe7, 0x07, 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x07, + 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, + 0x00, 0x00, 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, + 0xe7, 0x07, 0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07, + 0x00, 0x80, 0x40, 0xc0, 0xc0, 0x07, 0x00, 0x00, + 0xe7, 0x07, 0x00, 0x00, 0x40, 0xc0, 0xe7, 0x07, + 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07, 0x04, 0x00, + 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x02, 0x40, 0xc0, + 0xe7, 0x07, 0x0c, 0x02, 0x40, 0xc0, 0xe7, 0x07, + 0x00, 0x00, 0xc0, 0x06, 0xe7, 0x07, 0x00, 0x00, + 0xb8, 0x06, 0xe7, 0x07, 0x00, 0x00, 0xd2, 0x06, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x9f, 0xaf, + 0x34, 0x02, 0xe7, 0x05, 0x00, 0xc0, 0x9f, 0xaf, + 0xc4, 0x01, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, + 0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xe7, 0x57, + 0x00, 0x00, 0xa8, 0x06, 0x06, 0xc0, 0xc0, 0x09, + 0x92, 0xc0, 0xc0, 0x77, 0x09, 0x02, 0x9f, 0xc1, + 0x5c, 0x05, 0x9f, 0xcf, 0x32, 0x06, 0xd7, 0x09, + 0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x0e, 0xc0, + 0x9f, 0xaf, 0x02, 0x0c, 0xe7, 0x05, 0x0e, 0xc0, + 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x02, + 0xc8, 0x09, 0xb0, 0xc0, 0xe7, 0x67, 0xfe, 0x7f, + 0xb0, 0xc0, 0xc8, 0x77, 0x00, 0x20, 0x9f, 0xc1, + 0x64, 0xeb, 0xe7, 0x57, 0x00, 0x00, 0xc8, 0x02, + 0x9f, 0xc1, 0x80, 0xeb, 0xc8, 0x99, 0xca, 0x02, + 0xc8, 0x67, 0x04, 0x00, 0x9f, 0xc1, 0x96, 0xeb, + 0x9f, 0xcf, 0x4c, 0xeb, 0xe7, 0x07, 0x00, 0x00, + 0xa6, 0xc0, 0xe7, 0x09, 0xb0, 0xc0, 0xc8, 0x02, + 0xe7, 0x07, 0x03, 0x00, 0xb0, 0xc0, 0x97, 0xcf, + 0xc0, 0x09, 0xb0, 0x06, 0xc0, 0x37, 0x01, 0x00, + 0x97, 0xc9, 0xc9, 0x09, 0xb2, 0x06, 0x02, 0x00, + 0x41, 0x90, 0x48, 0x02, 0xc9, 0x17, 0x06, 0x00, + 0x9f, 0xaf, 0x08, 0x04, 0x9f, 0xa2, 0x72, 0x0c, + 0x02, 0xda, 0x77, 0xc1, 0x41, 0x60, 0x71, 0xc1, + 0x97, 0xcf, 0x17, 0x02, 0x57, 0x02, 0x43, 0x04, + 0x21, 0x04, 0xe0, 0x00, 0x43, 0x04, 0x21, 0x04, + 0xe0, 0x00, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, + 0xc1, 0x07, 0x01, 0x00, 0xc9, 0x05, 0xc8, 0x05, + 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00, 0x8e, 0x06, + 0xc8, 0x07, 0x86, 0x06, 0xe7, 0x07, 0x00, 0x00, + 0x86, 0x06, 0xe7, 0x07, 0x10, 0x08, 0x88, 0x06, + 0xe7, 0x07, 0x04, 0x00, 0x8a, 0x06, 0xe7, 0x07, + 0xbc, 0x0c, 0x8c, 0x06, 0xc1, 0x07, 0x03, 0x80, + 0x50, 0xaf, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00, + 0x8e, 0x06, 0x97, 0xcf, + 0x00, 0x00 +}; + +/**************************************************************** + * kaweth_new_code_fix + ****************************************************************/ +static __u8 kaweth_new_code_fix[] = +{ + 0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD, + 0x02, 0x00, 0x08, 0x00, 0x28, 0x00, 0x2c, 0x00, + 0x34, 0x00, 0x3c, 0x00, 0x40, 0x00, 0x48, 0x00, + 0x54, 0x00, 0x58, 0x00, 0x5e, 0x00, 0x64, 0x00, + 0x68, 0x00, 0x6e, 0x00, 0x6c, 0x00, 0x72, 0x00, + 0x76, 0x00, 0x7c, 0x00, 0x80, 0x00, 0x86, 0x00, + 0x8a, 0x00, 0x90, 0x00, 0x94, 0x00, 0x98, 0x00, + 0x9e, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xb0, 0x00, + 0xb4, 0x00, 0xb8, 0x00, 0xc0, 0x00, 0xc6, 0x00, + 0xca, 0x00, 0xd0, 0x00, 0xd4, 0x00, 0xd8, 0x00, + 0xe0, 0x00, 0xde, 0x00, 0xe8, 0x00, 0xf0, 0x00, + 0xfc, 0x00, 0x04, 0x01, 0x0a, 0x01, 0x18, 0x01, + 0x22, 0x01, 0x28, 0x01, 0x3a, 0x01, 0x3e, 0x01, + 0x7e, 0x01, 0x98, 0x01, 0x9c, 0x01, 0xa2, 0x01, + 0xac, 0x01, 0xb2, 0x01, 0xba, 0x01, 0xc0, 0x01, + 0xc8, 0x01, 0xd0, 0x01, 0xd6, 0x01, 0xf4, 0x01, + 0xfc, 0x01, 0x08, 0x02, 0x16, 0x02, 0x1a, 0x02, + 0x22, 0x02, 0x2a, 0x02, 0x2e, 0x02, 0x3e, 0x02, + 0x44, 0x02, 0x4a, 0x02, 0x50, 0x02, 0x64, 0x02, + 0x62, 0x02, 0x6c, 0x02, 0x72, 0x02, 0x86, 0x02, + 0x8c, 0x02, 0x90, 0x02, 0x9e, 0x02, 0xbc, 0x02, + 0xd0, 0x02, 0xd8, 0x02, 0xdc, 0x02, 0xe0, 0x02, + 0xe8, 0x02, 0xe6, 0x02, 0xf4, 0x02, 0xfe, 0x02, + 0x04, 0x03, 0x0c, 0x03, 0x28, 0x03, 0x7c, 0x03, + 0x90, 0x03, 0x94, 0x03, 0x9c, 0x03, 0xa2, 0x03, + 0xc0, 0x03, 0xd0, 0x03, 0xd4, 0x03, 0xee, 0x03, + 0xfa, 0x03, 0xfe, 0x03, 0x2e, 0x04, 0x32, 0x04, + 0x3c, 0x04, 0x40, 0x04, 0x4e, 0x04, 0x76, 0x04, + 0x7c, 0x04, 0x84, 0x04, 0x8a, 0x04, 0x8e, 0x04, + 0xa6, 0x04, 0xb0, 0x04, 0xb8, 0x04, 0xbe, 0x04, + 0xd2, 0x04, 0xdc, 0x04, 0xee, 0x04, 0x10, 0x05, + 0x1a, 0x05, 0x24, 0x05, 0x2a, 0x05, 0x36, 0x05, + 0x34, 0x05, 0x3c, 0x05, 0x42, 0x05, 0x64, 0x05, + 0x6a, 0x05, 0x6e, 0x05, 0x86, 0x05, 0x22, 0x06, + 0x26, 0x06, 0x2c, 0x06, 0x30, 0x06, 0x42, 0x06, + 0x4a, 0x06, 0x4e, 0x06, 0x56, 0x06, 0x54, 0x06, + 0x5a, 0x06, 0x60, 0x06, 0x66, 0x06, 0xe8, 0x06, + 0xee, 0x06, 0xf4, 0x06, 0x16, 0x07, 0x26, 0x07, + 0x2c, 0x07, 0x32, 0x07, 0x36, 0x07, 0x3a, 0x07, + 0x3e, 0x07, 0x52, 0x07, 0x56, 0x07, 0x5a, 0x07, + 0x64, 0x07, 0x76, 0x07, 0x7a, 0x07, 0x80, 0x07, + 0x84, 0x07, 0x8a, 0x07, 0x9e, 0x07, 0xa2, 0x07, + 0xda, 0x07, 0xde, 0x07, 0xe2, 0x07, 0xe6, 0x07, + 0xea, 0x07, 0xee, 0x07, 0xf2, 0x07, 0xf6, 0x07, + 0x0e, 0x08, 0x16, 0x08, 0x18, 0x08, 0x1a, 0x08, + 0x1c, 0x08, 0x1e, 0x08, 0x20, 0x08, 0x22, 0x08, + 0x24, 0x08, 0x26, 0x08, 0x28, 0x08, 0x2a, 0x08, + 0x2c, 0x08, 0x2e, 0x08, 0x32, 0x08, 0x3a, 0x08, + 0x46, 0x08, 0x4e, 0x08, 0x54, 0x08, 0x5e, 0x08, + 0x78, 0x08, 0x7e, 0x08, 0x82, 0x08, 0x86, 0x08, + 0x8c, 0x08, 0x90, 0x08, 0x98, 0x08, 0x9e, 0x08, + 0xa4, 0x08, 0xaa, 0x08, 0xb0, 0x08, 0xae, 0x08, + 0xb4, 0x08, 0xbe, 0x08, 0xc4, 0x08, 0xc2, 0x08, + 0xca, 0x08, 0xc8, 0x08, 0xd4, 0x08, 0xe4, 0x08, + 0xe8, 0x08, 0xf6, 0x08, 0x14, 0x09, 0x12, 0x09, + 0x1a, 0x09, 0x20, 0x09, 0x26, 0x09, 0x24, 0x09, + 0x2a, 0x09, 0x3e, 0x09, 0x4c, 0x09, 0x56, 0x09, + 0x70, 0x09, 0x74, 0x09, 0x78, 0x09, 0x7e, 0x09, + 0x7c, 0x09, 0x82, 0x09, 0x98, 0x09, 0x9c, 0x09, + 0xa0, 0x09, 0xa6, 0x09, 0xb8, 0x09, 0xdc, 0x09, + 0xe8, 0x09, 0xec, 0x09, 0xfc, 0x09, 0x12, 0x0a, + 0x18, 0x0a, 0x1e, 0x0a, 0x42, 0x0a, 0x46, 0x0a, + 0x4e, 0x0a, 0x54, 0x0a, 0x5a, 0x0a, 0x5e, 0x0a, + 0x68, 0x0a, 0x6e, 0x0a, 0x72, 0x0a, 0x78, 0x0a, + 0x76, 0x0a, 0x7c, 0x0a, 0x80, 0x0a, 0x84, 0x0a, + 0x94, 0x0a, 0xa4, 0x0a, 0xb8, 0x0a, 0xbe, 0x0a, + 0xbc, 0x0a, 0xc2, 0x0a, 0xc8, 0x0a, 0xc6, 0x0a, + 0xcc, 0x0a, 0xd0, 0x0a, 0xd4, 0x0a, 0xd8, 0x0a, + 0xdc, 0x0a, 0xe0, 0x0a, 0xf2, 0x0a, 0xf6, 0x0a, + 0xfa, 0x0a, 0x14, 0x0b, 0x1a, 0x0b, 0x20, 0x0b, + 0x1e, 0x0b, 0x26, 0x0b, 0x2e, 0x0b, 0x2c, 0x0b, + 0x36, 0x0b, 0x3c, 0x0b, 0x42, 0x0b, 0x40, 0x0b, + 0x4a, 0x0b, 0xaa, 0x0b, 0xb0, 0x0b, 0xb6, 0x0b, + 0xc0, 0x0b, 0xc8, 0x0b, 0xda, 0x0b, 0xe8, 0x0b, + 0xec, 0x0b, 0xfa, 0x0b, 0x4a, 0x0c, 0x54, 0x0c, + 0x62, 0x0c, 0x66, 0x0c, 0x96, 0x0c, 0x9a, 0x0c, + 0xa0, 0x0c, 0xa6, 0x0c, 0xa4, 0x0c, 0xac, 0x0c, + 0xb2, 0x0c, 0xb0, 0x0c, 0xc0, 0x0c, + 0x00, 0x00 +}; + + +const int len_kaweth_trigger_code = sizeof(kaweth_trigger_code); +const int len_kaweth_trigger_code_fix = sizeof(kaweth_trigger_code_fix); +const int len_kaweth_new_code = sizeof(kaweth_new_code); +const int len_kaweth_new_code_fix = sizeof(kaweth_new_code_fix); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/net1080.c linux.ac/drivers/usb/net1080.c --- linux.vanilla/drivers/usb/net1080.c Sat May 26 16:53:14 2001 +++ linux.ac/drivers/usb/net1080.c Thu Jan 1 01:00:00 1970 @@ -1,1129 +0,0 @@ -/* - * NetChip 1080 Driver (USB Host-to-Host Link) - * Copyright (C) 2000 by David Brownell - */ - -/* - * This talks to the NetChip 1080, which can appear in "network cables" - * and other designs. This driver interoperates with the Win32 network - * drivers from NetChip, using the NetChip reference design. - * - * The IP-over-USB protocol here may be of interest. Embedded devices - * could implement it at the cost of two bulk endpoints, and whatever - * other system resources the desired IP-based applications need. - * Some Linux palmtops could support that today. (Devices that don't - * support the TTL-driven data mangling of the net1080 chip won't need - * the header/trailer support though.) - * - * STATUS: - * - * 13-sept-2000 experimental, new - * - * This doesn't yet do any network hotplugging, and there's no matching - * ifup policy script ... it should arrange bridging with "brctl", and - * should handle static and dynamic ("pump") setups. - * - * RX/TX queue sizes currently fixed at one due to URB unlink problems. - * - * 10-oct-2000 - * usb_device_id table created. - * - * 28-oct-2000 - * misc fixes; mostly, discard more TTL-mangled rx packets. - * - * 01-nov-2000 - * usb_device_id table support added by Adam J. Richter . - * - * 08-apr-2001 gb - * Identify version on module load. - * - *-------------------------------------------------------------------------*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEBUG // error path messages -// #define VERBOSE // more; success messages -#define USE_TTL // timeout our reads - -#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) -# define DEBUG -#endif -#include - -/* - * Version Information - */ -#define DRIVER_VERSION "v1.0.0" -#define DRIVER_AUTHOR "David Brownell " -#define DRIVER_DESC "NetChip 1080 Driver (USB Host-to-Host Link)" - - -static const struct usb_device_id products [] = { - // reference design - { USB_DEVICE(0x1080, 0x525), - driver_info: (unsigned long) "NetChip TurboCONNECT" }, - // Belkin, ... - { }, // END -}; - -MODULE_DEVICE_TABLE (usb, products); - -static u8 node_id [ETH_ALEN]; - - -/*------------------------------------------------------------------------- - * - * NetChip protocol: ethernet framing, and only use bulk endpoints (01/81; - * not mailboxes 02/82 or status interrupt 83). Expects Ethernet bridging. - * Odd USB length == always short read. - * - nc_header - * - payload, in Ethernet framing (14 byte header etc) - * - (optional padding byte, if needed so length is odd) - * - nc_trailer - */ - -struct nc_header { - u16 hdr_len; // sizeof nc_header (LE, all) - u16 packet_len; // packet size - u16 packet_id; // detects dropped packets -#define NC_MIN_HEADER 6 - - // all else is optional, and must start with: - // u16 vendorId; // from usb-if - // u16 productId; -}; - -#define NC_PAD_BYTE ((unsigned char)0xAC) - -struct nc_trailer { - u16 packet_id; -}; - -// packetsize == f(mtu setting), with upper limit -#define NC_MAX_PACKET(mtu) (sizeof (struct nc_header) \ - + (mtu) \ - + 1 \ - + sizeof (struct nc_trailer)) -#define MAX_PACKET 8191 - -// zero means no timeout; else, how long a 64 byte bulk -// read may be queued before HW flushes it. -#define NC_READ_TTL ((u8)255) // ms - - -/*-------------------------------------------------------------------------*/ - -// list of all devices we manage -static DECLARE_MUTEX (net1080_mutex); -static LIST_HEAD (net1080_list); - - -// Nineteen USB 1.1 max size bulk transactions per frame, max. -#if 0 -#define RX_QLEN 4 -#define TX_QLEN 4 - -#else -// unlink_urbs() has probs on OHCI without test8-pre patches. -#define RX_QLEN 1 -#define TX_QLEN 1 -#endif - -enum skb_state { - illegal = 0, - tx_start, tx_done, - rx_start, rx_done, rx_cleanup -}; - -struct skb_data { // skb->cb is one of these - struct urb *urb; - struct net1080 *dev; - enum skb_state state; - size_t length; -}; - - -struct net1080 { - // housekeeping - struct usb_device *udev; - const struct usb_device_id *prod_info; - struct semaphore mutex; - struct list_head dev_list; - wait_queue_head_t *wait; - - // protocol/interface state - struct net_device net; - struct net_device_stats stats; - u16 packet_id; - - // various kinds of pending driver work - struct sk_buff_head rxq; - struct sk_buff_head txq; - struct sk_buff_head done; - struct tasklet_struct bh; -}; - -#define mutex_lock(x) down(x) -#define mutex_unlock(x) up(x) - -static void defer_bh (struct net1080 *dev, struct sk_buff *skb) -{ - unsigned long flags; - - skb_unlink (skb); - spin_lock_irqsave (&dev->done.lock, flags); - __skb_queue_tail (&dev->done, skb); - if (dev->done.qlen == 1) - tasklet_schedule (&dev->bh); - spin_unlock_irqrestore (&dev->done.lock, flags); -} - -/*------------------------------------------------------------------------- - * - * We ignore most registers and EEPROM contents. - */ - -#define REG_USBCTL ((u8)0x04) -#define REG_TTL ((u8)0x10) -#define REG_STATUS ((u8)0x11) - -/* - * Vendor specific requests to read/write data - */ - -#define REQUEST_REGISTER ((u8)0x10) -#define REQUEST_EEPROM ((u8)0x11) - -#define CONTROL_TIMEOUT (500) /* msec */ - -static int -vendor_read (struct net1080 *dev, u8 req, u8 regnum, u16 *retval_ptr) -{ - int status = usb_control_msg (dev->udev, - usb_rcvctrlpipe (dev->udev, 0), - req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, regnum, - retval_ptr, sizeof *retval_ptr, - CONTROL_TIMEOUT); - if (status > 0) - status = 0; - if (!status) - le16_to_cpus (retval_ptr); - return status; -} - -static inline int -register_read (struct net1080 *dev, u8 regnum, u16 *retval_ptr) -{ - return vendor_read (dev, REQUEST_REGISTER, regnum, retval_ptr); -} - -// without retval, this can become fully async (usable in_interrupt) -static void -vendor_write (struct net1080 *dev, u8 req, u8 regnum, u16 value) -{ - usb_control_msg (dev->udev, - usb_sndctrlpipe (dev->udev, 0), - req, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, regnum, - 0, 0, // data is in setup packet - CONTROL_TIMEOUT); -} - -static inline void -register_write (struct net1080 *dev, u8 regnum, u16 value) -{ - vendor_write (dev, REQUEST_REGISTER, regnum, value); -} - - -#if 0 -static void dump_registers (struct net1080 *dev) -{ - u8 reg; - u16 value; - - dbg ("%s registers:", dev->net.name); - for (reg = 0; reg < 0x20; reg++) { - int retval; - - // reading some registers is trouble - if (reg >= 0x08 && reg <= 0xf) - continue; - if (reg >= 0x12 && reg <= 0x1e) - continue; - - retval = register_read (dev, reg, &value); - if (retval < 0) - dbg ("%s reg [0x%x] ==> error %d", - dev->net.name, reg, retval); - else - dbg ("%s reg [0x%x] = 0x%x", - dev->net.name, reg, value); - } -} -#endif - - -/*------------------------------------------------------------------------- - * - * Control register - */ - -#define USBCTL_WRITABLE_MASK 0x1f0f -// bits 15-13 reserved, r/o -#define USBCTL_ENABLE_LANG (1 << 12) -#define USBCTL_ENABLE_MFGR (1 << 11) -#define USBCTL_ENABLE_PROD (1 << 10) -#define USBCTL_ENABLE_SERIAL (1 << 9) -#define USBCTL_ENABLE_DEFAULTS (1 << 8) -// bits 7-4 reserved, r/o -#define USBCTL_FLUSH_OTHER (1 << 3) -#define USBCTL_FLUSH_THIS (1 << 2) -#define USBCTL_DISCONN_OTHER (1 << 1) -#define USBCTL_DISCONN_THIS (1 << 0) - -#ifdef DEBUG -static void dump_usbctl (struct net1080 *dev, u16 usbctl) -{ - dbg ("%s: USB %d dev %d usbctl 0x%x:%s%s%s%s%s;" - " this%s%s;" - " other%s%s; r/o 0x%x", - dev->net.name, - dev->udev->bus->busnum, dev->udev->devnum, - usbctl, - (usbctl & USBCTL_ENABLE_LANG) ? " lang" : "", - (usbctl & USBCTL_ENABLE_MFGR) ? " mfgr" : "", - (usbctl & USBCTL_ENABLE_PROD) ? " prod" : "", - (usbctl & USBCTL_ENABLE_SERIAL) ? " serial" : "", - (usbctl & USBCTL_ENABLE_DEFAULTS) ? " defaults" : "", - - (usbctl & USBCTL_FLUSH_OTHER) ? " FLUSH" : "", - (usbctl & USBCTL_DISCONN_OTHER) ? " DIS" : "", - (usbctl & USBCTL_FLUSH_THIS) ? " FLUSH" : "", - (usbctl & USBCTL_DISCONN_THIS) ? " DIS" : "", - usbctl & ~USBCTL_WRITABLE_MASK - ); -} -#else -static inline void dump_usbctl (struct net1080 *dev, u16 usbctl) {} -#endif - -/*------------------------------------------------------------------------- - * - * Status register - */ - -#define STATUS_PORT_A (1 << 15) - -#define STATUS_CONN_OTHER (1 << 14) -#define STATUS_SUSPEND_OTHER (1 << 13) -#define STATUS_MAILBOX_OTHER (1 << 12) -#define STATUS_PACKETS_OTHER(n) (((n) >> 8) && 0x03) - -#define STATUS_CONN_THIS (1 << 6) -#define STATUS_SUSPEND_THIS (1 << 5) -#define STATUS_MAILBOX_THIS (1 << 4) -#define STATUS_PACKETS_THIS(n) (((n) >> 0) && 0x03) - -#define STATUS_UNSPEC_MASK 0x0c8c -#define STATUS_NOISE_MASK ((u16)~(0x0303|STATUS_UNSPEC_MASK)) - - -#ifdef DEBUG -static void dump_status (struct net1080 *dev, u16 status) -{ - dbg ("%s: USB %d dev %d status 0x%x:" - " this (%c) PKT=%d%s%s%s;" - " other PKT=%d%s%s%s; unspec 0x%x", - dev->net.name, - dev->udev->bus->busnum, dev->udev->devnum, - status, - - // XXX the packet counts don't seem right - // (1 at reset, not 0); maybe UNSPEC too - - (status & STATUS_PORT_A) ? 'A' : 'B', - STATUS_PACKETS_THIS (status), - (status & STATUS_CONN_THIS) ? " CON" : "", - (status & STATUS_SUSPEND_THIS) ? " SUS" : "", - (status & STATUS_MAILBOX_THIS) ? " MBOX" : "", - - STATUS_PACKETS_OTHER (status), - (status & STATUS_CONN_OTHER) ? " CON" : "", - (status & STATUS_SUSPEND_OTHER) ? " SUS" : "", - (status & STATUS_MAILBOX_OTHER) ? " MBOX" : "", - - status & STATUS_UNSPEC_MASK - ); -} -#else -static inline void dump_status (struct net1080 *dev, u16 status) {} -#endif - -/*------------------------------------------------------------------------- - * - * TTL register - */ - -#define TTL_THIS(ttl) (0x00ff & ttl) -#define TTL_OTHER(ttl) (0x00ff & (ttl >> 8)) -#define MK_TTL(this,other) ((u16)(((other)<<8)|(0x00ff&(this)))) - -#ifdef DEBUG -static void dump_ttl (struct net1080 *dev, u16 ttl) -{ - dbg ("%s: USB %d dev %d ttl 0x%x this = %d, other = %d", - dev->net.name, - dev->udev->bus->busnum, dev->udev->devnum, - ttl, - - TTL_THIS (ttl), - TTL_OTHER (ttl) - ); -} -#else -static inline void dump_ttl (struct net1080 *dev, u16 ttl) {} -#endif - -#define RUN_CONTEXT (in_irq () ? "in_irq" \ - : (in_interrupt () ? "in_interrupt" : "can sleep")) - -/*-------------------------------------------------------------------------*/ - -// ensure that the device is in a known state before using it. - -// preconditions: -// caller owns the device mutex -// caller has a process context - -static int net1080_reset (struct net1080 *dev) -{ - u16 usbctl, status, ttl; - int retval; - - if ((retval = register_read (dev, REG_STATUS, &status)) < 0) { - dbg ("can't read dev %d status: %d", dev->udev->devnum, retval); - goto done; - } - dump_status (dev, status); - - if ((retval = register_read (dev, REG_USBCTL, &usbctl)) < 0) { - dbg ("can't read USBCTL, %d", retval); - goto done; - } - dump_usbctl (dev, usbctl); - - register_write (dev, REG_USBCTL, - USBCTL_FLUSH_THIS | USBCTL_FLUSH_OTHER); - - if ((retval = register_read (dev, REG_TTL, &ttl)) < 0) { - dbg ("can't read TTL, %d", retval); - goto done; - } - dump_ttl (dev, ttl); - -#ifdef USE_TTL - // Have the chip flush reads that seem to be starving for read - // bandwidth ... or we're otherwise reading. Note, Win32 drivers - // may change our read TTL for us. - - register_write (dev, REG_TTL, - MK_TTL (NC_READ_TTL, TTL_OTHER (ttl)) ); - dbg ("%s: assigned TTL, %d ms", dev->net.name, NC_READ_TTL); -#endif - - info ("%s: %s, port %c on USB %d dev %d, peer %sconnected", - dev->net.name, (char *) dev->prod_info->driver_info, - (status & STATUS_PORT_A) ? 'A' : 'B', - dev->udev->bus->busnum, - dev->udev->devnum, - (status & STATUS_CONN_OTHER) ? "" : "dis" - ); - retval = 0; - -done: - return retval; -} - - -/*------------------------------------------------------------------------- - * - * Network Device Driver support (peer link to USB Host) - * - --------------------------------------------------------------------------*/ - -static int net1080_change_mtu (struct net_device *net, int new_mtu) -{ - if ((new_mtu < 0) || NC_MAX_PACKET (new_mtu) > MAX_PACKET) - return -EINVAL; - net->mtu = new_mtu; - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static struct net_device_stats *net1080_get_stats (struct net_device *net) -{ - return &((struct net1080 *) net->priv)->stats; -} - -/*-------------------------------------------------------------------------*/ - -static void rx_complete (struct urb *urb); - -static void rx_submit (struct net1080 *dev, struct urb *urb, int flags) -{ - struct sk_buff *skb; - struct skb_data *entry; - int retval = 0; - unsigned long lockflags; - - if ((skb = alloc_skb (NC_MAX_PACKET (dev->net.mtu), flags)) == 0) { - err ("no rx skb"); - tasklet_schedule (&dev->bh); - usb_free_urb (urb); - return; - } - - entry = (struct skb_data *) skb->cb; - entry->urb = urb; - entry->dev = dev; - entry->state = rx_start; - entry->length = 0; - - FILL_BULK_URB (urb, dev->udev, usb_rcvbulkpipe (dev->udev, 1), - skb->data, skb->truesize, rx_complete, skb); - urb->transfer_flags |= USB_QUEUE_BULK; - - spin_lock_irqsave (&dev->rxq.lock, lockflags); - if (!netif_queue_stopped (&dev->net)) { - if ((retval = usb_submit_urb (urb)) != 0) { - err ("%s rx submit, %d", dev->net.name, retval); - tasklet_schedule (&dev->bh); - } else { - __skb_queue_tail (&dev->rxq, skb); - } - } else { - dbg ("rx: stopped"); - retval = -ENOLINK; - } - spin_unlock_irqrestore (&dev->rxq.lock, lockflags); - if (retval) { - dev_kfree_skb_any (skb); - usb_free_urb (urb); - } -} - - -/*-------------------------------------------------------------------------*/ - -static void rx_complete (struct urb *urb) -{ - struct sk_buff *skb = (struct sk_buff *) urb->context; - struct skb_data *entry = (struct skb_data *) skb->cb; - struct net1080 *dev = entry->dev; - int urb_status = urb->status; - - urb->dev = 0; - skb->len = urb->actual_length; - entry->state = rx_done; - entry->urb = 0; - - if ((urb->transfer_flags & USB_ASYNC_UNLINK) != 0) { - dbg ("rx ... shutting down"); - usb_free_urb (urb); - urb = 0; - } - - switch (urb_status) { - // success - case 0: - if (!(skb->len & 0x01)) { - entry->state = rx_cleanup; - dev->stats.rx_errors++; - dev->stats.rx_length_errors++; - dbg ("even rx len %d", skb->len); - } else if (skb->len > MAX_PACKET) { - entry->state = rx_cleanup; - dev->stats.rx_errors++; - dev->stats.rx_frame_errors++; - dbg ("rx too big, %d", skb->len); - } - break; - - // hardware-reported interface shutdown ... which we - // typically see before khubd calls disconnect() - case -ETIMEDOUT: // usb-ohci - case -EILSEQ: // *uhci ... "crc"/timeout error - // netif_device_detach (&dev->net); - // FALLTHROUGH - - // software-driven interface shutdown - case -ECONNRESET: - entry->state = rx_cleanup; - usb_free_urb (urb); - urb = 0; - dbg ("%s ... shutdown rx (%d)", dev->net.name, urb_status); - break; - - // data overrun ... flush fifo? - case -EOVERFLOW: - dev->stats.rx_over_errors++; - // FALLTHROUGH - - default: - entry->state = rx_cleanup; - dev->stats.rx_errors++; - err ("%s rx: status %d", dev->net.name, urb_status); - break; - } - defer_bh (dev, skb); - - if (urb) { - if (!netif_queue_stopped (&dev->net)) { - rx_submit (dev, urb, GFP_ATOMIC); - return; - } else - usb_free_urb (urb); - } -#ifdef VERBOSE - dbg ("no read resubmitted"); -#endif VERBOSE -} - -/*-------------------------------------------------------------------------*/ - -// unlink pending rx/tx; completion handlers do all other cleanup - -static int unlink_urbs (struct sk_buff_head *q) -{ - unsigned long flags; - struct sk_buff *skb; - struct skb_data *entry; - int retval; - int count = 0; - - spin_lock_irqsave (&q->lock, flags); - for (skb = q->next; skb != (struct sk_buff *) q; skb = skb->next) { - entry = (struct skb_data *) skb->cb; - entry->urb->transfer_flags |= USB_ASYNC_UNLINK; - retval = usb_unlink_urb (entry->urb); - if (retval < 0) - dbg ("unlink urb err, %d", retval); - else - count++; - } - spin_unlock_irqrestore (&q->lock, flags); - return count; -} - - -/*-------------------------------------------------------------------------*/ - -// precondition: never called in_interrupt - -static int net1080_stop (struct net_device *net) -{ - struct net1080 *dev = (struct net1080 *) net->priv; - int temp; - DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); - DECLARE_WAITQUEUE (wait, current); - - mutex_lock (&dev->mutex); - - dbg ("%s stop stats: rx/tx %ld/%ld, errs %ld/%ld", net->name, - dev->stats.rx_packets, dev->stats.tx_packets, - dev->stats.rx_errors, dev->stats.tx_errors - ); - - netif_stop_queue(net); - - // ensure there are no more active urbs - add_wait_queue (&unlink_wakeup, &wait); - dev->wait = &unlink_wakeup; - temp = unlink_urbs (&dev->txq) + unlink_urbs (&dev->rxq); - - // maybe wait for deletions to finish. - if (temp) { - current->state = TASK_UNINTERRUPTIBLE; - schedule (); - dbg ("waited for %d urb completions", temp); - } - dev->wait = 0; - current->state = TASK_RUNNING; - remove_wait_queue (&unlink_wakeup, &wait); - - mutex_unlock (&dev->mutex); - MOD_DEC_USE_COUNT; - return 0; -} - -/*-------------------------------------------------------------------------*/ - -// posts a read, and enables write queing - -// precondition: never called in_interrupt - -static int net1080_open (struct net_device *net) -{ - struct net1080 *dev = (struct net1080 *) net->priv; - int retval; - u16 status; - int i; - - MOD_INC_USE_COUNT; - mutex_lock (&dev->mutex); - - // insist peer be connected -- is this the best place? - if ((retval = register_read (dev, REG_STATUS, &status)) != 0) { - dbg ("%s open: status read failed - %d", net->name, retval); - goto done; - } - if ((status & STATUS_CONN_OTHER) != STATUS_CONN_OTHER) { - retval = -ENOLINK; - dbg ("%s open: peer not connected", net->name); - goto done; - } - - MOD_INC_USE_COUNT; - netif_start_queue (net); - for (i = 0; i < RX_QLEN; i++) - rx_submit (dev, usb_alloc_urb (0), GFP_KERNEL); - - dbg ("%s open: started queueing (rx %d, tx %d)", - net->name, RX_QLEN, TX_QLEN); -done: - mutex_unlock (&dev->mutex); - MOD_DEC_USE_COUNT; - return retval; -} - -/*-------------------------------------------------------------------------*/ - -static void tx_complete (struct urb *urb) -{ - struct sk_buff *skb = (struct sk_buff *) urb->context; - struct skb_data *entry = (struct skb_data *) skb->cb; - struct net1080 *dev = entry->dev; - - urb->dev = 0; - entry->state = tx_done; - defer_bh (dev, skb); - netif_wake_queue (&dev->net); -} - -/*-------------------------------------------------------------------------*/ - -static struct sk_buff *fixup_skb (struct sk_buff *skb) -{ - int padlen; - struct sk_buff *skb2; - - padlen = ((skb->len + sizeof (struct nc_header) - + sizeof (struct nc_trailer)) & 0x01) ? 0 : 1; - if (!skb_cloned (skb)) { - int headroom = skb_headroom (skb); - int tailroom = skb_tailroom (skb); - - if ((padlen + sizeof (struct nc_trailer)) <= tailroom - && sizeof (struct nc_header) <= headroom) - return skb; - - if ((sizeof (struct nc_header) + padlen - + sizeof (struct nc_trailer)) < - (headroom + tailroom)) { - skb->data = memmove (skb->head - + sizeof (struct nc_header), - skb->data, skb->len); - skb->tail = skb->data + skb->len; - return skb; - } - } - skb2 = skb_copy_expand (skb, - sizeof (struct nc_header), - sizeof (struct nc_trailer) + padlen, - in_interrupt () ? GFP_ATOMIC : GFP_KERNEL); - dev_kfree_skb_any (skb); - return skb2; -} - -/*-------------------------------------------------------------------------*/ - -static int net1080_start_xmit (struct sk_buff *skb, struct net_device *net) -{ - struct net1080 *dev = (struct net1080 *) net->priv; - int length = skb->len; - int retval = 0; - struct urb *urb = 0; - struct skb_data *entry; - struct nc_header *header; - struct nc_trailer *trailer; - unsigned long flags; - - if ((skb = fixup_skb (skb)) == 0) { - dbg ("can't fixup skb"); - goto drop; - } - if ((urb = usb_alloc_urb (0)) == 0) { - dbg ("no urb"); - goto drop; - } - - entry = (struct skb_data *) skb->cb; - entry->urb = urb; - entry->dev = dev; - entry->state = tx_start; - entry->length = length; - - header = (struct nc_header *) skb_push (skb, sizeof *header); - header->hdr_len = cpu_to_le16 (sizeof (*header)); - header->packet_len = cpu_to_le16 (length); - if (!((skb->len + sizeof *trailer) & 0x01)) - *skb_put (skb, 1) = NC_PAD_BYTE; - trailer = (struct nc_trailer *) skb_put (skb, sizeof *trailer); - - FILL_BULK_URB (urb, dev->udev, - usb_sndbulkpipe (dev->udev, 1), - skb->data, skb->len, tx_complete, skb); - urb->transfer_flags |= USB_QUEUE_BULK; - // FIXME urb->timeout = ...; - - spin_lock_irqsave (&dev->txq.lock, flags); - if (!netif_queue_stopped (&dev->net)) { - header->packet_id = cpu_to_le16 (dev->packet_id++); - put_unaligned (header->packet_id, &trailer->packet_id); - - netif_stop_queue (net); - if ((retval = usb_submit_urb (urb)) != 0) { - netif_start_queue (net); - dbg ("%s tx: submit urb err %d", net->name, retval); - } else { - net->trans_start = jiffies; - __skb_queue_tail (&dev->txq, skb); - if (dev->txq.qlen < TX_QLEN) - netif_start_queue (net); - } - } else - retval = -ENOLINK; - spin_unlock_irqrestore (&dev->txq.lock, flags); - - if (retval) { - dbg ("drop"); -drop: - dev->stats.tx_dropped++; - dev_kfree_skb_any (skb); - usb_free_urb (urb); -#ifdef VERBOSE - } else { - dbg ("%s: tx %p len %d", net->name, skb, length); -#endif - } - return retval; -} - - -/*-------------------------------------------------------------------------*/ - -static void rx_process (struct net1080 *dev, struct sk_buff *skb) -{ - struct nc_header *header; - struct nc_trailer *trailer; - - header = (struct nc_header *) skb->data; - le16_to_cpus (&header->hdr_len); - le16_to_cpus (&header->packet_len); - if (header->packet_len > MAX_PACKET) { - dev->stats.rx_frame_errors++; - dbg ("packet too big, %d", header->packet_len); - goto error; - } else if (header->hdr_len < NC_MIN_HEADER) { - dev->stats.rx_frame_errors++; - dbg ("header too short, %d", header->hdr_len); - goto error; - } else if (header->hdr_len > header->packet_len) { - dev->stats.rx_frame_errors++; - dbg ("header too big, %d packet %d", header->hdr_len, header->packet_len); - goto error; - } else if (header->hdr_len != sizeof *header) { - // out of band data for us? - dbg ("header OOB, %d bytes", header->hdr_len - NC_MIN_HEADER); - // switch (vendor/product ids) { ... } - } - skb_pull (skb, header->hdr_len); - - trailer = (struct nc_trailer *) - (skb->data + skb->len - sizeof *trailer); - skb_trim (skb, skb->len - sizeof *trailer); - - if ((header->packet_len & 0x01) == 0) { - if (skb->data [header->packet_len] != NC_PAD_BYTE) { - dev->stats.rx_frame_errors++; - dbg ("bad pad"); - goto error; - } - skb_trim (skb, skb->len - 1); - } - if (skb->len != header->packet_len) { - dev->stats.rx_length_errors++; - dbg ("bad packet len %d (expected %d)", - skb->len, header->packet_len); - goto error; - } - if (header->packet_id != get_unaligned (&trailer->packet_id)) { - dev->stats.rx_fifo_errors++; - dbg ("(2+ dropped) rx packet_id mismatch 0x%x 0x%x", - header->packet_id, trailer->packet_id); - goto error; - } - - if (skb->len) { - skb->dev = &dev->net; - skb->protocol = eth_type_trans (skb, &dev->net); - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - -#ifdef VERBOSE - dbg ("%s: rx %p len %d, type 0x%x, id 0x%x", - dev->net.name, skb, skb->len, skb->protocol, - le16_to_cpu (header->packet_id)); -#endif - netif_rx (skb); - } else { - dbg ("drop"); -error: - dev->stats.rx_errors++; - dev_kfree_skb (skb); - } -} - -/*-------------------------------------------------------------------------*/ - -// tasklet - -// We can have a state machine in this tasklet monitor the link state, -// using async control messaging and calling attach/detach routines. - -// But then some listener ought to respond to the changes; do those -// network attach/detach notifications get to userland somehow, such -// as by calling "ifup usb0" and "ifdown usb0"? - -static void net1080_bh (unsigned long param) -{ - struct net1080 *dev = (struct net1080 *) param; - struct sk_buff *skb; - struct skb_data *entry; - - while ((skb = skb_dequeue (&dev->done))) { - entry = (struct skb_data *) skb->cb; - switch (entry->state) { - case rx_done: - rx_process (dev, skb); - continue; - case tx_done: - if (entry->urb->status) { - // can this statistic become more specific? - dev->stats.tx_errors++; - dbg ("%s tx: err %d", dev->net.name, - entry->urb->status); - } else { - dev->stats.tx_packets++; - dev->stats.tx_bytes += entry->length; - } - // FALLTHROUGH: - case rx_cleanup: - usb_free_urb (entry->urb); - dev_kfree_skb (skb); - continue; - default: - dbg ("%s: bogus skb state %d", - dev->net.name, entry->state); - } - } - - // waiting for all pending urbs to complete? - if (dev->wait) { - if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) { - wake_up (dev->wait); - } - - // or are we maybe short a few urbs? - } else if (!netif_queue_stopped (&dev->net)) { - if (dev->rxq.qlen < TX_QLEN) { - struct urb *urb; - int i; - for (i = 0; i < 3 && dev->rxq.qlen < TX_QLEN; i++) { - if ((urb = usb_alloc_urb (0)) != 0) - rx_submit (dev, urb, GFP_ATOMIC); - } - dbg ("%s: rxqlen now %d", - dev->net.name, dev->rxq.qlen); - } - } -} - -/*------------------------------------------------------------------------- - * - * USB Device Driver support - * - --------------------------------------------------------------------------*/ - -// precondition: never called in_interrupt - -static void net1080_disconnect (struct usb_device *udev, void *ptr) -{ - struct net1080 *dev = (struct net1080 *) ptr; - - info ("%s: USB %d dev %d, %s, disconnected", - dev->net.name, - udev->bus->busnum, udev->devnum, - (char *) dev->prod_info->driver_info); - - unregister_netdev (&dev->net); - - mutex_lock (&net1080_mutex); - mutex_lock (&dev->mutex); - list_del (&dev->dev_list); - mutex_unlock (&net1080_mutex); - -#ifdef DEBUG - memset (dev, 0x55, sizeof *dev); -#endif - kfree (dev); - usb_dec_dev_use (udev); -} - - -/*-------------------------------------------------------------------------*/ - -// precondition: never called in_interrupt - -static void * -net1080_probe (struct usb_device *udev, unsigned ifnum, const struct usb_device_id *prod) -{ - struct net1080 *dev; - struct net_device *net; - struct usb_interface_descriptor *interface; - int retval; - - // sanity check; expect dedicated interface/devices for now. - interface = &udev->actconfig->interface [ifnum].altsetting[0]; - if (udev->descriptor.bNumConfigurations != 1 - || udev->config[0].bNumInterfaces != 1 - || udev->config[0].bNumInterfaces != 1 - || interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC - || interface->bNumEndpoints != 5 - ) { - dbg ("Bogus config info"); - return 0; - } - - // set up our own records - if (!(dev = kmalloc (sizeof *dev, GFP_KERNEL))) { - dbg ("can't kmalloc dev"); - return 0; - } - memset (dev, 0, sizeof *dev); - - init_MUTEX_LOCKED (&dev->mutex); - usb_inc_dev_use (udev); - dev->udev = udev; - dev->prod_info = prod; - INIT_LIST_HEAD (&dev->dev_list); - skb_queue_head_init (&dev->rxq); - skb_queue_head_init (&dev->txq); - skb_queue_head_init (&dev->done); - dev->bh.func = net1080_bh; - dev->bh.data = (unsigned long) dev; - - // set up network interface records - net = &dev->net; - net->priv = dev; - strcpy (net->name, "usb%d"); - memcpy (net->dev_addr, node_id, sizeof node_id); - - ether_setup (net); - // net->flags |= IFF_POINTOPOINT; - - net->change_mtu = net1080_change_mtu; - net->get_stats = net1080_get_stats; - net->hard_start_xmit = net1080_start_xmit; - net->open = net1080_open; - net->stop = net1080_stop; - - register_netdev (&dev->net); - - // ... talk to the device - // dump_registers (dev); - - if ((retval = net1080_reset (dev)) < 0) { - err ("%s: init reset fail on USB %d dev %d - %d", - dev->net.name, udev->bus->busnum, udev->devnum, retval); - mutex_unlock (&dev->mutex); - net1080_disconnect (udev, dev); - return 0; - } - - // ok, it's ready to go. - mutex_lock (&net1080_mutex); - list_add (&dev->dev_list, &net1080_list); - mutex_unlock (&dev->mutex); - - // start as if the link is up - netif_device_attach (&dev->net); - - mutex_unlock (&net1080_mutex); - - return dev; -} - - -/*-------------------------------------------------------------------------*/ - -static struct usb_driver net1080_driver = { - name: "net1080", - id_table: products, - probe: net1080_probe, - disconnect: net1080_disconnect, -}; - -/*-------------------------------------------------------------------------*/ - -static int __init net1080_init (void) -{ - // compiler should optimize this out - if (sizeof (((struct sk_buff *)0)->cb) < sizeof (struct skb_data)) - BUG (); - - if (usb_register (&net1080_driver) < 0) - return -1; - - get_random_bytes (node_id, sizeof node_id); - node_id [0] &= 0x7f; - - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); - - return 0; -} -module_init (net1080_init); - -static void __exit net1080_exit (void) -{ - usb_deregister (&net1080_driver); -} -module_exit (net1080_exit); - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); - diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ov511.c linux.ac/drivers/usb/ov511.c --- linux.vanilla/drivers/usb/ov511.c Sat May 26 16:53:14 2001 +++ linux.ac/drivers/usb/ov511.c Wed May 23 09:34:14 2001 @@ -152,6 +152,7 @@ MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420"); MODULE_PARM(dumppix, "i"); MODULE_PARM_DESC(dumppix, "Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details"); +MODULE_PARM(video_nr,"i"); MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); @@ -337,7 +338,7 @@ /* IMPORTANT: This output MUST be kept under PAGE_SIZE * or we need to get more sophisticated. */ - out += sprintf (out, "driver_version : %s\n", version); + out += sprintf (out, "driver_version : %s\n", DRIVER_VERSION); out += sprintf (out, "custom_id : %d\n", ov511->customid); out += sprintf (out, "model : %s\n", ov511->desc ? clist[ov511->desc].description : "unknown"); @@ -3146,11 +3147,6 @@ init_waitqueue_head(&ov511->wq); - if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - err("video_register_device failed"); - return -EBUSY; - } - if (ov511_write_regvals(dev, aRegvalsInit)) goto error; if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error; @@ -3219,7 +3215,6 @@ return 0; error: - video_unregister_device(&ov511->vdev); usb_driver_release_interface(&ov511_driver, &dev->actconfig->interface[ov511->iface]); @@ -3328,6 +3323,11 @@ ov511->buf_state = BUF_NOT_ALLOCATED; } else { err("Failed to configure camera"); + goto error; + } + + if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + err("video_register_device failed"); goto error; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/printer.c linux.ac/drivers/usb/printer.c --- linux.vanilla/drivers/usb/printer.c Sat May 26 16:53:15 2001 +++ linux.ac/drivers/usb/printer.c Wed May 23 00:37:12 2001 @@ -641,6 +641,9 @@ } static struct usb_device_id usblp_ids [] = { + { USB_DEVICE_INFO(7, 1, 1) }, + { USB_DEVICE_INFO(7, 1, 2) }, + { USB_DEVICE_INFO(7, 1, 3) }, { USB_INTERFACE_INFO(7, 1, 1) }, { USB_INTERFACE_INFO(7, 1, 2) }, { USB_INTERFACE_INFO(7, 1, 3) }, diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/se401.c linux.ac/drivers/usb/se401.c --- linux.vanilla/drivers/usb/se401.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/se401.c Sat May 26 00:16:53 2001 @@ -0,0 +1,1675 @@ +/* + * Endpoints (formerly known as AOX) se401 USB Camera Driver + * + * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) + * + * Still somewhat based on the Linux ov511 driver. + * + * 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. + * + * + * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on + * their chipset available and supporting me while writing this driver. + * - Jeroen Vreeken + */ + +static const char version[] = "0.22"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) +#define virt_to_page(arg) MAP_NR(arg) +#define vmalloc_32 vmalloc +#endif + +#include "se401.h" + +static int flickerless=0; +static int video_nr = -1; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0) +static __devinitdata struct usb_device_id device_table [] = { + { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */ + { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */ + { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */ + { USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */ + { USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */ + { } +}; + +MODULE_DEVICE_TABLE(usb, device_table); +#endif + +MODULE_AUTHOR("Jeroen Vreeken "); +MODULE_DESCRIPTION("SE401 USB Camera Driver"); +MODULE_PARM(flickerless, "i"); +MODULE_PARM_DESC(flickerless, "Net frequency to adjust exposure time to (0/50/60)"); +MODULE_PARM(video_nr, "i"); +EXPORT_NO_SYMBOLS; + + +static struct usb_driver se401_driver; + + +/********************************************************************** + * + * Memory management + * + * This is a shameless copy from the USB-cpia driver (linux kernel + * version 2.3.29 or so, I have no idea what this code actually does ;). + * Actually it seems to be a copy of a shameless copy of the bttv-driver. + * Or that is a copy of a shameless copy of ... (To the powers: is there + * no generic kernel-function to do this sort of stuff?) + * + * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says + * there will be one, but apparentely not yet -jerdfelt + * + * So I copied it again for the ov511 driver -claudio + * + * Same for the se401 driver -Jeroen + **********************************************************************/ + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if (pte_present(pte)) { + ret = (unsigned long) page_address(pte_page(pte)); + ret |= (adr & (PAGE_SIZE - 1)); + } + } + } + + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + return ret; +} + +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr, page; + + /* Round it off to PAGE_SIZE */ + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_reserve(virt_to_page(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr, page; + + if (!mem) + return; + + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + adr=(unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_unreserve(virt_to_page(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + vfree(mem); +} + + + +/**************************************************************************** + * + * /proc interface + * + ***************************************************************************/ + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + +static struct proc_dir_entry *se401_proc_entry = NULL; +extern struct proc_dir_entry *video_proc_entry; + +#define YES_NO(x) ((x) ? "yes" : "no") + +static int se401_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + char *out = page; + int i, len; + struct usb_se401 *se401 = data; + + /* Stay under PAGE_SIZE or else bla bla bla.... */ + + out+=sprintf(out, "driver_version : %s\n", version); + out+=sprintf(out, "model : %s\n", se401->camera_name); + out+=sprintf(out, "in use : %s\n", YES_NO (se401->user)); + out+=sprintf(out, "streaming : %s\n", YES_NO (se401->streaming)); + out+=sprintf(out, "button state : %s\n", YES_NO (se401->button)); + out+=sprintf(out, "button pressed : %s\n", YES_NO (se401->buttonpressed)); + out+=sprintf(out, "num_frames : %d\n", SE401_NUMFRAMES); + + out+=sprintf(out, "Sizes :"); + for (i=0; isizes; i++) { + out+=sprintf(out, " %dx%d", se401->width[i], + se401->height[i]); + } + out+=sprintf(out, "\n"); + + out+=sprintf(out, "Frames total : %d\n", se401->readcount); + out+=sprintf(out, "Frames read : %d\n", se401->framecount); + out+=sprintf(out, "Packets dropped : %d\n", se401->dropped); + out+=sprintf(out, "Decoding Errors : %d\n", se401->error); + + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) return 0; + } else + len = count; + + *start = page + off; + + return len; +} + +static int se401_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + return -EINVAL; +} + +static void create_proc_se401_cam (struct usb_se401 *se401) +{ + char name[7]; + struct proc_dir_entry *ent; + + if (!se401_proc_entry || !se401) + return; + + sprintf (name, "video%d", se401->vdev.minor); + + ent = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, + se401_proc_entry); + + if (!ent) + return; + + ent->data = se401; + ent->read_proc = se401_read_proc; + ent->write_proc = se401_write_proc; + se401->proc_entry = ent; +} + +static void destroy_proc_se401_cam (struct usb_se401 *se401) +{ + /* One to much, just to be sure :) */ + char name[9]; + + if (!se401 || !se401->proc_entry) + return; + + sprintf(name, "video%d", se401->vdev.minor); + remove_proc_entry(name, se401_proc_entry); + se401->proc_entry = NULL; +} + +static void proc_se401_create (void) +{ + if (video_proc_entry == NULL) { + err("/proc/video/ doesn't exist"); + return; + } + + se401_proc_entry=create_proc_entry("se401", S_IFDIR, video_proc_entry); + + if (se401_proc_entry) + se401_proc_entry->owner = THIS_MODULE; + else + err("Unable to initialize /proc/video/se401"); +} + +static void proc_se401_destroy(void) +{ + if (se401_proc_entry == NULL) + return; + + remove_proc_entry("se401", video_proc_entry); +} +#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */ + + +/**************************************************************************** + * + * se401 register read/write functions + * + ***************************************************************************/ + +static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req, + unsigned short value, unsigned char *cp, int size) +{ + return usb_control_msg ( + se401->dev, + set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0), + req, + (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + 0, + cp, + size, + HZ + ); +} + +static int se401_set_feature(struct usb_se401 *se401, unsigned short selector, + unsigned short param) +{ + /* specs say that the selector (address) should go in the value field + and the param in index, but in the logs of the windows driver they do + this the other way around... + */ + return usb_control_msg ( + se401->dev, + usb_sndctrlpipe(se401->dev, 0), + SE401_REQ_SET_EXT_FEATURE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + param, + selector, + NULL, + 0, + HZ + ); +} + +static unsigned short se401_get_feature(struct usb_se401 *se401, + unsigned short selector) +{ + /* For 'set' the selecetor should be in index, not sure if the spec is + wrong here to.... + */ + unsigned char cp[2]; + usb_control_msg ( + se401->dev, + usb_rcvctrlpipe(se401->dev, 0), + SE401_REQ_GET_EXT_FEATURE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + selector, + cp, + 2, + HZ + ); + return cp[0]+cp[1]*256; +} + +/**************************************************************************** + * + * Camera control + * + ***************************************************************************/ + + +static int se401_send_pict(struct usb_se401 *se401) +{ + se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l);/* integration time low */ + se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m);/* integration time mid */ + se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h);/* integration time mid */ + se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);/* reset level value */ + se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain);/* red color gain */ + se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain);/* green color gain */ + se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain);/* blue color gain */ + + return 0; +} + +static void se401_set_exposure(struct usb_se401 *se401, int brightness) +{ + int integration=brightness<<5; + + if (flickerless==50) { + integration=integration-integration%106667; + } + if (flickerless==60) { + integration=integration-integration%88889; + } + se401->brightness=integration>>5; + se401->expose_h=(integration>>16)&0xff; + se401->expose_m=(integration>>8)&0xff; + se401->expose_l=integration&0xff; +} + +static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p) +{ + p->brightness=se401->brightness; + if (se401->enhance) { + p->whiteness=32768; + } else { + p->whiteness=0; + } + p->colour=65535; + p->contrast=65535; + p->hue=se401->rgain<<10; + p->palette=se401->palette; + p->depth=3; /* rgb24 */ + return 0; +} + + +static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p) +{ + if (p->palette != VIDEO_PALETTE_RGB24) + return 1; + se401->palette=p->palette; + if (p->hue!=se401->hue) { + se401->rgain= p->hue>>10; + se401->bgain= 0x40-(p->hue>>10); + se401->hue=p->hue; + } + if (p->brightness!=se401->brightness) { + se401_set_exposure(se401, p->brightness); + } + if (p->whiteness>=32768) { + se401->enhance=1; + } else { + se401->enhance=0; + } + se401_send_pict(se401); + se401_send_pict(se401); + return 0; +} + +/* + Hyundai have some really nice docs about this and other sensor related + stuff on their homepage: www.hei.co.kr +*/ +static void se401_auto_resetlevel(struct usb_se401 *se401) +{ + unsigned int ahrc, alrc; + int oldreset=se401->resetlevel; + + /* For some reason this normally read-only register doesn't get reset + to zero after reading them just once... + */ + se401_get_feature(se401, HV7131_REG_HIREFNOH); + se401_get_feature(se401, HV7131_REG_HIREFNOL); + se401_get_feature(se401, HV7131_REG_LOREFNOH); + se401_get_feature(se401, HV7131_REG_LOREFNOL); + ahrc=256*se401_get_feature(se401, HV7131_REG_HIREFNOH) + + se401_get_feature(se401, HV7131_REG_HIREFNOL); + alrc=256*se401_get_feature(se401, HV7131_REG_LOREFNOH) + + se401_get_feature(se401, HV7131_REG_LOREFNOL); + + /* Not an exact science, but it seems to work pretty well... */ + if (alrc > 10) { + while (alrc>=10 && se401->resetlevel < 63) { + se401->resetlevel++; + alrc /=2; + } + } else if (ahrc > 20) { + while (ahrc>=20 && se401->resetlevel > 0) { + se401->resetlevel--; + ahrc /=2; + } + } + if (se401->resetlevel!=oldreset) + se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); + + return; +} + +/* irq handler for snapshot button */ +static void se401_button_irq(struct urb *urb) +{ + struct usb_se401 *se401=urb->context; + + if (!urb) { + info("ohoh: null urb"); + return; + } + if (!se401->dev) { + info("ohoh: device vapourished"); + return; + } + + if (urb->actual_length >=2 && !urb->status) { + if (se401->button) + se401->buttonpressed=1; + } +} + +static void se401_video_irq(struct urb *urb) +{ + struct usb_se401 *se401=urb->context; + int length=urb->actual_length; + + /* ohoh... */ + if (!se401->streaming) { + return; + } + if (!urb) { + info ("ohoh: null urb"); + return; + } + if (!se401->dev) { + info ("ohoh: device vapourished"); + return; + } + + /* 0 sized packets happen if we are to fast, but sometimes the camera + keeps sending them forever... + */ + if (length && !urb->status) { + se401->nullpackets=0; + switch(se401->scratch[se401->scratch_next].state) { + case BUFFER_READY: + case BUFFER_BUSY: { + se401->dropped++; + break; + } + case BUFFER_UNUSED: { + memcpy(se401->scratch[se401->scratch_next].data, (unsigned char *)urb->transfer_buffer, length); + se401->scratch[se401->scratch_next].state=BUFFER_READY; + se401->scratch[se401->scratch_next].offset=se401->bayeroffset; + se401->scratch[se401->scratch_next].length=length; + if (waitqueue_active(&se401->wq)) { + wake_up_interruptible(&se401->wq); + } + se401->scratch_overflow=0; + se401->scratch_next++; + if (se401->scratch_next>=SE401_NUMSCRATCH) + se401->scratch_next=0;; + break; + } + } + se401->bayeroffset+=length; + if (se401->bayeroffset>=se401->cheight*se401->cwidth) { + se401->bayeroffset=0; + } + } else { + se401->nullpackets++; + if (se401->nullpackets > SE401_MAX_NULLPACKETS) { + if (waitqueue_active(&se401->wq)) { + wake_up_interruptible(&se401->wq); + } + } + } + + /* Resubmit urb for new data */ + urb->status=0; + urb->dev=se401->dev; + if(usb_submit_urb(urb)) + info("urb burned down"); + return; +} + +static void se401_send_size(struct usb_se401 *se401, int width, int height) +{ + int i=0; + int mode=0x03; /* No compression */ + int sendheight=height; + int sendwidth=width; + + /* JangGu compression can only be used with the camera supported sizes, + but bayer seems to work with any size that fits on the sensor. + We check if we can use compression with the current size with either + 4 or 16 times subcapturing, if not we use uncompressed bayer data + but this will result in cutouts of the maximum size.... + */ + while (isizes && !(se401->width[i]==width && se401->height[i]==height)) + i++; + while (isizes) { + if (se401->width[i]==width*2 && se401->height[i]==height*2) { + sendheight=se401->height[i]; + sendwidth=se401->width[i]; + mode=0x40; + } + if (se401->width[i]==width*4 && se401->height[i]==height*4) { + sendheight=se401->height[i]; + sendwidth=se401->width[i]; + mode=0x42; + } + i++; + } + + se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0); + se401_set_feature(se401, SE401_OPERATINGMODE, mode); + + if (mode==0x03) { + se401->format=FMT_BAYER; + } else { + se401->format=FMT_JANGGU; + } + + return; +} + +/* + In this function se401_send_pict is called several times, + for some reason (depending on the state of the sensor and the phase of + the moon :) doing this only in either place doesn't always work... +*/ +static int se401_start_stream(struct usb_se401 *se401) +{ + urb_t *urb; + int err=0, i; + se401->streaming=1; + + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); + + /* Set picture settings */ + se401_set_feature(se401, HV7131_REG_MODE_B, 0x05);/*windowed + pix intg */ + se401_send_pict(se401); + + se401_send_size(se401, se401->cwidth, se401->cheight); + + se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, NULL, 0); + + /* Do some memory allocation */ + for (i=0; iframe[i].data=se401->fbuf + i * se401->maxframesize; + se401->frame[i].curpix=0; + } + for (i=0; isbuf[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + } + + se401->bayeroffset=0; + se401->scratch_next=0; + se401->scratch_use=0; + se401->scratch_overflow=0; + for (i=0; iscratch[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + se401->scratch[i].state=BUFFER_UNUSED; + } + + for (i=0; idev, + usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT), + se401->sbuf[i].data, SE401_PACKETSIZE, + se401_video_irq, + se401); + urb->transfer_flags |= USB_QUEUE_BULK; + + se401->urb[i]=urb; + + err=usb_submit_urb(se401->urb[i]); + if(err) + err("urb burned down"); + } + + se401->framecount=0; + + return 0; +} + +static int se401_stop_stream(struct usb_se401 *se401) +{ + int i; + + if (!se401->streaming || !se401->dev) + return 1; + + se401->streaming=0; + + se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0); + + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); + + for (i=0; iurb[i]) { + se401->urb[i]->next=NULL; + usb_unlink_urb(se401->urb[i]); + usb_free_urb(se401->urb[i]); + se401->urb[i]=NULL; + kfree(se401->sbuf[i].data); + } + for (i=0; iscratch[i].data); + se401->scratch[i].data=NULL; + } + + return 0; +} + +static int se401_set_size(struct usb_se401 *se401, int width, int height) +{ + int wasstreaming=se401->streaming; + /* Check to see if we need to change */ + if (se401->cwidth==width && se401->cheight==height) + return 0; + + /* Check for a valid mode */ + if (!width || !height) + return 1; + if ((width & 1) || (height & 1)) + return 1; + if (width>se401->width[se401->sizes-1]) + return 1; + if (height>se401->height[se401->sizes-1]) + return 1; + + /* Stop a current stream and start it again at the new size */ + if (wasstreaming) + se401_stop_stream(se401); + se401->cwidth=width; + se401->cheight=height; + if (wasstreaming) + se401_start_stream(se401); + return 0; +} + + +/**************************************************************************** + * + * Video Decoding + * + ***************************************************************************/ + +/* + This shouldn't really be done in a v4l driver.... + But it does make the image look a lot more usable. + Basicly it lifts the dark pixels more than the light pixels. +*/ +static inline void enhance_picture(unsigned char *frame, int len) +{ + while (len--) { + *frame++=(((*frame^255)*(*frame^255))/255)^255; + } +} + +static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data) +{ + struct se401_frame *frame=&se401->frame[se401->curframe]; + int linelength=se401->cwidth*3; + + if (frame->curlinepix >= linelength) { + frame->curlinepix=0; + frame->curline+=linelength; + } + + /* First three are absolute, all others relative. + * Format is rgb from right to left (mirrorred image), + * we flip it to get bgr from left to right. */ + if (frame->curlinepix < 3) { + *(frame->curline-frame->curlinepix)=1+data*4; + } else { + *(frame->curline-frame->curlinepix)= + *(frame->curline-frame->curlinepix+3)+data*4; + } + frame->curlinepix++; +} + +static inline void decode_JangGu_vlc (struct usb_se401 *se401, unsigned char *data, int bit_exp, int packetlength) +{ + int pos=0; + int vlc_cod=0; + int vlc_size=0; + int vlc_data=0; + int bit_cur; + int bit; + data+=4; + while (pos < packetlength) { + bit_cur=8; + while (bit_cur && bit_exp) { + bit=((*data)>>(bit_cur-1))&1; + if (!vlc_cod) { + if (bit) { + vlc_size++; + } else { + if (!vlc_size) { + decode_JangGu_integrate(se401, 0); + } else { + vlc_cod=2; + vlc_data=0; + } + } + } else { + if (vlc_cod==2) { + if (!bit) vlc_data=-(1<data; + int len=buffer->length; + int bit_exp=0, pix_exp=0, frameinfo=0, packetlength=0, size; + int datapos=0; + + /* New image? */ + if (!se401->frame[se401->curframe].curpix) { + se401->frame[se401->curframe].curlinepix=0; + se401->frame[se401->curframe].curline= + se401->frame[se401->curframe].data+ + se401->cwidth*3-1; + if (se401->frame[se401->curframe].grabstate==FRAME_READY) + se401->frame[se401->curframe].grabstate=FRAME_GRABBING; + se401->vlcdatapos=0; + } + while (datapos < len) { + size=1024-se401->vlcdatapos; + if (size+datapos > len) + size=len-datapos; + memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size); + se401->vlcdatapos+=size; + packetlength=0; + if (se401->vlcdatapos >= 4) { + bit_exp=se401->vlcdata[3]+(se401->vlcdata[2]<<8); + pix_exp=se401->vlcdata[1]+((se401->vlcdata[0]&0x3f)<<8); + frameinfo=se401->vlcdata[0]&0xc0; + packetlength=((bit_exp+47)>>4)<<1; + if (packetlength > 1024) { + se401->vlcdatapos=0; + datapos=len; + packetlength=0; + se401->error++; + se401->frame[se401->curframe].curpix=0; + } + } + if (packetlength && se401->vlcdatapos >= packetlength) { + decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, packetlength); + se401->frame[se401->curframe].curpix+=pix_exp*3; + datapos+=size-(se401->vlcdatapos-packetlength); + se401->vlcdatapos=0; + if (se401->frame[se401->curframe].curpix>=se401->cwidth*se401->cheight*3) { + if (se401->frame[se401->curframe].curpix==se401->cwidth*se401->cheight*3) { + if (se401->frame[se401->curframe].grabstate==FRAME_GRABBING) { + se401->frame[se401->curframe].grabstate=FRAME_DONE; + se401->framecount++; + se401->readcount++; + } + if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) { + se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1); + } + } else { + se401->error++; + } + se401->frame[se401->curframe].curpix=0; + datapos=len; + } + } else { + datapos+=size; + } + } +} + +static inline void decode_bayer (struct usb_se401 *se401, struct se401_scratch *buffer) +{ + unsigned char *data=buffer->data; + int len=buffer->length; + int offset=buffer->offset; + int datasize=se401->cwidth*se401->cheight; + struct se401_frame *frame=&se401->frame[se401->curframe]; + + unsigned char *framedata=frame->data, *curline, *nextline; + int width=se401->cwidth; + int blineoffset=0, bline; + int linelength=width*3, i; + + + if (frame->curpix==0) { + if (frame->grabstate==FRAME_READY) { + frame->grabstate=FRAME_GRABBING; + } + frame->curline=framedata+linelength; + frame->curlinepix=0; + } + + if (offset!=frame->curpix) { + /* Regard frame as lost :( */ + frame->curpix=0; + se401->error++; + return; + } + + /* Check if we have to much data */ + if (frame->curpix+len > datasize) { + len=datasize-frame->curpix; + } + if (se401->cheight%4) + blineoffset=1; + bline=frame->curpix/se401->cwidth+blineoffset; + + curline=frame->curline; + nextline=curline+linelength; + if (nextline >= framedata+datasize*3) + nextline=curline; + while (len) { + if (frame->curlinepix>=width) { + frame->curlinepix-=width; + bline=frame->curpix/width+blineoffset; + curline+=linelength*2; + nextline+=linelength*2; + if (curline >= framedata+datasize*3) { + frame->curlinepix++; + curline-=3; + nextline-=3; + len--; + data++; + frame->curpix++; + } + if (nextline >= framedata+datasize*3) + nextline=curline; + } + if ((bline&1)) { + if ((frame->curlinepix&1)) { + *(curline+2)=*data; + *(curline-1)=*data; + *(nextline+2)=*data; + *(nextline-1)=*data; + } else { + *(curline+1)= + (*(curline+1)+*data)/2; + *(curline-2)= + (*(curline-2)+*data)/2; + *(nextline+1)=*data; + *(nextline-2)=*data; + } + } else { + if ((frame->curlinepix&1)) { + *(curline+1)= + (*(curline+1)+*data)/2; + *(curline-2)= + (*(curline-2)+*data)/2; + *(nextline+1)=*data; + *(nextline-2)=*data; + } else { + *curline=*data; + *(curline-3)=*data; + *nextline=*data; + *(nextline-3)=*data; + } + } + frame->curlinepix++; + curline-=3; + nextline-=3; + len--; + data++; + frame->curpix++; + } + frame->curline=curline; + + if (frame->curpix>=datasize) { + /* Fix the top line */ + framedata+=linelength; + for (i=0; icheight; i++) { + *framedata=*(framedata+3); + *(framedata+1)=*(framedata+4); + *(framedata+2)=*(framedata+5); + framedata+=linelength; + } + frame->curpix=0; + frame->grabstate=FRAME_DONE; + se401->framecount++; + se401->readcount++; + if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) { + se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1); + } + } +} + +static int se401_newframe(struct usb_se401 *se401, int framenr) +{ + DECLARE_WAITQUEUE(wait, current); + int errors=0; + + while (se401->streaming && + (se401->frame[framenr].grabstate==FRAME_READY || + se401->frame[framenr].grabstate==FRAME_GRABBING) ) { + if(!se401->frame[framenr].curpix) { + errors++; + } + wait_interruptible( + se401->scratch[se401->scratch_use].state!=BUFFER_READY, + &se401->wq, + &wait + ); + if (se401->nullpackets > SE401_MAX_NULLPACKETS) { + se401->nullpackets=0; + info("to many null length packets, restarting capture"); + se401_stop_stream(se401); + se401_start_stream(se401); + } else { + if (se401->scratch[se401->scratch_use].state!=BUFFER_READY) { + se401->frame[framenr].grabstate=FRAME_ERROR; + return -EIO; + } + se401->scratch[se401->scratch_use].state=BUFFER_BUSY; + if (se401->format==FMT_JANGGU) { + decode_JangGu(se401, &se401->scratch[se401->scratch_use]); + } else { + decode_bayer(se401, &se401->scratch[se401->scratch_use]); + } + se401->scratch[se401->scratch_use].state=BUFFER_UNUSED; + se401->scratch_use++; + if (se401->scratch_use>=SE401_NUMSCRATCH) + se401->scratch_use=0; + if (errors > SE401_MAX_ERRORS) { + errors=0; + info("to much errors, restarting capture"); + se401_stop_stream(se401); + se401_start_stream(se401); + } + } + } + + if (se401->frame[framenr].grabstate==FRAME_DONE) + if (se401->enhance) + enhance_picture(se401->frame[framenr].data, se401->cheight*se401->cwidth*3); + return 0; +} + + +/**************************************************************************** + * + * Video4Linux + * + ***************************************************************************/ + + +static int se401_open(struct video_device *dev, int flags) +{ + struct usb_se401 *se401 = (struct usb_se401 *)dev; + int err = 0; + + MOD_INC_USE_COUNT; + down(&se401->lock); + + se401->fbuf=rvmalloc(se401->maxframesize * SE401_NUMFRAMES); + if(!se401->fbuf) err=-ENOMEM; + + if (err) { + MOD_DEC_USE_COUNT; + up(&se401->lock); + return err; + } + + se401->user=1; + + up(&se401->lock); + + return 0; +} + +static void se401_close(struct video_device *dev) +{ + struct usb_se401 *se401 = (struct usb_se401 *)dev; + int i; + + down(&se401->lock); + + for (i=0; iframe[i].grabstate=FRAME_UNUSED; + if (se401->streaming) + se401_stop_stream(se401); + + rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES); + se401->user=0; + up(&se401->lock); + + if (!se401->dev) { + video_unregister_device(&se401->vdev); + kfree(se401->width); + kfree(se401->height); + kfree(se401); + se401 = NULL; + info("device unregistered"); + } + + MOD_DEC_USE_COUNT; +} + +static int se401_init_done(struct video_device *dev) +{ +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + create_proc_se401_cam((struct usb_se401 *)dev); +#endif + + return 0; +} + +static long se401_write(struct video_device *dev, const char *buf, unsigned long + count, int noblock) +{ + return -EINVAL; +} + +static int se401_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) +{ + struct usb_se401 *se401 = (struct usb_se401 *)vdev; + + if (!se401->dev) + return -EIO; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name, se401->camera_name); + b.type = VID_TYPE_CAPTURE; + b.channels = 1; + b.audios = 0; + b.maxwidth = se401->width[se401->sizes-1]; + b.maxheight = se401->height[se401->sizes-1]; + b.minwidth = se401->width[0]; + b.minheight = se401->height[0]; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v.channel != 0) + return -EINVAL; + + v.flags = 0; + v.tuners = 0; + v.type = VIDEO_TYPE_CAMERA; + strcpy(v.name, "Camera"); + + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDIOCSCHAN: + { + int v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + if (v != 0) + return -EINVAL; + + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p; + + se401_get_pict(se401, &p); + + 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; + + if (se401_set_pict(se401, &p)) + return -EINVAL; + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + + if (copy_from_user(&vw, arg, sizeof(vw))) + return -EFAULT; + if (vw.flags) + return -EINVAL; + if (vw.clipcount) + return -EINVAL; + if (se401_set_size(se401, vw.width, vw.height)) + return -EINVAL; + + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + + vw.x = 0; /* FIXME */ + vw.y = 0; + vw.chromakey = 0; + vw.flags = 0; + vw.clipcount = 0; + vw.width = se401->cwidth; + vw.height = se401->cheight; + + if (copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + memset(&vm, 0, sizeof(vm)); + vm.size = SE401_NUMFRAMES * se401->maxframesize; + vm.frames = SE401_NUMFRAMES; + for (i=0; imaxframesize * i; + + if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) + return -EFAULT; + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (copy_from_user(&vm, arg, sizeof(vm))) + return -EFAULT; + if (vm.format != VIDEO_PALETTE_RGB24) + return -EINVAL; + if (vm.frame >= SE401_NUMFRAMES) + return -EINVAL; + if (se401->frame[vm.frame].grabstate != FRAME_UNUSED) + return -EBUSY; + + /* Is this according to the v4l spec??? */ + if (se401_set_size(se401, vm.width, vm.height)) + return -EINVAL; + se401->frame[vm.frame].grabstate=FRAME_READY; + + if (!se401->streaming) + se401_start_stream(se401); + + /* Set the picture properties */ + if (se401->framecount==0) + se401_send_pict(se401); + /* Calibrate the reset level after a few frames. */ + if (se401->framecount%20==1) + se401_auto_resetlevel(se401); + + return 0; + } + case VIDIOCSYNC: + { + int frame, ret=0; + + if (copy_from_user((void *)&frame, arg, sizeof(int))) + return -EFAULT; + + ret=se401_newframe(se401, frame); + se401->frame[frame].grabstate=FRAME_UNUSED; + return ret; + } + case VIDIOCGFBUF: + { + struct video_buffer vb; + + memset(&vb, 0, sizeof(vb)); + vb.base = NULL; /* frame buffer not supported, not used */ + + if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) + return -EFAULT; + + return 0; + } + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + return -EINVAL; + case VIDIOCSFBUF: + return -EINVAL; + case VIDIOCGTUNER: + case VIDIOCSTUNER: + return -EINVAL; + case VIDIOCGFREQ: + case VIDIOCSFREQ: + return -EINVAL; + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } /* end switch */ + + return 0; +} + +static long se401_read(struct video_device *dev, char *buf, unsigned long count, + int noblock) +{ + int realcount=count, ret=0; + struct usb_se401 *se401 = (struct usb_se401 *)dev; + + + if (se401->dev == NULL) + return -EIO; + if (realcount > se401->cwidth*se401->cheight*3) + realcount=se401->cwidth*se401->cheight*3; + + /* Shouldn't happen: */ + if (se401->frame[0].grabstate==FRAME_GRABBING) + return -EBUSY; + se401->frame[0].grabstate=FRAME_READY; + se401->frame[1].grabstate=FRAME_UNUSED; + se401->curframe=0; + + if (!se401->streaming) + se401_start_stream(se401); + + /* Set the picture properties */ + if (se401->framecount==0) + se401_send_pict(se401); + /* Calibrate the reset level after a few frames. */ + if (se401->framecount%20==1) + se401_auto_resetlevel(se401); + + ret=se401_newframe(se401, 0); + + if (!ret) { + copy_to_user(buf, se401->frame[0].data, realcount); + } else { + realcount=ret; + } + se401->frame[0].grabstate=FRAME_UNUSED; + + return realcount; +} + +static int se401_mmap(struct video_device *dev, const char *adr, + unsigned long size) +{ + struct usb_se401 *se401 = (struct usb_se401 *)dev; + unsigned long start = (unsigned long)adr; + unsigned long page, pos; + + down(&se401->lock); + + if (se401->dev == NULL) { + up(&se401->lock); + return -EIO; + } + if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { + up(&se401->lock); + return -EINVAL; + } + pos = (unsigned long)se401->fbuf; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) { + up(&se401->lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + up(&se401->lock); + + return 0; +} + +static struct video_device se401_template = { + name: "se401 USB camera", + type: VID_TYPE_CAPTURE, + hardware: VID_HARDWARE_SE401, + open: se401_open, + close: se401_close, + read: se401_read, + write: se401_write, + ioctl: se401_ioctl, + mmap: se401_mmap, + initialize: se401_init_done, +}; + + + +/***************************/ +static int se401_init(struct usb_se401 *se401) +{ + int i=0, rc; + unsigned char cp[0x40]; + char temp[200]; + + /* led on */ + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); + + /* get camera descriptor */ + rc=se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, cp, sizeof(cp)); + if (cp[1]!=0x41) { + err("Wrong descriptor type"); + return 1; + } + sprintf (temp, "ExtraFeatures: %d", cp[3]); + + se401->sizes=cp[4]+cp[5]*256; + se401->width=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); + se401->height=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); + for (i=0; isizes; i++) { + se401->width[i]=cp[6+i*4+0]+cp[6+i*4+1]*256; + se401->height[i]=cp[6+i*4+2]+cp[6+i*4+3]*256; + } + sprintf (temp, "%s Sizes:", temp); + for (i=0; isizes; i++) { + sprintf(temp, "%s %dx%d", temp, se401->width[i], se401->height[i]); + } + info("%s", temp); + se401->maxframesize=se401->width[se401->sizes-1]*se401->height[se401->sizes-1]*3; + + rc=se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp)); + se401->cwidth=cp[0]+cp[1]*256; + rc=se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp)); + se401->cheight=cp[0]+cp[1]*256; + + if (!cp[2] && SE401_FORMAT_BAYER) { + err("Bayer format not supported!"); + return 1; + } + /* set output mode (BAYER) */ + se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, SE401_FORMAT_BAYER, NULL, 0); + + rc=se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp)); + se401->brightness=cp[0]+cp[1]*256; + /* some default values */ + se401->resetlevel=0x2d; + se401->rgain=0x20; + se401->ggain=0x20; + se401->bgain=0x20; + se401_set_exposure(se401, 20000); + se401->palette=VIDEO_PALETTE_RGB24; + se401->enhance=1; + se401->dropped=0; + se401->error=0; + se401->framecount=0; + se401->readcount=0; + + /* Start interrupt transfers for snapshot button */ + se401->inturb=usb_alloc_urb(0); + if (!se401->inturb) { + info("Allocation of inturb failed"); + return 1; + } + FILL_INT_URB(se401->inturb, se401->dev, + usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT), + &se401->button, sizeof(se401->button), + se401_button_irq, + se401, + HZ/10 + ); + if (usb_submit_urb(se401->inturb)) { + info("int urb burned down"); + return 1; + } + + /* Flash the led */ + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) +static void* se401_probe(struct usb_device *dev, unsigned int ifnum) +#else +static void* __devinit se401_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) +#endif +{ + struct usb_interface_descriptor *interface; + struct usb_se401 *se401; + char *camera_name=NULL; + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return NULL; + + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + + /* Is it an se401? */ + if (dev->descriptor.idVendor == 0x03e8 && + dev->descriptor.idProduct == 0x0004) { + camera_name="Endpoints/Aox SE401"; + } else if (dev->descriptor.idVendor == 0x0471 && + dev->descriptor.idProduct == 0x030b) { + camera_name="Philips PCVC665K"; + } else if (dev->descriptor.idVendor == 0x047d && + dev->descriptor.idProduct == 0x5001) { + camera_name="Kensington VideoCAM 67014"; + } else if (dev->descriptor.idVendor == 0x047d && + dev->descriptor.idProduct == 0x5002) { + camera_name="Kensington VideoCAM 6701(5/7)"; + } else if (dev->descriptor.idVendor == 0x047d && + dev->descriptor.idProduct == 0x5003) { + camera_name="Kensington VideoCAM 67016"; + } else + return NULL; + + /* Checking vendor/product should be enough, but what the hell */ + if (interface->bInterfaceClass != 0x00) + return NULL; + if (interface->bInterfaceSubClass != 0x00) + return NULL; + + /* We found one */ + info("SE401 camera found: %s", camera_name); + + if ((se401 = kmalloc(sizeof(*se401), GFP_KERNEL)) == NULL) { + err("couldn't kmalloc se401 struct"); + return NULL; + } + + memset(se401, 0, sizeof(*se401)); + + se401->dev = dev; + se401->iface = interface->bInterfaceNumber; + se401->camera_name = camera_name; + + info("firmware version: %02x", dev->descriptor.bcdDevice & 255); + + if (se401_init(se401)) + return NULL; + memcpy(&se401->vdev, &se401_template, sizeof(se401_template)); + memcpy(se401->vdev.name, se401->camera_name, strlen(se401->camera_name)); + if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + err("video_register_device failed"); + return NULL; + } + info("registered new video device: video%d", se401->vdev.minor); + + init_waitqueue_head(&se401->wq); + init_MUTEX(&se401->lock); + + return se401; +} + +static void se401_disconnect(struct usb_device *dev, void *ptr) +{ + int i; + struct usb_se401 *se401 = (struct usb_se401 *) ptr; + + /* We don't want people trying to open up the device */ + if (!se401->user) + video_unregister_device(&se401->vdev); + + usb_driver_release_interface(&se401_driver, + &se401->dev->actconfig->interface[se401->iface]); + + se401->dev = NULL; + se401->frame[0].grabstate = FRAME_ERROR; + se401->frame[1].grabstate = FRAME_ERROR; + + se401->streaming = 0; + + if (waitqueue_active(&se401->wq)) + wake_up_interruptible(&se401->wq); + + for (i=0; iurb[i]) { + se401->urb[i]->next = NULL; + usb_unlink_urb(se401->urb[i]); + usb_free_urb(se401->urb[i]); + se401->urb[i] = NULL; + kfree(se401->sbuf[i].data); + } + for (i=0; iscratch[i].data) { + kfree(se401->scratch[i].data); + } + if (se401->inturb) { + usb_unlink_urb(se401->inturb); + usb_free_urb(se401->inturb); + } + info("%s disconnected", se401->camera_name); + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + destroy_proc_se401_cam(se401); +#endif + + /* Free the memory */ + if (!se401->user) { + kfree(se401->width); + kfree(se401->height); + kfree(se401); + se401 = NULL; + } +} + + +static struct usb_driver se401_driver = { + name: "se401", +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0) + id_table: device_table, +#endif + probe: se401_probe, + disconnect: se401_disconnect +}; + + + +/**************************************************************************** + * + * Module routines + * + ***************************************************************************/ + +static int __init usb_se401_init(void) +{ +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + proc_se401_create(); +#endif + + info("SE401 usb camera driver version %s registering", version); + if (flickerless) + if (flickerless!=50 && flickerless!=60) { + info("Invallid flickerless value, use 0, 50 or 60."); + return -1; + } + if (usb_register(&se401_driver) < 0) + return -1; + return 0; +} + +static void __exit usb_se401_exit(void) +{ + usb_deregister(&se401_driver); + info("SE401 driver deregistered"); + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + proc_se401_destroy(); +#endif +} + +module_init(usb_se401_init); +module_exit(usb_se401_exit); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/se401.h linux.ac/drivers/usb/se401.h --- linux.vanilla/drivers/usb/se401.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/se401.h Sat May 26 18:17:48 2001 @@ -0,0 +1,234 @@ + +#ifndef __LINUX_se401_H +#define __LINUX_se401_H + +#include +#include +#include + +#define se401_DEBUG /* Turn on debug messages */ + +#ifdef se401_DEBUG +# define PDEBUG(level, fmt, args...) \ +if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) +#else +# define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +/* An almost drop-in replacement for sleep_on_interruptible */ +#define wait_interruptible(test, queue, wait) \ +{ \ + add_wait_queue(queue, wait); \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (test) \ + schedule(); \ + remove_wait_queue(queue, wait); \ + set_current_state(TASK_RUNNING); \ + if (signal_pending(current)) \ + break; \ +} + +#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06 +#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41 +#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42 +#define SE401_REQ_CAPTURE_FRAME 0x43 +#define SE401_REQ_GET_BRT 0x44 +#define SE401_REQ_SET_BRT 0x45 +#define SE401_REQ_GET_WIDTH 0x4c +#define SE401_REQ_SET_WIDTH 0x4d +#define SE401_REQ_GET_HEIGHT 0x4e +#define SE401_REQ_SET_HEIGHT 0x4f +#define SE401_REQ_GET_OUTPUT_MODE 0x50 +#define SE401_REQ_SET_OUTPUT_MODE 0x51 +#define SE401_REQ_GET_EXT_FEATURE 0x52 +#define SE401_REQ_SET_EXT_FEATURE 0x53 +#define SE401_REQ_CAMERA_POWER 0x56 +#define SE401_REQ_LED_CONTROL 0x57 +#define SE401_REQ_BIOS 0xff + +#define SE401_BIOS_READ 0x07 + +#define SE401_FORMAT_BAYER 0x40 + +/* Hyundai hv7131b registers + 7121 and 7141 should be the same (haven't really checked...) */ +/* Mode registers: */ +#define HV7131_REG_MODE_A 0x00 +#define HV7131_REG_MODE_B 0x01 +#define HV7131_REG_MODE_C 0x02 +/* Frame registers: */ +#define HV7131_REG_FRSU 0x10 +#define HV7131_REG_FRSL 0x11 +#define HV7131_REG_FCSU 0x12 +#define HV7131_REG_FCSL 0x13 +#define HV7131_REG_FWHU 0x14 +#define HV7131_REG_FWHL 0x15 +#define HV7131_REG_FWWU 0x16 +#define HV7131_REG_FWWL 0x17 +/* Timing registers: */ +#define HV7131_REG_THBU 0x20 +#define HV7131_REG_THBL 0x21 +#define HV7131_REG_TVBU 0x22 +#define HV7131_REG_TVBL 0x23 +#define HV7131_REG_TITU 0x25 +#define HV7131_REG_TITM 0x26 +#define HV7131_REG_TITL 0x27 +#define HV7131_REG_TMCD 0x28 +/* Adjust Registers: */ +#define HV7131_REG_ARLV 0x30 +#define HV7131_REG_ARCG 0x31 +#define HV7131_REG_AGCG 0x32 +#define HV7131_REG_ABCG 0x33 +#define HV7131_REG_APBV 0x34 +#define HV7131_REG_ASLP 0x54 +/* Offset Registers: */ +#define HV7131_REG_OFSR 0x50 +#define HV7131_REG_OFSG 0x51 +#define HV7131_REG_OFSB 0x52 +/* REset level statistics registers: */ +#define HV7131_REG_LOREFNOH 0x57 +#define HV7131_REG_LOREFNOL 0x58 +#define HV7131_REG_HIREFNOH 0x59 +#define HV7131_REG_HIREFNOL 0x5a + +/* se401 registers */ +#define SE401_OPERATINGMODE 0x2000 + + +/* size of usb transfers */ +#define SE401_PACKETSIZE 4096 +/* number of queued bulk transfers to use, should be about 8 */ +#define SE401_NUMSBUF 1 +/* read the usb specs for this one :) */ +#define SE401_VIDEO_ENDPOINT 1 +#define SE401_BUTTON_ENDPOINT 2 +/* number of frames supported by the v4l part */ +#define SE401_NUMFRAMES 2 +/* scratch buffers for passing data to the decoders */ +#define SE401_NUMSCRATCH 32 +/* maximum amount of data in a JangGu packet */ +#define SE401_VLCDATALEN 1024 +/* number of nul sized packets to receive before kicking the camera */ +#define SE401_MAX_NULLPACKETS 4000 +/* number of decoding errors before kicking the camera */ +#define SE401_MAX_ERRORS 200 + +struct usb_device; + +struct se401_sbuf { + unsigned char *data; +}; + +enum { + FRAME_UNUSED, /* Unused (no MCAPTURE) */ + FRAME_READY, /* Ready to start grabbing */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_ERROR, /* Something bad happened while processing */ +}; + +enum { + FMT_BAYER, + FMT_JANGGU, +}; + +enum { + BUFFER_UNUSED, + BUFFER_READY, + BUFFER_BUSY, + BUFFER_DONE, +}; + +struct se401_scratch { + unsigned char *data; + volatile int state; + int offset; + int length; +}; + +struct se401_frame { + unsigned char *data; /* Frame buffer */ + + volatile int grabstate; /* State of grabbing */ + + unsigned char *curline; + int curlinepix; + int curpix; +}; + +struct usb_se401 { + struct video_device vdev; + + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + + char *camera_name; + + int change; + int brightness; + int hue; + int rgain; + int ggain; + int bgain; + int expose_h; + int expose_m; + int expose_l; + int resetlevel; + + int enhance; + + int format; + int sizes; + int *width; + int *height; + int cwidth; /* current width */ + int cheight; /* current height */ + int palette; + int maxframesize; + int cframesize; /* current framesize */ + + struct semaphore lock; + int user; /* user count for exclusive use */ + + int streaming; /* Are we streaming video? */ + + char *fbuf; /* Videodev buffer area */ + + urb_t *urb[SE401_NUMSBUF]; + urb_t *inturb; + + int button; + int buttonpressed; + + int curframe; /* Current receiving frame */ + struct se401_frame frame[SE401_NUMFRAMES]; + int readcount; + int framecount; + int error; + int dropped; + + int scratch_next; + int scratch_use; + int scratch_overflow; + struct se401_scratch scratch[SE401_NUMSCRATCH]; + + /* Decoder specific data: */ + unsigned char vlcdata[SE401_VLCDATALEN]; + int vlcdatapos; + int bayeroffset; + + struct se401_sbuf sbuf[SE401_NUMSBUF]; + + wait_queue_head_t wq; /* Processes waiting */ + + /* proc interface */ + struct proc_dir_entry *proc_entry; /* /proc/se401/videoX */ + + int nullpackets; +}; + + +#endif + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/Config.in linux.ac/drivers/usb/serial/Config.in --- linux.vanilla/drivers/usb/serial/Config.in Mon Apr 30 15:13:27 2001 +++ linux.ac/drivers/usb/serial/Config.in Sun Apr 22 01:58:23 2001 @@ -4,7 +4,7 @@ mainmenu_option next_comment comment 'USB Serial Converter support' -tristate 'USB Serial Converter support' CONFIG_USB_SERIAL $CONFIG_USB +dep_tristate 'USB Serial Converter support' CONFIG_USB_SERIAL $CONFIG_USB if [ "$CONFIG_USB_SERIAL" != "n" ]; then if [ "$CONFIG_USB_SERIAL" = "y" ]; then bool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/io_edgeport.c linux.ac/drivers/usb/serial/io_edgeport.c --- linux.vanilla/drivers/usb/serial/io_edgeport.c Sat May 26 16:53:15 2001 +++ linux.ac/drivers/usb/serial/io_edgeport.c Thu May 24 23:21:43 2001 @@ -944,7 +944,7 @@ } if (status) { - dbg(__FUNCTION__" - nonzero write bulk status received: %d", urb->status); + dbg(__FUNCTION__" - nonzero write bulk status received: %d", status); return; } diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/serial/usbserial.c linux.ac/drivers/usb/serial/usbserial.c --- linux.vanilla/drivers/usb/serial/usbserial.c Sat May 26 16:53:16 2001 +++ linux.ac/drivers/usb/serial/usbserial.c Wed May 23 00:15:37 2001 @@ -304,10 +304,10 @@ static int generic_chars_in_buffer (struct usb_serial_port *port); static void generic_read_bulk_callback (struct urb *urb); static void generic_write_bulk_callback (struct urb *urb); -static void generic_shutdown (struct usb_serial *serial); #ifdef CONFIG_USB_SERIAL_GENERIC +static void generic_shutdown (struct usb_serial *serial); static __u16 vendor = 0x05f9; static __u16 product = 0xffff; @@ -970,6 +970,7 @@ } +#ifdef CONFIG_USB_SERIAL_GENERIC static void generic_shutdown (struct usb_serial *serial) { int i; @@ -983,6 +984,7 @@ } } } +#endif static void port_softint(void *private) diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/storage/freecom.c linux.ac/drivers/usb/storage/freecom.c --- linux.vanilla/drivers/usb/storage/freecom.c Wed Nov 29 05:50:07 2000 +++ linux.ac/drivers/usb/storage/freecom.c Sun Apr 22 01:58:23 2001 @@ -96,7 +96,7 @@ #define FCM_PACKET_OUTPUT 0x01 /* Write a value to an ide register. Or the ide register to write after - * munging the addres a bit. */ + * munging the address a bit. */ #define FCM_PACKET_IDE_WRITE 0x40 #define FCM_PACKET_IDE_READ 0xC0 @@ -412,7 +412,7 @@ US_DEBUG(pdump ((void *) fst, partial)); - /* while we haven't recieved the IRQ */ + /* while we haven't received the IRQ */ while (!(fst->Status & 0x2)) { /* send a command to re-fetch the status */ US_DEBUGP("Re-attempting to get status...\n"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/storage/scsiglue.c linux.ac/drivers/usb/storage/scsiglue.c --- linux.vanilla/drivers/usb/storage/scsiglue.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/usb/storage/scsiglue.c Sun Apr 22 01:58:23 2001 @@ -122,7 +122,8 @@ */ US_DEBUGP("-- sending US_ACT_EXIT command to thread\n"); us->action = US_ACT_EXIT; - wake_up(&(us->wqh)); + + up(&(us->sema)); down(&(us->notify)); /* remove the pointer to the data structure we were using */ @@ -160,7 +161,7 @@ up(&(us->queue_exclusion)); /* wake up the process task */ - wake_up(&(us->wqh)); + up(&(us->sema)); return 0; } @@ -331,7 +332,7 @@ return -ESRCH; } - /* print the controler name */ + /* print the controller name */ SPRINTF(" Host scsi%d: usb-storage\n", hostno); /* print product, vendor, and serial number strings */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/storage/unusual_devs.h linux.ac/drivers/usb/storage/unusual_devs.h --- linux.vanilla/drivers/usb/storage/unusual_devs.h Sat May 26 16:53:16 2001 +++ linux.ac/drivers/usb/storage/unusual_devs.h Wed May 2 15:10:52 2001 @@ -54,6 +54,11 @@ US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), #endif +UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x2210, + "Fujifilm", + "FinePix 1400Zoom", + US_SC_8070, US_PR_CBI, NULL, US_FL_FIX_INQUIRY), + UNUSUAL_DEV( 0x04e6, 0x0001, 0x0200, 0x0200, "Matshita", "LS-120", @@ -138,6 +143,12 @@ US_SC_UFI, US_PR_CB, NULL, US_FL_SINGLE_LUN | US_FL_START_STOP ), +UNUSUAL_DEV( 0x054c, 0x0032, 0x0000, 0x9999, + "Sony", + "Memorystick MSC-U01N", + US_SC_UFI, US_PR_CB, NULL, + US_FL_SINGLE_LUN | US_FL_START_STOP ), + UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data", "Flashbuster-U", @@ -235,4 +246,10 @@ US_SC_SCSI, US_PR_DPCM_USB, NULL, US_FL_START_STOP ), #endif + +UNUSUAL_DEV( 0x07cf, 0x1001, 0x9009, 0x9009, + "Casio", + "QV DigitalCamera", + US_SC_8070, US_PR_CB, NULL, + US_FL_FIX_INQUIRY ), diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/storage/usb.c linux.ac/drivers/usb/storage/usb.c --- linux.vanilla/drivers/usb/storage/usb.c Fri Feb 9 19:30:23 2001 +++ linux.ac/drivers/usb/storage/usb.c Sun Apr 22 01:58:23 2001 @@ -281,7 +281,6 @@ static int usb_stor_control_thread(void * __us) { - wait_queue_t wait; struct us_data *us = (struct us_data *)__us; int action; @@ -302,9 +301,7 @@ unlock_kernel(); /* set up for wakeups by new commands */ - init_waitqueue_entry(&wait, current); - init_waitqueue_head(&(us->wqh)); - add_wait_queue(&(us->wqh), &wait); + init_MUTEX_LOCKED(&us->sema); /* signal that we've started the thread */ up(&(us->notify)); @@ -312,7 +309,9 @@ for(;;) { US_DEBUGP("*** thread sleeping.\n"); - schedule(); + if(down_interruptible(&us->sema)) + break; + US_DEBUGP("*** thread awakened.\n"); /* lock access to the queue element */ @@ -378,6 +377,22 @@ break; } + if ((us->srb->cmnd[0] == INQUIRY) && + (us->flags & US_FL_FIX_INQUIRY)) { + unsigned char data_ptr[36] = { + 0x00, 0x80, 0x02, 0x02, 0x1F, 0x00, 0x00, 0x00}; + + US_DEBUGP("Faking INQUIRY command\n"); + fill_inquiry_response(us, data_ptr, 36); + us->srb->result = GOOD << 1; + + set_current_state(TASK_INTERRUPTIBLE); + us->srb->scsi_done(us->srb); + us->srb = NULL; + break; + } + + /* lock the device pointers */ down(&(us->dev_semaphore)); @@ -435,14 +450,13 @@ /* exit if we get a signal to exit */ if (action == US_ACT_EXIT) { - US_DEBUGP("-- US_ACT_EXIT command recieved\n"); + US_DEBUGP("-- US_ACT_EXIT command received\n"); break; } } /* for (;;) */ /* clean up after ourselves */ set_current_state(TASK_INTERRUPTIBLE); - remove_wait_queue(&(us->wqh), &wait); /* notify the exit routine that we're actually exiting now */ up(&(us->notify)); @@ -907,7 +921,7 @@ ss->host_number = my_host_number++; /* We abuse this pointer so we can pass the ss pointer to - * the host controler thread in us_detect. But how else are + * the host controller thread in us_detect. But how else are * we to do it? */ (struct us_data *)ss->htmplt.proc_dir = ss; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/storage/usb.h linux.ac/drivers/usb/storage/usb.h --- linux.vanilla/drivers/usb/storage/usb.h Tue Apr 3 17:32:24 2001 +++ linux.ac/drivers/usb/storage/usb.h Sat May 26 18:18:40 2001 @@ -94,11 +94,12 @@ /* Flag definitions */ #define US_FL_SINGLE_LUN 0x00000001 /* allow access to only LUN 0 */ -#define US_FL_MODE_XLATE 0x00000002 /* translate _6 to _10 comands for +#define US_FL_MODE_XLATE 0x00000002 /* translate _6 to _10 commands for Win/MacOS compatibility */ #define US_FL_START_STOP 0x00000004 /* ignore START_STOP commands */ #define US_FL_IGNORE_SER 0x00000010 /* Ignore the serial number given */ #define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */ +#define US_FL_FIX_INQUIRY 0x00000040 /* INQUIRY response needs fixing */ #define USB_STOR_STRING_LEN 32 @@ -165,8 +166,8 @@ struct semaphore current_urb_sem; /* to protect irq_urb */ struct urb *current_urb; /* non-int USB requests */ - /* the waitqueue for sleeping the control thread */ - wait_queue_head_t wqh; /* to sleep thread on */ + /* the semaphore for sleeping the control thread */ + struct semaphore sema; /* to sleep thread on */ /* mutual exclusion structures */ struct semaphore notify; /* thread begin/end */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/ultracam.c linux.ac/drivers/usb/ultracam.c --- linux.vanilla/drivers/usb/ultracam.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/ultracam.c Sun Apr 22 01:58:23 2001 @@ -0,0 +1,708 @@ +/* + * USB NB Camera driver + */ + +#include +#include +#include +#include +#include + +#include "usbvideo.h" + +#define ULTRACAM_VENDOR_ID 0x0461 +#define ULTRACAM_PRODUCT_ID 0x0813 + +#define MAX_CAMERAS 4 /* How many devices we allow to connect */ + +/* + * This structure lives in uvd_t->user field. + */ +typedef struct { + int initialized; /* Had we already sent init sequence? */ + int camera_model; /* What type of IBM camera we got? */ + int has_hdr; +} ultracam_t; +#define ULTRACAM_T(uvd) ((ultracam_t *)((uvd)->user_data)) + +usbvideo_t *cams = NULL; + +static int debug = 0; + +static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */ + +static const int min_canvasWidth = 8; +static const int min_canvasHeight = 4; + +//static int lighting = 1; /* Medium */ + +#define SHARPNESS_MIN 0 +#define SHARPNESS_MAX 6 +//static int sharpness = 4; /* Low noise, good details */ + +#define FRAMERATE_MIN 0 +#define FRAMERATE_MAX 6 +static int framerate = -1; + +/* + * Here we define several initialization variables. They may + * be used to automatically set color, hue, brightness and + * contrast to desired values. This is particularly useful in + * case of webcams (which have no controls and no on-screen + * output) and also when a client V4L software is used that + * does not have some of those controls. In any case it's + * good to have startup values as options. + * + * These values are all in [0..255] range. This simplifies + * operation. Note that actual values of V4L variables may + * be scaled up (as much as << 8). User can see that only + * on overlay output, however, or through a V4L client. + */ +static int init_brightness = 128; +static int init_contrast = 192; +static int init_color = 128; +static int init_hue = 128; +static int hue_correction = 128; + +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); +MODULE_PARM(flags, "i"); +MODULE_PARM_DESC(flags, + "Bitfield: 0=VIDIOCSYNC, " + "1=B/W, " + "2=show hints, " + "3=show stats, " + "4=test pattern, " + "5=separate frames, " + "6=clean frames"); +MODULE_PARM(framerate, "i"); +MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)"); +MODULE_PARM(lighting, "i"); +MODULE_PARM_DESC(lighting, "Photosensitivity: 0=bright, 1=medium (default), 2=low light"); +MODULE_PARM(sharpness, "i"); +MODULE_PARM_DESC(sharpness, "Model1 noise reduction: 0=smooth, 6=sharp (default=4)"); + +MODULE_PARM(init_brightness, "i"); +MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)"); +MODULE_PARM(init_contrast, "i"); +MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)"); +MODULE_PARM(init_color, "i"); +MODULE_PARM_DESC(init_color, "Color preconfiguration: 0-255 (default=128)"); +MODULE_PARM(init_hue, "i"); +MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)"); +MODULE_PARM(hue_correction, "i"); +MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)"); + +/* + * ultracam_ProcessIsocData() + * + * Generic routine to parse the ring queue data. It employs either + * ultracam_find_header() or ultracam_parse_lines() to do most + * of work. + * + * 02-Nov-2000 First (mostly dummy) version. + * 06-Nov-2000 Rewrote to dump all data into frame. + */ +void ultracam_ProcessIsocData(uvd_t *uvd, usbvideo_frame_t *frame) +{ + int n; + + assert(uvd != NULL); + assert(frame != NULL); + + /* Try to move data from queue into frame buffer */ + n = RingQueue_GetLength(&uvd->dp); + if (n > 0) { + int m; + /* See how much spare we have left */ + m = uvd->max_frame_size - frame->seqRead_Length; + if (n > m) + n = m; + /* Now move that much data into frame buffer */ + RingQueue_Dequeue( + &uvd->dp, + frame->data + frame->seqRead_Length, + m); + frame->seqRead_Length += m; + } + /* See if we filled the frame */ + if (frame->seqRead_Length >= uvd->max_frame_size) { + frame->frameState = FrameState_Done; + uvd->curframe = -1; + uvd->stats.frame_num++; + } +} + +/* + * ultracam_veio() + * + * History: + * 1/27/00 Added check for dev == NULL; this happens if camera is unplugged. + */ +static int ultracam_veio( + uvd_t *uvd, + unsigned char req, + unsigned short value, + unsigned short index, + int is_out) +{ + static const char proc[] = "ultracam_veio"; + unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */; + int i; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return 0; + + if (!is_out) { + i = usb_control_msg( + uvd->dev, + usb_rcvctrlpipe(uvd->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + cp, + sizeof(cp), + HZ); +#if 1 + info("USB => %02x%02x%02x%02x%02x%02x%02x%02x " + "(req=$%02x val=$%04x ind=$%04x)", + cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7], + req, value, index); +#endif + } else { + i = usb_control_msg( + uvd->dev, + usb_sndctrlpipe(uvd->dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + NULL, + 0, + HZ); + } + if (i < 0) { + err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.", + proc, i); + uvd->last_error = i; + } + return i; +} + +/* + * ultracam_calculate_fps() + */ +static int ultracam_calculate_fps(uvd_t *uvd) +{ + return 3 + framerate*4 + framerate/2; +} + +/* + * ultracam_adjust_contrast() + */ +static void ultracam_adjust_contrast(uvd_t *uvd) +{ +} + +/* + * ultracam_change_lighting_conditions() + */ +static void ultracam_change_lighting_conditions(uvd_t *uvd) +{ +} + +/* + * ultracam_set_sharpness() + * + * Cameras model 1 have internal smoothing feature. It is controlled by value in + * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess). + * Recommended value is 4. Cameras model 2 do not have this feature at all. + */ +static void ultracam_set_sharpness(uvd_t *uvd) +{ +} + +/* + * ultracam_set_brightness() + * + * This procedure changes brightness of the picture. + */ +static void ultracam_set_brightness(uvd_t *uvd) +{ +} + +static void ultracam_set_hue(uvd_t *uvd) +{ +} + +/* + * ultracam_adjust_picture() + * + * This procedure gets called from V4L interface to update picture settings. + * Here we change brightness and contrast. + */ +static void ultracam_adjust_picture(uvd_t *uvd) +{ + ultracam_adjust_contrast(uvd); + ultracam_set_brightness(uvd); + ultracam_set_hue(uvd); +} + +/* + * ultracam_video_stop() + * + * This code tells camera to stop streaming. The interface remains + * configured and bandwidth - claimed. + */ +static void ultracam_video_stop(uvd_t *uvd) +{ +} + +/* + * ultracam_reinit_iso() + * + * This procedure sends couple of commands to the camera and then + * resets the video pipe. This sequence was observed to reinit the + * camera or, at least, to initiate ISO data stream. + */ +static void ultracam_reinit_iso(uvd_t *uvd, int do_stop) +{ +} + +static void ultracam_video_start(uvd_t *uvd) +{ + ultracam_change_lighting_conditions(uvd); + ultracam_set_sharpness(uvd); + ultracam_reinit_iso(uvd, 0); +} + +static int ultracam_resetPipe(uvd_t *uvd) +{ + usb_clear_halt(uvd->dev, uvd->video_endp); + return 0; +} + +static int ultracam_alternateSetting(uvd_t *uvd, int setting) +{ + static const char proc[] = "ultracam_alternateSetting"; + int i; + i = usb_set_interface(uvd->dev, uvd->iface, setting); + if (i < 0) { + err("%s: usb_set_interface error", proc); + uvd->last_error = i; + return -EBUSY; + } + return 0; +} + +/* + * Return negative code on failure, 0 on success. + */ +static int ultracam_setup_on_open(uvd_t *uvd) +{ + int setup_ok = 0; /* Success by default */ + /* Send init sequence only once, it's large! */ + if (!ULTRACAM_T(uvd)->initialized) { + ultracam_alternateSetting(uvd, 0x04); + ultracam_alternateSetting(uvd, 0x00); + ultracam_veio(uvd, 0x02, 0x0004, 0x000b, 1); + ultracam_veio(uvd, 0x02, 0x0001, 0x0005, 1); + ultracam_veio(uvd, 0x02, 0x8000, 0x0000, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1); + ultracam_veio(uvd, 0x00, 0x00b0, 0x0001, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0002, 1); + ultracam_veio(uvd, 0x00, 0x000c, 0x0003, 1); + ultracam_veio(uvd, 0x00, 0x000b, 0x0004, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0005, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0006, 1); + ultracam_veio(uvd, 0x00, 0x0079, 0x0007, 1); + ultracam_veio(uvd, 0x00, 0x003b, 0x0008, 1); + ultracam_veio(uvd, 0x00, 0x0002, 0x000f, 1); + ultracam_veio(uvd, 0x00, 0x0001, 0x0010, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0011, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00bf, 1); + ultracam_veio(uvd, 0x00, 0x0001, 0x00c0, 1); + ultracam_veio(uvd, 0x00, 0x0010, 0x00cb, 1); + ultracam_veio(uvd, 0x01, 0x00a4, 0x0001, 1); + ultracam_veio(uvd, 0x01, 0x0010, 0x0002, 1); + ultracam_veio(uvd, 0x01, 0x0066, 0x0007, 1); + ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1); + ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1); + ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1); + ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1); + ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1); + ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1); + ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1); + ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1); + ultracam_veio(uvd, 0x01, 0x000b, 0x0011, 1); + ultracam_veio(uvd, 0x01, 0x0001, 0x0012, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0013, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0014, 1); + ultracam_veio(uvd, 0x01, 0x0087, 0x0051, 1); + ultracam_veio(uvd, 0x01, 0x0040, 0x0052, 1); + ultracam_veio(uvd, 0x01, 0x0058, 0x0053, 1); + ultracam_veio(uvd, 0x01, 0x0040, 0x0054, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0040, 1); + ultracam_veio(uvd, 0x01, 0x0010, 0x0041, 1); + ultracam_veio(uvd, 0x01, 0x0020, 0x0042, 1); + ultracam_veio(uvd, 0x01, 0x0030, 0x0043, 1); + ultracam_veio(uvd, 0x01, 0x0040, 0x0044, 1); + ultracam_veio(uvd, 0x01, 0x0050, 0x0045, 1); + ultracam_veio(uvd, 0x01, 0x0060, 0x0046, 1); + ultracam_veio(uvd, 0x01, 0x0070, 0x0047, 1); + ultracam_veio(uvd, 0x01, 0x0080, 0x0048, 1); + ultracam_veio(uvd, 0x01, 0x0090, 0x0049, 1); + ultracam_veio(uvd, 0x01, 0x00a0, 0x004a, 1); + ultracam_veio(uvd, 0x01, 0x00b0, 0x004b, 1); + ultracam_veio(uvd, 0x01, 0x00c0, 0x004c, 1); + ultracam_veio(uvd, 0x01, 0x00d0, 0x004d, 1); + ultracam_veio(uvd, 0x01, 0x00e0, 0x004e, 1); + ultracam_veio(uvd, 0x01, 0x00f0, 0x004f, 1); + ultracam_veio(uvd, 0x01, 0x00ff, 0x0050, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0056, 1); + ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0004, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0002, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0020, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0040, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0017, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x00c0, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x02, 0xc040, 0x0001, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0008, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x0009, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000b, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000c, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000d, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000e, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000f, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x0010, 0); + ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1); + ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1); + ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1); + ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1); + ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1); + ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1); + ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1); + ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0001, 0); + ultracam_veio(uvd, 0x01, 0x0064, 0x0001, 1); + ultracam_veio(uvd, 0x01, 0x0059, 0x0051, 1); + ultracam_veio(uvd, 0x01, 0x003f, 0x0052, 1); + ultracam_veio(uvd, 0x01, 0x0094, 0x0053, 1); + ultracam_veio(uvd, 0x01, 0x00ff, 0x0011, 1); + ultracam_veio(uvd, 0x01, 0x0003, 0x0012, 1); + ultracam_veio(uvd, 0x01, 0x00f7, 0x0013, 1); + ultracam_veio(uvd, 0x00, 0x0009, 0x0011, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0001, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1); + ultracam_veio(uvd, 0x00, 0x0020, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0010, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_alternateSetting(uvd, 0x04); + ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1); + ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1); + ultracam_veio(uvd, 0x02, 0x0000, 0x0006, 1); + ultracam_veio(uvd, 0x02, 0x9000, 0x0007, 1); + ultracam_veio(uvd, 0x02, 0x0042, 0x0001, 1); + ultracam_veio(uvd, 0x02, 0x0000, 0x000b, 0); + ultracam_resetPipe(uvd); + ULTRACAM_T(uvd)->initialized = (setup_ok != 0); + } + return setup_ok; +} + +static void ultracam_configure_video(uvd_t *uvd) +{ + if (uvd == NULL) + return; + + RESTRICT_TO_RANGE(init_brightness, 0, 255); + RESTRICT_TO_RANGE(init_contrast, 0, 255); + RESTRICT_TO_RANGE(init_color, 0, 255); + RESTRICT_TO_RANGE(init_hue, 0, 255); + RESTRICT_TO_RANGE(hue_correction, 0, 255); + + memset(&uvd->vpic, 0, sizeof(uvd->vpic)); + memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); + + uvd->vpic.colour = init_color << 8; + uvd->vpic.hue = init_hue << 8; + uvd->vpic.brightness = init_brightness << 8; + uvd->vpic.contrast = init_contrast << 8; + uvd->vpic.whiteness = 105 << 8; /* This one isn't used */ + uvd->vpic.depth = 24; + uvd->vpic.palette = VIDEO_PALETTE_RGB24; + + memset(&uvd->vcap, 0, sizeof(uvd->vcap)); + strcpy(uvd->vcap.name, "IBM Ultra Camera"); + uvd->vcap.type = VID_TYPE_CAPTURE; + uvd->vcap.channels = 1; + uvd->vcap.audios = 0; + uvd->vcap.maxwidth = VIDEOSIZE_X(uvd->canvas); + uvd->vcap.maxheight = VIDEOSIZE_Y(uvd->canvas); + uvd->vcap.minwidth = min_canvasWidth; + uvd->vcap.minheight = min_canvasHeight; + + memset(&uvd->vchan, 0, sizeof(uvd->vchan)); + uvd->vchan.flags = 0; + uvd->vchan.tuners = 0; + uvd->vchan.channel = 0; + uvd->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(uvd->vchan.name, "Camera"); +} + +/* + * ultracam_probe() + * + * This procedure queries device descriptor and accepts the interface + * if it looks like our camera. + * + * History: + * 12-Nov-2000 Reworked to comply with new probe() signature. + * 23-Jan-2001 Added compatibility with 2.2.x kernels. + */ +static void *ultracam_probe(struct usb_device *dev, unsigned int ifnum +#if defined(usb_device_id_ver) + ,const struct usb_device_id *devid +#endif + ) +{ + uvd_t *uvd = NULL; + int i, nas; + int actInterface=-1, inactInterface=-1, maxPS=0; + unsigned char video_ep = 0; + + if (debug >= 1) + info("ultracam_probe(%p,%u.)", dev, ifnum); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return NULL; + + /* Is it an IBM camera? */ + if ((dev->descriptor.idVendor != ULTRACAM_VENDOR_ID) || + (dev->descriptor.idProduct != ULTRACAM_PRODUCT_ID)) + return NULL; + + info("IBM Ultra camera found (rev. 0x%04x)", dev->descriptor.bcdDevice); + + /* Validate found interface: must have one ISO endpoint */ + nas = dev->actconfig->interface[ifnum].num_altsetting; + if (debug > 0) + info("Number of alternate settings=%d.", nas); + if (nas < 8) { + err("Too few alternate settings for this camera!"); + return NULL; + } + /* Validate all alternate settings */ + for (i=0; i < nas; i++) { + const struct usb_interface_descriptor *interface; + const struct usb_endpoint_descriptor *endpoint; + + interface = &dev->actconfig->interface[ifnum].altsetting[i]; + if (interface->bNumEndpoints != 1) { + err("Interface %d. has %u. endpoints!", + ifnum, (unsigned)(interface->bNumEndpoints)); + return NULL; + } + endpoint = &interface->endpoint[0]; + if (video_ep == 0) + video_ep = endpoint->bEndpointAddress; + else if (video_ep != endpoint->bEndpointAddress) { + err("Alternate settings have different endpoint addresses!"); + return NULL; + } + if ((endpoint->bmAttributes & 0x03) != 0x01) { + err("Interface %d. has non-ISO endpoint!", ifnum); + return NULL; + } + if ((endpoint->bEndpointAddress & 0x80) == 0) { + err("Interface %d. has ISO OUT endpoint!", ifnum); + return NULL; + } + if (endpoint->wMaxPacketSize == 0) { + if (inactInterface < 0) + inactInterface = i; + else { + err("More than one inactive alt. setting!"); + return NULL; + } + } else { + if (actInterface < 0) { + actInterface = i; + maxPS = endpoint->wMaxPacketSize; + if (debug > 0) + info("Active setting=%d. maxPS=%d.", i, maxPS); + } else { + /* Got another active alt. setting */ + if (maxPS < endpoint->wMaxPacketSize) { + /* This one is better! */ + actInterface = i; + maxPS = endpoint->wMaxPacketSize; + if (debug > 0) { + info("Even better ctive setting=%d. maxPS=%d.", + i, maxPS); + } + } + } + } + } + if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) { + err("Failed to recognize the camera!"); + return NULL; + } + + /* Code below may sleep, need to lock module while we are here */ + MOD_INC_USE_COUNT; + uvd = usbvideo_AllocateDevice(cams); + if (uvd != NULL) { + /* Here uvd is a fully allocated uvd_t object */ + uvd->flags = flags; + uvd->debug = debug; + uvd->dev = dev; + uvd->iface = ifnum; + uvd->ifaceAltInactive = inactInterface; + uvd->ifaceAltActive = actInterface; + uvd->video_endp = video_ep; + uvd->iso_packet_len = maxPS; + uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24; + uvd->defaultPalette = VIDEO_PALETTE_RGB24; + uvd->canvas = VIDEOSIZE(640, 480); /* FIXME */ + uvd->videosize = uvd->canvas; /* ultracam_size_to_videosize(size);*/ + + /* Initialize ibmcam-specific data */ + assert(ULTRACAM_T(uvd) != NULL); + ULTRACAM_T(uvd)->camera_model = 0; /* Not used yet */ + ULTRACAM_T(uvd)->initialized = 0; + + ultracam_configure_video(uvd); + + i = usbvideo_RegisterVideoDevice(uvd); + if (i != 0) { + err("usbvideo_RegisterVideoDevice() failed."); + uvd = NULL; + } + } + MOD_DEC_USE_COUNT; + return uvd; +} + +/* + * ultracam_init() + * + * This code is run to initialize the driver. + */ +static int __init ultracam_init(void) +{ + usbvideo_cb_t cbTbl; + memset(&cbTbl, 0, sizeof(cbTbl)); + cbTbl.probe = ultracam_probe; + cbTbl.setupOnOpen = ultracam_setup_on_open; + cbTbl.videoStart = ultracam_video_start; + cbTbl.videoStop = ultracam_video_stop; + cbTbl.processData = ultracam_ProcessIsocData; + cbTbl.postProcess = usbvideo_DeinterlaceFrame; + cbTbl.adjustPicture = ultracam_adjust_picture; + cbTbl.getFPS = ultracam_calculate_fps; + return usbvideo_register( + &cams, + MAX_CAMERAS, + sizeof(ultracam_t), + "ultracam", + &cbTbl, + THIS_MODULE); +} + +static void __exit ultracam_cleanup(void) +{ + usbvideo_Deregister(&cams); +} + +#if defined(usb_device_id_ver) + +static __devinitdata struct usb_device_id id_table[] = { + { USB_DEVICE(ULTRACAM_VENDOR_ID, ULTRACAM_PRODUCT_ID) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, id_table); + +#endif /* defined(usb_device_id_ver) */ + +module_init(ultracam_init); +module_exit(ultracam_cleanup); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb-uhci.c linux.ac/drivers/usb/usb-uhci.c --- linux.vanilla/drivers/usb/usb-uhci.c Sat May 26 16:53:17 2001 +++ linux.ac/drivers/usb/usb-uhci.c Wed May 23 00:15:50 2001 @@ -2992,8 +2992,9 @@ if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) { err("request_irq %d failed!",irq); - usb_free_bus (bus); reset_hc (s); + usb_deregister_bus (s->bus); + usb_free_bus (bus); release_region (s->io_addr, s->io_size); cleanup_skel(s); kfree(s); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usb.c linux.ac/drivers/usb/usb.c --- linux.vanilla/drivers/usb/usb.c Sat May 26 16:53:17 2001 +++ linux.ac/drivers/usb/usb.c Thu May 17 20:27:16 2001 @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef CONFIG_USB_DEBUG #define DEBUG @@ -59,6 +60,7 @@ */ LIST_HEAD(usb_driver_list); LIST_HEAD(usb_bus_list); +rwlock_t usb_bus_list_lock = RW_LOCK_UNLOCKED; devfs_handle_t usb_devfs_handle; /* /dev/usb dir. */ @@ -110,6 +112,7 @@ { struct list_head *tmp; + read_lock_irq (&usb_bus_list_lock); tmp = usb_bus_list.next; while (tmp != &usb_bus_list) { struct usb_bus *bus = list_entry(tmp,struct usb_bus, bus_list); @@ -117,6 +120,7 @@ tmp = tmp->next; usb_check_support(bus->root_hub); } + read_unlock_irq (&usb_bus_list_lock); } /* @@ -178,6 +182,7 @@ */ list_del(&driver->driver_list); + read_lock_irq (&usb_bus_list_lock); tmp = usb_bus_list.next; while (tmp != &usb_bus_list) { struct usb_bus *bus = list_entry(tmp,struct usb_bus,bus_list); @@ -185,6 +190,7 @@ tmp = tmp->next; usb_drivers_purge(driver, bus->root_hub); } + read_unlock_irq (&usb_bus_list_lock); } struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) @@ -415,6 +421,7 @@ { int busnum; + write_lock_irq (&usb_bus_list_lock); busnum = find_next_zero_bit(busmap.busmap, USB_MAXBUS, 1); if (busnum < USB_MAXBUS) { set_bit(busnum, busmap.busmap); @@ -426,6 +433,7 @@ /* Add it to the list of buses */ list_add(&bus->bus_list, &usb_bus_list); + write_unlock_irq (&usb_bus_list_lock); usbdevfs_add_bus(bus); @@ -447,7 +455,9 @@ * controller code, as well as having it call this when cleaning * itself up */ + write_lock_irq (&usb_bus_list_lock); list_del(&bus->bus_list); + write_unlock_irq (&usb_bus_list_lock); usbdevfs_remove_bus(bus); @@ -700,6 +710,10 @@ tmp = tmp->next; down(&driver->serialize); + if (usb_interface_claimed(interface)) { + up(&driver->serialize); + return -1; + } id = driver->id_table; /* new style driver? */ if (id) { @@ -719,11 +733,13 @@ else /* "old style" driver */ private = driver->probe(dev, ifnum, NULL); - up(&driver->serialize); - if (private) { - usb_driver_claim_interface(driver, interface, private); - return 0; + if (!private) { + up(&driver->serialize); + continue; } + usb_driver_claim_interface(driver, interface, private); + up(&driver->serialize); + return 0; } return -1; @@ -1157,7 +1173,7 @@ * @pipe: endpoint "pipe" to send the message to * @data: pointer to the data to send * @len: length in bytes of the data to send - * @actual_length: pointer to a location to put the actual length transfered in bytes + * @actual_length: pointer to a location to put the actual length transferred in bytes * @timeout: time to wait for the message to complete before timing out (if 0 the wait is forever) * * This function sends a simple bulk message to a specified endpoint @@ -1744,8 +1760,11 @@ * These are the actual routines to send * and receive control messages. */ - +#ifdef CONFIG_USB_LONG_TIMEOUT +#define GET_TIMEOUT 4 +#else #define GET_TIMEOUT 3 +#endif #define SET_TIMEOUT 3 int usb_set_address(struct usb_device *dev) @@ -1970,7 +1989,11 @@ { int result; unsigned int cfgno, length; +#ifdef CONFIG_USB_LARGE_CONFIG + unsigned char buffer[1009]; +#else unsigned char buffer[8]; +#endif unsigned char *bigbuffer; struct usb_config_descriptor *desc = (struct usb_config_descriptor *)buffer; @@ -2005,7 +2028,11 @@ for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) { /* 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); +#ifdef CONFIG_USB_LARGE_CONFIG + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 1009); +#else + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8); +#endif if (result < 8) { if (result < 0) err("unable to get descriptor"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usbnet.c linux.ac/drivers/usb/usbnet.c --- linux.vanilla/drivers/usb/usbnet.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/usbnet.c Sun Apr 22 01:58:23 2001 @@ -0,0 +1,1527 @@ +/* + * USB Host-to-Host Links + * Copyright (C) 2000-2001 by David Brownell + */ + +/* + * This is used for "USB networking", connecting USB hosts as peers. + * + * It can be used with USB "network cables", for IP-over-USB communications; + * Ethernet speeds without the Ethernet. USB devices (including some PDAs) + * can support such links directly, replacing device-specific protocols + * with Internet standard ones. + * + * The links can be bridged using the Ethernet bridging (net/bridge) + * support as appropriate. Devices currently supported include: + * + * - AnchorChip 2720 + * - "Linux Devices" (like iPaq and similar SA-1100 based PDAs) + * - NetChip 1080 (interoperates with NetChip Win32 drivers) + * - Prolific PL-2301/2302 (replaces "plusb" driver) + * + * USB devices can implement their side of this protocol at the cost + * of two bulk endpoints; it's not restricted to "cable" applications. + * See the LINUXDEV support. + * + * + * TODO: + * + * This needs to be retested for bulk queuing problems ... earlier versions + * seemed to find different types of problems in each HCD. Once they're fixed, + * re-enable queues to get higher bandwidth utilization (without needing + * to tweak MTU for larger packets). + * + * Add support for more "network cable" chips; interop with their Win32 + * drivers may be a good thing. Test the AnchorChip 2720 support.. + * Figure out the initialization protocol used by the Prolific chips, + * for better robustness. + * + * Use interrupt on PL230x to detect peer connect/disconnect, and call + * netif_carrier_{on,off} (?) appropriately. For Net1080, detect peer + * connect/disconnect with async control messages. + * + * Find some way to report "peer connected" network hotplug events; it'll + * likely mean updating the networking layer. + * + * Craft smarter hotplug policy scripts ... ones that know how to arrange + * bridging with "brctl", and can handle static and dynamic ("pump") setups. + * Use those "peer connected" events. + * + * + * CHANGELOG: + * + * 13-sep-2000 experimental, new + * 10-oct-2000 usb_device_id table created. + * 28-oct-2000 misc fixes; mostly, discard more TTL-mangled rx packets. + * 01-nov-2000 usb_device_id table and probing api update by + * Adam J. Richter . + * 18-dec-2000 (db) tx watchdog, "net1080" renaming to "usbnet", device_info + * and prolific support, isolate net1080-specific bits, cleanup. + * fix unlink_urbs oops in D3 PM resume code path. + * 02-feb-2001 (db) fix tx skb sharing, packet length, match_flags, ... + * 08-feb-2001 stubbed in "linuxdev", maybe the SA-1100 folk can use it; + * AnchorChips 2720 support (from spec) for testing; + * fix bit-ordering problem with ethernet multicast addr + * 19-feb-2001 Support for clearing halt conditions. SA1100 UDC support + * updates. Oleg Drokin (green@iXcelerator.com) + * 25-mar-2001 More SA-110 updates, including workaround for ip problem + * expecting cleared skb->cb and framing change to match latest + * handhelds.org version (Oleg). Enable device IDs from the + * Win32 Belkin driver; other cleanups (db). + * + *-------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG // error path messages, extra info +// #define VERBOSE // more; success messages +// #define REALLY_QUEUE + +#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) +# define DEBUG +#endif +#include + + +#define CONFIG_USB_AN2720 +#define CONFIG_USB_LINUXDEV +#define CONFIG_USB_NET1080 +#define CONFIG_USB_PL2301 + + +/*-------------------------------------------------------------------------*/ + +/* + * Nineteen USB 1.1 max size bulk transactions per frame (ms), max. + * Several dozen bytes of IPv4 data can fit in two such transactions. + * One maximum size Ethernet packet takes twenty four of them. + */ +#ifdef REALLY_QUEUE +#define RX_QLEN 4 +#define TX_QLEN 4 +#else +#define RX_QLEN 1 +#define TX_QLEN 1 +#endif + +// packets are always ethernet inside +// ... except they can be bigger (up to 64K with this framing) +#define MIN_PACKET sizeof(struct ethhdr) +#define MAX_PACKET 32768 + +// reawaken network queue this soon after stopping; else watchdog barks +#define TX_TIMEOUT_JIFFIES (5*HZ) + +// for vendor-specific control operations +#define CONTROL_TIMEOUT_MS (500) /* msec */ +#define CONTROL_TIMEOUT_JIFFIES ((CONTROL_TIMEOUT_MS * HZ)/1000) + +// between wakeups +#define UNLINK_TIMEOUT_JIFFIES ((3 /*ms*/ * HZ)/1000) + +/*-------------------------------------------------------------------------*/ + +// list of all devices we manage +static DECLARE_MUTEX (usbnet_mutex); +static LIST_HEAD (usbnet_list); + +// randomly generated ethernet address +static u8 node_id [ETH_ALEN]; + +// state we keep for each device we handle +struct usbnet { + // housekeeping + struct usb_device *udev; + struct driver_info *driver_info; + struct semaphore mutex; + struct list_head dev_list; + wait_queue_head_t *wait; + + // protocol/interface state + struct net_device net; + struct net_device_stats stats; + u16 packet_id; + + // various kinds of pending driver work + struct sk_buff_head rxq; + struct sk_buff_head txq; + struct sk_buff_head done; + struct tasklet_struct bh; + struct tq_struct ctrl_task; +}; + +// device-specific info used by the driver +struct driver_info { + char *description; + + int flags; +#define FLAG_FRAMING 0x0001 /* guard against device dropouts */ + + /* reset device ... can sleep */ + int (*reset)(struct usbnet *); + + /* see if peer is connected ... can sleep */ + int (*check_connect)(struct usbnet *); + + // FIXME -- also an interrupt mechanism + + /* framework currently "knows" bulk EPs talk packets */ + int in; /* rx endpoint */ + int out; /* tx endpoint */ + int epsize; +}; + +#define EP_SIZE(usbnet) ((usbnet)->driver_info->epsize) + +// we record the state for each of our queued skbs +enum skb_state { + illegal = 0, + tx_start, tx_done, + rx_start, rx_done, rx_cleanup +}; + +struct skb_data { // skb->cb is one of these + struct urb *urb; + struct usbnet *dev; + enum skb_state state; + size_t length; +}; + + +#define mutex_lock(x) down(x) +#define mutex_unlock(x) up(x) + +#define RUN_CONTEXT (in_irq () ? "in_irq" \ + : (in_interrupt () ? "in_interrupt" : "can sleep")) + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#define devdbg(usbnet, fmt, arg...) \ + printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net.name, ## arg) +#else +#define devdbg(usbnet, fmt, arg...) do {} while(0) +#endif + +#define devinfo(usbnet, fmt, arg...) \ + printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net.name, ## arg) + +/*------------------------------------------------------------------------- + * + * NetChip framing of ethernet packets, supporting additional error + * checks for links that may drop bulk packets from inside messages. + * Odd USB length == always short read for last usb packet. + * - nc_header + * - Ethernet header (14 bytes) + * - payload + * - (optional padding byte, if needed so length becomes odd) + * - nc_trailer + * + * This framing is to be avoided for non-NetChip devices. + */ + +struct nc_header { // packed: + u16 hdr_len; // sizeof nc_header (LE, all) + u16 packet_len; // payload size (including ethhdr) + u16 packet_id; // detects dropped packets +#define MIN_HEADER 6 + + // all else is optional, and must start with: + // u16 vendorId; // from usb-if + // u16 productId; +} __attribute__((__packed__)); + +#define PAD_BYTE ((unsigned char)0xAC) + +struct nc_trailer { + u16 packet_id; +} __attribute__((__packed__)); + +// packets may use FLAG_FRAMING and optional pad +#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \ + + sizeof (struct ethhdr) \ + + (mtu) \ + + 1 \ + + sizeof (struct nc_trailer)) + +#define MIN_FRAMED FRAMED_SIZE(0) + + + +#ifdef CONFIG_USB_AN2720 + +/*------------------------------------------------------------------------- + * + * AnchorChips 2720 driver ... http://www.cypress.com + * + * This doesn't seem to have a way to detect whether the peer is + * connected, or need any reset handshaking. It's got pretty big + * internal buffers (handles most of a frame's worth of data). + * Chip data sheets don't describe any vendor control messages. + * + *-------------------------------------------------------------------------*/ + +static const struct driver_info an2720_info = { + description: "AnchorChips/Cypress 2720", + // no reset available! + // no check_connect available! + + in: 2, out: 2, // direction distinguishes these + epsize: 64, +}; + +#endif /* CONFIG_USB_AN2720 */ + + + +#ifdef CONFIG_USB_LINUXDEV + +/*------------------------------------------------------------------------- + * + * This could talk to a device that uses Linux, such as a PDA or + * an embedded system, or in fact to any "smart" device using this + * particular mapping of USB and Ethernet. + * + * Such a Linux host would need a "USB Device Controller" hardware + * (not "USB Host Controller"), and a network driver talking to that + * hardware. + * + * One example is Intel's SA-1100 chip, which integrates basic USB + * support (arch/arm/sa1100/usb-eth.c). + * + *-------------------------------------------------------------------------*/ + + +static const struct driver_info linuxdev_info = { + description: "Linux Device", + // no reset defined (yet?) + // no check_connect needed! + in: 2, out: 1, + epsize: 64, +}; + +#endif /* CONFIG_USB_LINUXDEV */ + + + +#ifdef CONFIG_USB_NET1080 + +/*------------------------------------------------------------------------- + * + * Netchip 1080 driver ... http://www.netchip.com + * + *-------------------------------------------------------------------------*/ + +/* + * Zero means no timeout; else, how long a 64 byte bulk packet may be queued + * before the hardware drops it. If that's done, the driver will need to + * frame network packets to guard against the dropped USB packets. The win32 + * driver sets this for both sides of the link. + */ +#define NC_READ_TTL_MS ((u8)255) // ms + +/* + * We ignore most registers and EEPROM contents. + */ +#define REG_USBCTL ((u8)0x04) +#define REG_TTL ((u8)0x10) +#define REG_STATUS ((u8)0x11) + +/* + * Vendor specific requests to read/write data + */ +#define REQUEST_REGISTER ((u8)0x10) +#define REQUEST_EEPROM ((u8)0x11) + +static int +nc_vendor_read (struct usbnet *dev, u8 req, u8 regnum, u16 *retval_ptr) +{ + int status = usb_control_msg (dev->udev, + usb_rcvctrlpipe (dev->udev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, regnum, + retval_ptr, sizeof *retval_ptr, + CONTROL_TIMEOUT_JIFFIES); + if (status > 0) + status = 0; + if (!status) + le16_to_cpus (retval_ptr); + return status; +} + +static inline int +nc_register_read (struct usbnet *dev, u8 regnum, u16 *retval_ptr) +{ + return nc_vendor_read (dev, REQUEST_REGISTER, regnum, retval_ptr); +} + +// no retval ... can become async, usable in_interrupt() +static void +nc_vendor_write (struct usbnet *dev, u8 req, u8 regnum, u16 value) +{ + usb_control_msg (dev->udev, + usb_sndctrlpipe (dev->udev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, regnum, + 0, 0, // data is in setup packet + CONTROL_TIMEOUT_JIFFIES); +} + +static inline void +nc_register_write (struct usbnet *dev, u8 regnum, u16 value) +{ + nc_vendor_write (dev, REQUEST_REGISTER, regnum, value); +} + + +#if 0 +static void nc_dump_registers (struct usbnet *dev) +{ + u8 reg; + u16 value; + + dbg ("%s registers:", dev->net.name); + for (reg = 0; reg < 0x20; reg++) { + int retval; + + // reading some registers is trouble + if (reg >= 0x08 && reg <= 0xf) + continue; + if (reg >= 0x12 && reg <= 0x1e) + continue; + + retval = nc_register_read (dev, reg, &value); + if (retval < 0) + dbg ("%s reg [0x%x] ==> error %d", + dev->net.name, reg, retval); + else + dbg ("%s reg [0x%x] = 0x%x", + dev->net.name, reg, value); + } +} +#endif + + +/*-------------------------------------------------------------------------*/ + +/* + * Control register + */ + +#define USBCTL_WRITABLE_MASK 0x1f0f +// bits 15-13 reserved, r/o +#define USBCTL_ENABLE_LANG (1 << 12) +#define USBCTL_ENABLE_MFGR (1 << 11) +#define USBCTL_ENABLE_PROD (1 << 10) +#define USBCTL_ENABLE_SERIAL (1 << 9) +#define USBCTL_ENABLE_DEFAULTS (1 << 8) +// bits 7-4 reserved, r/o +#define USBCTL_FLUSH_OTHER (1 << 3) +#define USBCTL_FLUSH_THIS (1 << 2) +#define USBCTL_DISCONN_OTHER (1 << 1) +#define USBCTL_DISCONN_THIS (1 << 0) + +static inline void nc_dump_usbctl (struct usbnet *dev, u16 usbctl) +{ +#ifdef DEBUG + devdbg (dev, "net1080 %03d/%03d usbctl 0x%x:%s%s%s%s%s;" + " this%s%s;" + " other%s%s; r/o 0x%x", + dev->udev->bus->busnum, dev->udev->devnum, + usbctl, + (usbctl & USBCTL_ENABLE_LANG) ? " lang" : "", + (usbctl & USBCTL_ENABLE_MFGR) ? " mfgr" : "", + (usbctl & USBCTL_ENABLE_PROD) ? " prod" : "", + (usbctl & USBCTL_ENABLE_SERIAL) ? " serial" : "", + (usbctl & USBCTL_ENABLE_DEFAULTS) ? " defaults" : "", + + (usbctl & USBCTL_FLUSH_OTHER) ? " FLUSH" : "", + (usbctl & USBCTL_DISCONN_OTHER) ? " DIS" : "", + (usbctl & USBCTL_FLUSH_THIS) ? " FLUSH" : "", + (usbctl & USBCTL_DISCONN_THIS) ? " DIS" : "", + usbctl & ~USBCTL_WRITABLE_MASK + ); +#endif +} + +/*-------------------------------------------------------------------------*/ + +/* + * Status register + */ + +#define STATUS_PORT_A (1 << 15) + +#define STATUS_CONN_OTHER (1 << 14) +#define STATUS_SUSPEND_OTHER (1 << 13) +#define STATUS_MAILBOX_OTHER (1 << 12) +#define STATUS_PACKETS_OTHER(n) (((n) >> 8) && 0x03) + +#define STATUS_CONN_THIS (1 << 6) +#define STATUS_SUSPEND_THIS (1 << 5) +#define STATUS_MAILBOX_THIS (1 << 4) +#define STATUS_PACKETS_THIS(n) (((n) >> 0) && 0x03) + +#define STATUS_UNSPEC_MASK 0x0c8c +#define STATUS_NOISE_MASK ((u16)~(0x0303|STATUS_UNSPEC_MASK)) + + +static inline void nc_dump_status (struct usbnet *dev, u16 status) +{ +#ifdef DEBUG + devdbg (dev, "net1080 %03d/%03d status 0x%x:" + " this (%c) PKT=%d%s%s%s;" + " other PKT=%d%s%s%s; unspec 0x%x", + dev->udev->bus->busnum, dev->udev->devnum, + status, + + // XXX the packet counts don't seem right + // (1 at reset, not 0); maybe UNSPEC too + + (status & STATUS_PORT_A) ? 'A' : 'B', + STATUS_PACKETS_THIS (status), + (status & STATUS_CONN_THIS) ? " CON" : "", + (status & STATUS_SUSPEND_THIS) ? " SUS" : "", + (status & STATUS_MAILBOX_THIS) ? " MBOX" : "", + + STATUS_PACKETS_OTHER (status), + (status & STATUS_CONN_OTHER) ? " CON" : "", + (status & STATUS_SUSPEND_OTHER) ? " SUS" : "", + (status & STATUS_MAILBOX_OTHER) ? " MBOX" : "", + + status & STATUS_UNSPEC_MASK + ); +#endif +} + +/*-------------------------------------------------------------------------*/ + +/* + * TTL register + */ + +#define TTL_THIS(ttl) (0x00ff & ttl) +#define TTL_OTHER(ttl) (0x00ff & (ttl >> 8)) +#define MK_TTL(this,other) ((u16)(((other)<<8)|(0x00ff&(this)))) + +static inline void nc_dump_ttl (struct usbnet *dev, u16 ttl) +{ +#ifdef DEBUG + devdbg (dev, "net1080 %03d/%03d ttl 0x%x this = %d, other = %d", + dev->udev->bus->busnum, dev->udev->devnum, + ttl, + + TTL_THIS (ttl), + TTL_OTHER (ttl) + ); +#endif +} + +/*-------------------------------------------------------------------------*/ + +static int net1080_reset (struct usbnet *dev) +{ + u16 usbctl, status, ttl; + int retval; + + // nc_dump_registers (dev); + + if ((retval = nc_register_read (dev, REG_STATUS, &status)) < 0) { + dbg ("can't read dev %d status: %d", dev->udev->devnum, retval); + goto done; + } + // nc_dump_status (dev, status); + + if ((retval = nc_register_read (dev, REG_USBCTL, &usbctl)) < 0) { + dbg ("can't read USBCTL, %d", retval); + goto done; + } + // nc_dump_usbctl (dev, usbctl); + + nc_register_write (dev, REG_USBCTL, + USBCTL_FLUSH_THIS | USBCTL_FLUSH_OTHER); + + if ((retval = nc_register_read (dev, REG_TTL, &ttl)) < 0) { + dbg ("can't read TTL, %d", retval); + goto done; + } + // nc_dump_ttl (dev, ttl); + + nc_register_write (dev, REG_TTL, + MK_TTL (NC_READ_TTL_MS, TTL_OTHER (ttl)) ); + dbg ("%s: assigned TTL, %d ms", dev->net.name, NC_READ_TTL_MS); + + devdbg (dev, "port %c, peer %sconnected", + (status & STATUS_PORT_A) ? 'A' : 'B', + (status & STATUS_CONN_OTHER) ? "" : "dis" + ); + retval = 0; + +done: + return retval; +} + +static int net1080_check_connect (struct usbnet *dev) +{ + int retval; + u16 status; + + if ((retval = nc_register_read (dev, REG_STATUS, &status)) != 0) { + dbg ("%s net1080_check_conn read - %d", dev->net.name, retval); + return retval; + } + if ((status & STATUS_CONN_OTHER) != STATUS_CONN_OTHER) + return -ENOLINK; + return 0; +} + +static const struct driver_info net1080_info = { + description: "NetChip TurboCONNECT", + flags: FLAG_FRAMING, + reset: net1080_reset, + check_connect: net1080_check_connect, + + in: 1, out: 1, // direction distinguishes these + epsize: 64, +}; + +#endif /* CONFIG_USB_NET1080 */ + + + +#ifdef CONFIG_USB_PL2301 + +/*------------------------------------------------------------------------- + * + * Prolific PL-2301/PL-2302 driver ... http://www.prolifictech.com + * + *-------------------------------------------------------------------------*/ + +/* + * Bits 0-4 can be used for software handshaking; they're set from + * one end, cleared from the other, "read" with the interrupt byte. + */ +#define PL_S_EN (1<<7) /* (feature only) suspend enable */ +/* reserved bit -- rx ready (6) ? */ +#define PL_TX_READY (1<<5) /* (interrupt only) transmit ready */ +#define PL_RESET_OUT (1<<4) /* reset output pipe */ +#define PL_RESET_IN (1<<3) /* reset input pipe */ +#define PL_TX_C (1<<2) /* transmission complete */ +#define PL_TX_REQ (1<<1) /* transmission received */ +#define PL_PEER_E (1<<0) /* peer exists */ + +static inline int +pl_vendor_req (struct usbnet *dev, u8 req, u8 val, u8 index) +{ + return usb_control_msg (dev->udev, + usb_rcvctrlpipe (dev->udev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, index, + 0, 0, + CONTROL_TIMEOUT_JIFFIES); +} + +static inline int +pl_clear_QuickLink_features (struct usbnet *dev, int val) +{ + return pl_vendor_req (dev, 1, (u8) val, 0); +} + +static inline int +pl_set_QuickLink_features (struct usbnet *dev, int val) +{ + return pl_vendor_req (dev, 3, (u8) val, 0); +} + +/*-------------------------------------------------------------------------*/ + +static int pl_reset (struct usbnet *dev) +{ + return pl_set_QuickLink_features (dev, + PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E); +} + +static int pl_check_connect (struct usbnet *dev) +{ + // FIXME test interrupt data PL_PEER_E bit + // plus, there's some handshake done by + // the prolific win32 driver... + dbg ("%s: assuming peer is connected", dev->net.name); + return 0; +} + +static const struct driver_info prolific_info = { + description: "Prolific PL-2301/PL-2302", + reset: pl_reset, + check_connect: pl_check_connect, + + in: 3, out: 2, + epsize: 64, +}; + +#endif /* CONFIG_USB_PL2301 */ + + + +/*------------------------------------------------------------------------- + * + * Network Device Driver (peer link to "Host Device", from USB host) + * + *-------------------------------------------------------------------------*/ + +static int usbnet_change_mtu (struct net_device *net, int new_mtu) +{ + struct usbnet *dev = (struct usbnet *) net->priv; + + if (new_mtu <= sizeof (struct ethhdr) || new_mtu > MAX_PACKET) + return -EINVAL; + if (((dev->driver_info->flags) & FLAG_FRAMING)) { + if (FRAMED_SIZE (new_mtu) > MAX_PACKET) + return -EINVAL; + // no second zero-length packet read wanted after mtu-sized packets + } else if (((new_mtu + sizeof (struct ethhdr)) % EP_SIZE (dev)) == 0) + return -EDOM; + net->mtu = new_mtu; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct net_device_stats *usbnet_get_stats (struct net_device *net) +{ + return &((struct usbnet *) net->priv)->stats; +} + +/*-------------------------------------------------------------------------*/ + +/* urb completions are currently in_irq; avoid doing real work then. */ + +static void defer_bh (struct usbnet *dev, struct sk_buff *skb) +{ + struct sk_buff_head *list = skb->list; + + spin_lock (&list->lock); + __skb_unlink (skb, list); + spin_unlock (&list->lock); + spin_lock (&dev->done.lock); + __skb_queue_tail (&dev->done, skb); + if (dev->done.qlen == 1) + tasklet_schedule (&dev->bh); + spin_unlock (&dev->done.lock); +} + +/*-------------------------------------------------------------------------*/ + +static void rx_complete (struct urb *urb); + +static void rx_submit (struct usbnet *dev, struct urb *urb, int flags) +{ + struct sk_buff *skb; + struct skb_data *entry; + int retval = 0; + unsigned long lockflags; + size_t size; + + /* make the IP layer happier with 16-byte alignment. + * Ethernet header uses 14 bytes; pad 2 normally. + * NetChip framing adds another 6, so pad total of 12. + */ + size = (dev->driver_info->flags & FLAG_FRAMING) + ? (12 + FRAMED_SIZE (dev->net.mtu)) + : (2 + sizeof (struct ethhdr) + dev->net.mtu); + if ((skb = alloc_skb (size, flags)) == 0) { + dbg ("no rx skb"); + tasklet_schedule (&dev->bh); + usb_free_urb (urb); + return; + } + skb_reserve (skb, (dev->driver_info->flags & FLAG_FRAMING) ? 12 : 2); + + entry = (struct skb_data *) skb->cb; + entry->urb = urb; + entry->dev = dev; + entry->state = rx_start; + entry->length = 0; + + FILL_BULK_URB (urb, dev->udev, + usb_rcvbulkpipe (dev->udev, dev->driver_info->in), + skb->data, size, rx_complete, skb); +#ifdef REALLY_QUEUE + urb->transfer_flags |= USB_QUEUE_BULK; +#endif + + spin_lock_irqsave (&dev->rxq.lock, lockflags); + + if (netif_running (&dev->net)) { + if ((retval = usb_submit_urb (urb)) != 0) { + dbg ("%s rx submit, %d", dev->net.name, retval); + tasklet_schedule (&dev->bh); + } else { + __skb_queue_tail (&dev->rxq, skb); + } + } else { + dbg ("rx: stopped"); + retval = -ENOLINK; + } + spin_unlock_irqrestore (&dev->rxq.lock, lockflags); + if (retval) { + dev_kfree_skb_any (skb); + usb_free_urb (urb); + } +} + + +/*-------------------------------------------------------------------------*/ + +static inline void rx_process (struct usbnet *dev, struct sk_buff *skb) +{ + if (dev->driver_info->flags & FLAG_FRAMING) { + struct nc_header *header; + struct nc_trailer *trailer; + + if (!(skb->len & 0x01) + || MIN_FRAMED > skb->len + || skb->len > FRAMED_SIZE (dev->net.mtu)) { + dev->stats.rx_frame_errors++; + dbg ("rx framesize %d range %d..%d mtu %d", skb->len, + MIN_FRAMED, FRAMED_SIZE (dev->net.mtu), + dev->net.mtu + ); + goto error; + } + + header = (struct nc_header *) skb->data; + le16_to_cpus (&header->hdr_len); + le16_to_cpus (&header->packet_len); + if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) { + dev->stats.rx_frame_errors++; + dbg ("packet too big, %d", header->packet_len); + goto error; + } else if (header->hdr_len < MIN_HEADER) { + dev->stats.rx_frame_errors++; + dbg ("header too short, %d", header->hdr_len); + goto error; + } else if (header->hdr_len > MIN_HEADER) { + // out of band data for us? + dbg ("header OOB, %d bytes", + header->hdr_len - MIN_HEADER); + // switch (vendor/product ids) { ... } + } + skb_pull (skb, header->hdr_len); + + trailer = (struct nc_trailer *) + (skb->data + skb->len - sizeof *trailer); + skb_trim (skb, skb->len - sizeof *trailer); + + if ((header->packet_len & 0x01) == 0) { + if (skb->data [header->packet_len] != PAD_BYTE) { + dev->stats.rx_frame_errors++; + dbg ("bad pad"); + goto error; + } + skb_trim (skb, skb->len - 1); + } + if (skb->len != header->packet_len) { + dev->stats.rx_frame_errors++; + dbg ("bad packet len %d (expected %d)", + skb->len, header->packet_len); + goto error; + } + if (header->packet_id != get_unaligned (&trailer->packet_id)) { + dev->stats.rx_fifo_errors++; + dbg ("(2+ dropped) rx packet_id mismatch 0x%x 0x%x", + header->packet_id, trailer->packet_id); + goto error; + } +#if 0 + devdbg (dev, "frame hdr_len, + header->packet_len, header->packet_id); +#endif + } else { + // we trust the network stack to remove + // the extra byte we may have appended + } + + if (skb->len) { + int status; + +// FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ? + + skb->dev = &dev->net; + skb->protocol = eth_type_trans (skb, &dev->net); + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + +#ifdef VERBOSE + devdbg (dev, "< rx, len %d, type 0x%x", + skb->len + sizeof (struct ethhdr), skb->protocol); +#endif + memset (skb->cb,0,sizeof(struct skb_data)); + status = netif_rx (skb); + if (status != NET_RX_SUCCESS) + devdbg (dev, "netif_rx status %d", status); + } else { + dbg ("drop"); +error: + dev->stats.rx_errors++; + skb_queue_tail (&dev->done, skb); + } +} + +/*-------------------------------------------------------------------------*/ + +static void rx_complete (struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct skb_data *entry = (struct skb_data *) skb->cb; + struct usbnet *dev = entry->dev; + int urb_status = urb->status; + + skb_put (skb, urb->actual_length); + entry->state = rx_done; + entry->urb = 0; + + if ((urb->transfer_flags & USB_ASYNC_UNLINK) != 0) { + dbg ("rx ... shutting down"); + usb_free_urb (urb); + urb = 0; + } + + switch (urb_status) { + // success + case 0: + if (MIN_PACKET > skb->len || skb->len > MAX_PACKET) { + entry->state = rx_cleanup; + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; + dbg ("rx length %d", skb->len); + } + break; + + // software-driven interface shutdown + case -ECONNRESET: // usb-ohci, usb-uhci + case -ECONNABORTED: // uhci ... for usb-uhci, INTR + dbg ("%s shutdown, code %d", dev->net.name, urb_status); + entry->state = rx_cleanup; + usb_free_urb (urb); + urb = 0; + break; + + // data overrun ... flush fifo? + case -EOVERFLOW: + dev->stats.rx_over_errors++; + // FALLTHROUGH + + default: + // on unplug we'll get a burst of ETIMEDOUT/EILSEQ + // till the khubd gets and handles its interrupt. + entry->state = rx_cleanup; + dev->stats.rx_errors++; + dbg ("%s rx: status %d", dev->net.name, urb_status); + break; + } + + defer_bh (dev, skb); + + if (urb) { + if (netif_running (&dev->net)) { + rx_submit (dev, urb, GFP_ATOMIC); + return; + } else + usb_free_urb (urb); + } +#ifdef VERBOSE + dbg ("no read resubmitted"); +#endif /* VERBOSE */ +} + +/*-------------------------------------------------------------------------*/ + +// unlink pending rx/tx; completion handlers do all other cleanup + +static int unlink_urbs (struct sk_buff_head *q) +{ + unsigned long flags; + struct sk_buff *skb, *skbnext; + int count = 0; + + spin_lock_irqsave (&q->lock, flags); + for (skb = q->next; skb != (struct sk_buff *) q; skb = skbnext) { + struct skb_data *entry; + struct urb *urb; + int retval; + + entry = (struct skb_data *) skb->cb; + urb = entry->urb; + skbnext = skb->next; + + // during some PM-driven resume scenarios, + // these unlinks complete immediately + urb->transfer_flags |= USB_ASYNC_UNLINK; + retval = usb_unlink_urb (urb); + if (retval < 0) + dbg ("unlink urb err, %d", retval); + else + count++; + } + spin_unlock_irqrestore (&q->lock, flags); + return count; +} + + +/*-------------------------------------------------------------------------*/ + +// precondition: never called in_interrupt + +static int usbnet_stop (struct net_device *net) +{ + struct usbnet *dev = (struct usbnet *) net->priv; + int temp; + DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); + DECLARE_WAITQUEUE (wait, current); + + mutex_lock (&dev->mutex); + netif_stop_queue(net); + + devdbg (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld", + dev->stats.rx_packets, dev->stats.tx_packets, + dev->stats.rx_errors, dev->stats.tx_errors + ); + + // ensure there are no more active urbs + add_wait_queue (&unlink_wakeup, &wait); + dev->wait = &unlink_wakeup; + temp = unlink_urbs (&dev->txq) + unlink_urbs (&dev->rxq); + + // maybe wait for deletions to finish. + while (skb_queue_len (&dev->rxq) + && skb_queue_len (&dev->done) + && skb_queue_len (&dev->txq)) { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout (UNLINK_TIMEOUT_JIFFIES); + dbg ("waited for %d urb completions", temp); + } + dev->wait = 0; + remove_wait_queue (&unlink_wakeup, &wait); + + mutex_unlock (&dev->mutex); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +// posts reads, and enables write queing + +// precondition: never called in_interrupt + +static int usbnet_open (struct net_device *net) +{ + struct usbnet *dev = (struct usbnet *) net->priv; + int retval = 0; + struct driver_info *info = dev->driver_info; + + mutex_lock (&dev->mutex); + + // put into "known safe" state + if (info->reset && (retval = info->reset (dev)) < 0) { + devinfo (dev, "open reset fail (%d) usbnet %03d/%03d, %s", + retval, + dev->udev->bus->busnum, dev->udev->devnum, + info->description); + goto done; + } + + // insist peer be connected + if (info->check_connect && (retval = info->check_connect (dev)) < 0) { + devdbg (dev, "can't open; %d", retval); + goto done; + } + + netif_start_queue (net); + devdbg (dev, "open: enable queueing (rx %d, tx %d) mtu %d %sframed", + RX_QLEN, TX_QLEN, dev->net.mtu, + (info->flags & FLAG_FRAMING) ? "" : "un" + ); + + // delay posting reads until we're fully open + tasklet_schedule (&dev->bh); +done: + mutex_unlock (&dev->mutex); + return retval; +} + +/*-------------------------------------------------------------------------*/ + +/* usb_clear_halt cannot be called in interrupt context */ + +static void +tx_clear_halt(void *data) +{ + struct usbnet *dev = data; + + usb_clear_halt (dev->udev, + usb_sndbulkpipe (dev->udev, dev->driver_info->out)); + netif_wake_queue (&dev->net); +} + +/*-------------------------------------------------------------------------*/ + +static void tx_complete (struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct skb_data *entry = (struct skb_data *) skb->cb; + struct usbnet *dev = entry->dev; + + if (urb->status == USB_ST_STALL) { + if (dev->ctrl_task.sync == 0) { + dev->ctrl_task.routine = tx_clear_halt; + dev->ctrl_task.data = dev; + schedule_task(&dev->ctrl_task); + } else { + dbg ("Cannot clear TX stall"); + } + } + urb->dev = 0; + entry->state = tx_done; + defer_bh (dev, skb); +} + +/*-------------------------------------------------------------------------*/ + +static void usbnet_tx_timeout (struct net_device *net) +{ + struct usbnet *dev = (struct usbnet *) net->priv; + + unlink_urbs (&dev->txq); + tasklet_schedule (&dev->bh); + + // FIXME: device recovery -- reset? +} + +/*-------------------------------------------------------------------------*/ + +static inline struct sk_buff *fixup_skb (struct sk_buff *skb, int flags) +{ + int padlen; + struct sk_buff *skb2; + + padlen = ((skb->len + sizeof (struct nc_header) + + sizeof (struct nc_trailer)) & 0x01) ? 0 : 1; + if (!skb_cloned (skb)) { + int headroom = skb_headroom (skb); + int tailroom = skb_tailroom (skb); + + if ((padlen + sizeof (struct nc_trailer)) <= tailroom + && sizeof (struct nc_header) <= headroom) + return skb; + + if ((sizeof (struct nc_header) + padlen + + sizeof (struct nc_trailer)) < + (headroom + tailroom)) { + skb->data = memmove (skb->head + + sizeof (struct nc_header), + skb->data, skb->len); + skb->tail = skb->data + skb->len; + return skb; + } + } + skb2 = skb_copy_expand (skb, + sizeof (struct nc_header), + sizeof (struct nc_trailer) + padlen, + flags); + dev_kfree_skb_any (skb); + return skb2; +} + +/*-------------------------------------------------------------------------*/ + +static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) +{ + struct usbnet *dev = (struct usbnet *) net->priv; + int length = skb->len; + int retval = NET_XMIT_SUCCESS; + struct urb *urb = 0; + struct skb_data *entry; + struct nc_header *header = 0; + struct nc_trailer *trailer = 0; + struct driver_info *info = dev->driver_info; + int flags; + + flags = in_interrupt () ? GFP_ATOMIC : GFP_KERNEL; + + if (info->flags & FLAG_FRAMING) { + struct sk_buff *skb2; + skb2 = fixup_skb (skb, flags); + if (!skb2) { + dbg ("can't fixup skb"); + goto drop; + } + skb = skb2; + } + + if (!(urb = usb_alloc_urb (0))) { + dbg ("no urb"); + goto drop; + } + + entry = (struct skb_data *) skb->cb; + entry->urb = urb; + entry->dev = dev; + entry->state = tx_start; + entry->length = length; + + if (info->flags & FLAG_FRAMING) { + header = (struct nc_header *) skb_push (skb, sizeof *header); + header->hdr_len = cpu_to_le16 (sizeof (*header)); + header->packet_len = cpu_to_le16 (length); + if (!((skb->len + sizeof *trailer) & 0x01)) + *skb_put (skb, 1) = PAD_BYTE; + trailer = (struct nc_trailer *) skb_put (skb, sizeof *trailer); + } else if ((length % EP_SIZE (dev)) == 0) { + if (skb_shared (skb)) { + struct sk_buff *skb2; + skb2 = skb_unshare (skb, flags); + if (!skb2) { + usb_free_urb (urb); + dbg ("can't unshare skb"); + goto drop; + } + skb = skb2; + } + skb->len++; + } + + FILL_BULK_URB (urb, dev->udev, + usb_sndbulkpipe (dev->udev, info->out), + skb->data, skb->len, tx_complete, skb); +#ifdef REALLY_QUEUE + urb->transfer_flags |= USB_QUEUE_BULK; +#endif + // FIXME urb->timeout = ... jiffies ... ; + + spin_lock_irqsave (&dev->txq.lock, flags); + if (info->flags & FLAG_FRAMING) { + header->packet_id = cpu_to_le16 (dev->packet_id++); + put_unaligned (header->packet_id, &trailer->packet_id); +#if 0 + devdbg (dev, "frame >tx h %d p %d id %d", + header->hdr_len, header->packet_len, + header->packet_id); +#endif + } + + netif_stop_queue (net); + if ((retval = usb_submit_urb (urb)) != 0) { + netif_start_queue (net); + dbg ("%s tx: submit urb err %d", net->name, retval); + } else { + net->trans_start = jiffies; + __skb_queue_tail (&dev->txq, skb); + if (dev->txq.qlen < TX_QLEN) + netif_start_queue (net); + } + spin_unlock_irqrestore (&dev->txq.lock, flags); + + if (retval) { + devdbg (dev, "drop, code %d", retval); +drop: + retval = NET_XMIT_DROP; + dev->stats.tx_dropped++; + dev_kfree_skb_any (skb); + usb_free_urb (urb); +#ifdef VERBOSE + } else { + devdbg (dev, "> tx, len %d, type 0x%x", + length, skb->protocol); +#endif + } + return retval; +} + + +/*-------------------------------------------------------------------------*/ + +// tasklet ... work that avoided running in_irq() + +static void usbnet_bh (unsigned long param) +{ + struct usbnet *dev = (struct usbnet *) param; + struct sk_buff *skb; + struct skb_data *entry; + + while ((skb = skb_dequeue (&dev->done))) { + entry = (struct skb_data *) skb->cb; + switch (entry->state) { + case rx_done: + entry->state = rx_cleanup; + rx_process (dev, skb); + continue; + case tx_done: + if (entry->urb->status) { + // can this statistic become more specific? + dev->stats.tx_errors++; + dbg ("%s tx: err %d", dev->net.name, + entry->urb->status); + } else { + dev->stats.tx_packets++; + dev->stats.tx_bytes += entry->length; + } + // FALLTHROUGH: + case rx_cleanup: + usb_free_urb (entry->urb); + dev_kfree_skb (skb); + continue; + default: + dbg ("%s: bogus skb state %d", + dev->net.name, entry->state); + } + } + + // waiting for all pending urbs to complete? + if (dev->wait) { + if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) { + wake_up (dev->wait); + } + + // or are we maybe short a few urbs? + } else if (netif_running (&dev->net)) { + int temp = dev->rxq.qlen; + + if (temp < RX_QLEN) { + struct urb *urb; + int i; + for (i = 0; i < 3 && dev->rxq.qlen < RX_QLEN; i++) { + if ((urb = usb_alloc_urb (0)) != 0) + rx_submit (dev, urb, GFP_ATOMIC); + } + if (temp != dev->rxq.qlen) + devdbg (dev, "rxqlen %d --> %d", + temp, dev->rxq.qlen); + if (dev->rxq.qlen < RX_QLEN) + tasklet_schedule (&dev->bh); + } + if (dev->txq.qlen < TX_QLEN) + netif_wake_queue (&dev->net); + } +} + + + +/*------------------------------------------------------------------------- + * + * USB Device Driver support + * + *-------------------------------------------------------------------------*/ + +// precondition: never called in_interrupt + +static void usbnet_disconnect (struct usb_device *udev, void *ptr) +{ + struct usbnet *dev = (struct usbnet *) ptr; + + devinfo (dev, "unregister usbnet %03d/%03d, %s", + udev->bus->busnum, udev->devnum, + dev->driver_info->description); + + unregister_netdev (&dev->net); + + mutex_lock (&usbnet_mutex); + mutex_lock (&dev->mutex); + list_del (&dev->dev_list); + mutex_unlock (&usbnet_mutex); + + kfree (dev); + usb_dec_dev_use (udev); +} + + +/*-------------------------------------------------------------------------*/ + +// precondition: never called in_interrupt + +static void * +usbnet_probe (struct usb_device *udev, unsigned ifnum, + const struct usb_device_id *prod) +{ + struct usbnet *dev; + struct net_device *net; + struct usb_interface_descriptor *interface; + struct driver_info *info; + int altnum = 0; + + info = (struct driver_info *) prod->driver_info; + + // sanity check; expect dedicated interface/devices for now. + interface = &udev->actconfig->interface [ifnum].altsetting [altnum]; + if (udev->descriptor.bNumConfigurations != 1 + || udev->config[0].bNumInterfaces != 1 + || interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC + ) { + dbg ("Bogus config info"); + return 0; + } + + if (usb_set_interface (udev, ifnum, altnum) < 0) { + err ("set_interface failed"); + return 0; + } + + // set up our own records + if (!(dev = kmalloc (sizeof *dev, GFP_KERNEL))) { + dbg ("can't kmalloc dev"); + return 0; + } + memset (dev, 0, sizeof *dev); + + init_MUTEX_LOCKED (&dev->mutex); + usb_inc_dev_use (udev); + dev->udev = udev; + dev->driver_info = info; + INIT_LIST_HEAD (&dev->dev_list); + skb_queue_head_init (&dev->rxq); + skb_queue_head_init (&dev->txq); + skb_queue_head_init (&dev->done); + dev->bh.func = usbnet_bh; + dev->bh.data = (unsigned long) dev; + + // set up network interface records + net = &dev->net; + SET_MODULE_OWNER (net); + net->priv = dev; + strcpy (net->name, "usb%d"); + memcpy (net->dev_addr, node_id, sizeof node_id); + + // point-to-point link ... we always use Ethernet headers + // supports win32 interop and the bridge driver. + ether_setup (net); + + net->change_mtu = usbnet_change_mtu; + net->get_stats = usbnet_get_stats; + net->hard_start_xmit = usbnet_start_xmit; + net->open = usbnet_open; + net->stop = usbnet_stop; + net->watchdog_timeo = TX_TIMEOUT_JIFFIES; + net->tx_timeout = usbnet_tx_timeout; + + register_netdev (&dev->net); + devinfo (dev, "register usbnet %03d/%03d, %s", + udev->bus->busnum, udev->devnum, + dev->driver_info->description); + + // ok, it's ready to go. + mutex_lock (&usbnet_mutex); + list_add (&dev->dev_list, &usbnet_list); + mutex_unlock (&dev->mutex); + + // start as if the link is up + netif_device_attach (&dev->net); + + mutex_unlock (&usbnet_mutex); + return dev; +} + + +/*-------------------------------------------------------------------------*/ + +/* + * chip vendor names won't normally be on the cables, and + * may not be on the device. + */ + +static const struct usb_device_id products [] = { + +#ifdef CONFIG_USB_AN2720 +{ + USB_DEVICE (0x0547, 0x2720), // AnchorChips defaults + driver_info: (unsigned long) &an2720_info, +}, +#endif + + +// GeneSys GL620USB (www.genesyslogic.com.tw) +// (patch exists against an older driver version) + + +#ifdef CONFIG_USB_LINUXDEV +/* + * for example, this can be a host side talk-to-PDA driver. + * this driver is NOT what runs _inside_ a Linux device !! + */ +{ + // 1183 = 0x049F, both used as hex values? + USB_DEVICE (0x049F, 0x505A), // Compaq "Itsy" + driver_info: (unsigned long) &linuxdev_info, +}, +#endif + +#ifdef CONFIG_USB_NET1080 +{ + USB_DEVICE (0x0525, 0x1080), // NetChip ref design + driver_info: (unsigned long) &net1080_info, +}, { + USB_DEVICE (0x050d, 0x0004), // Belkin + driver_info: (unsigned long) &net1080_info, +}, { + USB_DEVICE (0x056c, 0x8100), // eTEK + driver_info: (unsigned long) &net1080_info, +}, +#endif + +#ifdef CONFIG_USB_PL2301 +{ + USB_DEVICE (0x067b, 0x0000), // PL-2301 + driver_info: (unsigned long) &prolific_info, +}, { + USB_DEVICE (0x067b, 0x0001), // PL-2302 + driver_info: (unsigned long) &prolific_info, +}, +#endif + + { }, // END +}; +MODULE_DEVICE_TABLE (usb, products); + +static struct usb_driver usbnet_driver = { + name: "usbnet", + id_table: products, + probe: usbnet_probe, + disconnect: usbnet_disconnect, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init usbnet_init (void) +{ + // compiler should optimize this out + if (sizeof (((struct sk_buff *)0)->cb) < sizeof (struct skb_data)) + BUG (); + + get_random_bytes (node_id, sizeof node_id); + node_id [0] &= 0xfe; // clear multicast bit + + if (usb_register (&usbnet_driver) < 0) + return -1; + + return 0; +} +module_init (usbnet_init); + +static void __exit usbnet_exit (void) +{ + usb_deregister (&usbnet_driver); +} +module_exit (usbnet_exit); + +MODULE_AUTHOR ("David Brownell "); +MODULE_DESCRIPTION ("USB Host-to-Host Link Drivers (Linux, NetChip, Prolific, ...)"); diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usbvideo.c linux.ac/drivers/usb/usbvideo.c --- linux.vanilla/drivers/usb/usbvideo.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/usbvideo.c Fri May 4 16:39:24 2001 @@ -0,0 +1,2471 @@ +/* + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#define __NO_VERSION__ /* Temporary: usbvideo is not a module yet */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "usbvideo.h" + +#if defined(MAP_NR) +#define virt_to_page(v) MAP_NR(v) /* Kernels 2.2.x */ +#endif + +static int video_nr = -1; +MODULE_PARM(video_nr, "i"); + +/* + * Local prototypes. + */ +#if USES_PROC_FS +static void usbvideo_procfs_level1_create(usbvideo_t *ut); +static void usbvideo_procfs_level1_destroy(usbvideo_t *ut); +static void usbvideo_procfs_level2_create(uvd_t *uvd); +static void usbvideo_procfs_level2_destroy(uvd_t *uvd); +static int usbvideo_default_procfs_read_proc( + char *page, char **start, off_t off, int count, + int *eof, void *data); +static int usbvideo_default_procfs_write_proc( + struct file *file, const char *buffer, + unsigned long count, void *data); +#endif + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +#define MDEBUG(x) do { } while(0) /* Debug memory management */ + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +unsigned long usbvideo_uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if (pte_present(pte)) { + ret = (unsigned long) page_address(pte_page(pte)); + ret |= (adr & (PAGE_SIZE-1)); + } + } + } + MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); + return ret; +} + +unsigned long usbvideo_uvirt_to_bus(unsigned long adr) +{ + unsigned long kva, ret; + + kva = usbvideo_uvirt_to_kva(pgd_offset(current->mm, adr), adr); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +unsigned long usbvideo_kvirt_to_bus(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = usbvideo_uvirt_to_kva(pgd_offset_k(va), va); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +/* + * Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +unsigned long usbvideo_kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = usbvideo_uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); + return ret; +} + +void *usbvideo_rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr, page; + + /* Round it off to PAGE_SIZE */ + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + page = usbvideo_kvirt_to_pa(adr); + mem_map_reserve(virt_to_page(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return mem; +} + +void usbvideo_rvfree(void *mem, unsigned long size) +{ + unsigned long adr, page; + + if (!mem) + return; + + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + adr=(unsigned long) mem; + while (size > 0) { + page = usbvideo_kvirt_to_pa(adr); + mem_map_unreserve(virt_to_page(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + vfree(mem); +} + +void RingQueue_Initialize(RingQueue_t *rq) +{ + assert(rq != NULL); + init_waitqueue_head(&rq->wqh); +} + +void RingQueue_Allocate(RingQueue_t *rq, int rqLen) +{ + assert(rq != NULL); + assert(rqLen > 0); + rq->length = rqLen; + rq->queue = usbvideo_rvmalloc(rq->length); + assert(rq->queue != NULL); +} + +int RingQueue_IsAllocated(const RingQueue_t *rq) +{ + if (rq == NULL) + return 0; + return (rq->queue != NULL) && (rq->length > 0); +} + +void RingQueue_Free(RingQueue_t *rq) +{ + assert(rq != NULL); + if (RingQueue_IsAllocated(rq)) { + usbvideo_rvfree(rq->queue, rq->length); + rq->queue = NULL; + rq->length = 0; + } +} + +int RingQueue_Dequeue(RingQueue_t *rq, unsigned char *dst, int len) +{ + int i; + assert(rq != NULL); + assert(dst != NULL); + for (i=0; i < len; i++) { + dst[i] = rq->queue[rq->ri]; + RING_QUEUE_DEQUEUE_BYTES(rq,1); + } + return len; +} + +int RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n) +{ + int enqueued = 0; + + assert(rq != NULL); + assert(cdata != NULL); + assert(rq->length > 0); + while (n > 0) { + int m, q_avail; + + /* Calculate the largest chunk that fits the tail of the ring */ + q_avail = rq->length - rq->wi; + if (q_avail <= 0) { + rq->wi = 0; + q_avail = rq->length; + } + m = n; + assert(q_avail > 0); + if (m > q_avail) + m = q_avail; + + memmove(rq->queue + rq->wi, cdata, m); + RING_QUEUE_ADVANCE_INDEX(rq, wi, m); + cdata += m; + enqueued += m; + n -= m; + } + return enqueued; +} + +int RingQueue_GetLength(const RingQueue_t *rq) +{ + int ri, wi; + + assert(rq != NULL); + + ri = rq->ri; + wi = rq->wi; + if (ri == wi) + return 0; + else if (ri < wi) + return wi - ri; + else + return wi + (rq->length - ri); +} + +void RingQueue_InterruptibleSleepOn(RingQueue_t *rq) +{ + assert(rq != NULL); + interruptible_sleep_on(&rq->wqh); +} + +void RingQueue_WakeUpInterruptible(RingQueue_t *rq) +{ + assert(rq != NULL); + if (waitqueue_active(&rq->wqh)) + wake_up_interruptible(&rq->wqh); +} + +/* + * usbvideo_VideosizeToString() + * + * This procedure converts given videosize value to readable string. + * + * History: + * 07-Aug-2000 Created. + * 19-Oct-2000 Reworked for usbvideo module. + */ +void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs) +{ + char tmp[40]; + int n; + + n = 1 + sprintf(tmp, "%ldx%ld", VIDEOSIZE_X(vs), VIDEOSIZE_Y(vs)); + assert(n < sizeof(tmp)); + if ((buf == NULL) || (bufLen < n)) + err("usbvideo_VideosizeToString: buffer is too small."); + else + memmove(buf, tmp, n); +} + +/* + * usbvideo_OverlayChar() + * + * History: + * 01-Feb-2000 Created. + */ +void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame, + int x, int y, int ch) +{ + static const unsigned short digits[16] = { + 0xF6DE, /* 0 */ + 0x2492, /* 1 */ + 0xE7CE, /* 2 */ + 0xE79E, /* 3 */ + 0xB792, /* 4 */ + 0xF39E, /* 5 */ + 0xF3DE, /* 6 */ + 0xF492, /* 7 */ + 0xF7DE, /* 8 */ + 0xF79E, /* 9 */ + 0x77DA, /* a */ + 0xD75C, /* b */ + 0xF24E, /* c */ + 0xD6DC, /* d */ + 0xF34E, /* e */ + 0xF348 /* f */ + }; + unsigned short digit; + int ix, iy; + + if ((uvd == NULL) || (frame == NULL)) + return; + + if (ch >= '0' && ch <= '9') + ch -= '0'; + else if (ch >= 'A' && ch <= 'F') + ch = 10 + (ch - 'A'); + else if (ch >= 'a' && ch <= 'f') + ch = 10 + (ch - 'a'); + else + return; + digit = digits[ch]; + + for (iy=0; iy < 5; iy++) { + for (ix=0; ix < 3; ix++) { + if (digit & 0x8000) { + if (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)) { +/* TODO */ RGB24_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF); + } + } + digit = digit << 1; + } + } +} + +/* + * usbvideo_OverlayString() + * + * History: + * 01-Feb-2000 Created. + */ +void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame, + int x, int y, const char *str) +{ + while (*str) { + usbvideo_OverlayChar(uvd, frame, x, y, *str); + str++; + x += 4; /* 3 pixels character + 1 space */ + } +} + +/* + * usbvideo_OverlayStats() + * + * Overlays important debugging information. + * + * History: + * 01-Feb-2000 Created. + */ +void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame) +{ + const int y_diff = 8; + char tmp[16]; + int x = 10, y=10; + long i, j, barLength; + const int qi_x1 = 60, qi_y1 = 10; + const int qi_x2 = VIDEOSIZE_X(frame->request) - 10, qi_h = 10; + + /* Call the user callback, see if we may proceed after that */ + if (VALID_CALLBACK(uvd, overlayHook)) { + if (GET_CALLBACK(uvd, overlayHook)(uvd, frame) < 0) + return; + } + + /* + * We draw a (mostly) hollow rectangle with qi_xxx coordinates. + * Left edge symbolizes the queue index 0; right edge symbolizes + * the full capacity of the queue. + */ + barLength = qi_x2 - qi_x1 - 2; + if ((barLength > 10) && (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24))) { +/* TODO */ long u_lo, u_hi, q_used; + long m_ri, m_wi, m_lo, m_hi; + + /* + * Determine fill zones (used areas of the queue): + * 0 xxxxxxx u_lo ...... uvd->dp.ri xxxxxxxx u_hi ..... uvd->dp.length + * + * if u_lo < 0 then there is no first filler. + */ + + q_used = RingQueue_GetLength(&uvd->dp); + if ((uvd->dp.ri + q_used) >= uvd->dp.length) { + u_hi = uvd->dp.length; + u_lo = (q_used + uvd->dp.ri) % uvd->dp.length; + } else { + u_hi = (q_used + uvd->dp.ri); + u_lo = -1; + } + + /* Convert byte indices into screen units */ + m_ri = qi_x1 + ((barLength * uvd->dp.ri) / uvd->dp.length); + m_wi = qi_x1 + ((barLength * uvd->dp.wi) / uvd->dp.length); + m_lo = (u_lo > 0) ? (qi_x1 + ((barLength * u_lo) / uvd->dp.length)) : -1; + m_hi = qi_x1 + ((barLength * u_hi) / uvd->dp.length); + + for (j=qi_y1; j < (qi_y1 + qi_h); j++) { + for (i=qi_x1; i < qi_x2; i++) { + /* Draw border lines */ + if ((j == qi_y1) || (j == (qi_y1 + qi_h - 1)) || + (i == qi_x1) || (i == (qi_x2 - 1))) { + RGB24_PUTPIXEL(frame, i, j, 0xFF, 0xFF, 0xFF); + continue; + } + /* For all other points the Y coordinate does not matter */ + if ((i >= m_ri) && (i <= (m_ri + 3))) { + RGB24_PUTPIXEL(frame, i, j, 0x00, 0xFF, 0x00); + } else if ((i >= m_wi) && (i <= (m_wi + 3))) { + RGB24_PUTPIXEL(frame, i, j, 0xFF, 0x00, 0x00); + } else if ((i < m_lo) || ((i > m_ri) && (i < m_hi))) + RGB24_PUTPIXEL(frame, i, j, 0x00, 0x00, 0xFF); + } + } + } + + sprintf(tmp, "%8lx", uvd->stats.frame_num); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.urb_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.urb_length); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.data_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.header_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.iso_skip_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.iso_err_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.colour); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.hue); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.brightness >> 8); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.contrast >> 12); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8d", uvd->vpic.whiteness >> 8); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; +} + +/* + * usbvideo_ReportStatistics() + * + * This procedure prints packet and transfer statistics. + * + * History: + * 14-Jan-2000 Corrected default multiplier. + */ +void usbvideo_ReportStatistics(const uvd_t *uvd) +{ + if ((uvd != NULL) && (uvd->stats.urb_count > 0)) { + unsigned long allPackets, badPackets, goodPackets, percent; + allPackets = uvd->stats.urb_count * CAMERA_URB_FRAMES; + badPackets = uvd->stats.iso_skip_count + uvd->stats.iso_err_count; + goodPackets = allPackets - badPackets; + /* Calculate percentage wisely, remember integer limits */ + assert(allPackets != 0); + if (goodPackets < (((unsigned long)-1)/100)) + percent = (100 * goodPackets) / allPackets; + else + percent = goodPackets / (allPackets / 100); + info("Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%", + allPackets, badPackets, percent); + if (uvd->iso_packet_len > 0) { + unsigned long allBytes, xferBytes; + char multiplier = ' '; + allBytes = allPackets * uvd->iso_packet_len; + xferBytes = uvd->stats.data_count; + assert(allBytes != 0); + if (xferBytes < (((unsigned long)-1)/100)) + percent = (100 * xferBytes) / allBytes; + else + percent = xferBytes / (allBytes / 100); + /* Scale xferBytes for easy reading */ + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'K'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'M'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'G'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'T'; + } + } + } + } + info("Transfer Statistics: Transferred=%lu%cB Usage=%lu%%", + xferBytes, multiplier, percent); + } + } +} + +/* + * usbvideo_DrawLine() + * + * A standard implementation of Bresenham's line drawing algorithm. + * This procedure is provided primarily for debugging or demo + * purposes. + */ +void usbvideo_DrawLine( + usbvideo_frame_t *frame, + int x1, int y1, + int x2, int y2, + unsigned char cr, unsigned char cg, unsigned char cb) +{ + int i, dx, dy, np, d; + int dinc1, dinc2, x, xinc1, xinc2, y, yinc1, yinc2; + + if ((dx = x2 - x1) < 0) + dx = -dx; + if ((dy = y2 - y1) < 0) + dy = -dy; + if (dx >= dy) { + np = dx + 1; + d = (2 * dy) - dx; + dinc1 = dy << 1; + dinc2 = (dy - dx) << 1; + xinc1 = 1; + xinc2 = 1; + yinc1 = 0; + yinc2 = 1; + } else { + np = dy + 1; + d = (2 * dx) - dy; + dinc1 = dx << 1; + dinc2 = (dx - dy) << 1; + xinc1 = 0; + xinc2 = 1; + yinc1 = 1; + yinc2 = 1; + } + /* Make sure x and y move in the right directions */ + if (x1 > x2) { + xinc1 = -xinc1; + xinc2 = -xinc2; + } + if (y1 > y2) { + yinc1 = -yinc1; + yinc2 = -yinc2; + } + for (i=0, x=x1, y=y1; i < np; i++) { + if (frame->palette == VIDEO_PALETTE_RGB24) { +/* TODO */ RGB24_PUTPIXEL(frame, x, y, cr, cg, cb); + } + if (d < 0) { + d += dinc1; + x += xinc1; + y += yinc1; + } else { + d += dinc2; + x += xinc2; + y += yinc2; + } + } +} + +/* + * usbvideo_TestPattern() + * + * Procedure forms a test pattern (yellow grid on blue background). + * + * Parameters: + * fullframe: if TRUE then entire frame is filled, otherwise the procedure + * continues from the current scanline. + * pmode 0: fill the frame with solid blue color (like on VCR or TV) + * 1: Draw a colored grid + * + * History: + * 01-Feb-2000 Created. + */ +void usbvideo_TestPattern(uvd_t *uvd, int fullframe, int pmode) +{ + static const char proc[] = "usbvideo_TestPattern"; + usbvideo_frame_t *frame; + int num_cell = 0; + int scan_length = 0; + static int num_pass = 0; + + if (uvd == NULL) { + err("%s: uvd == NULL", proc); + return; + } + if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { + err("%s: uvd->curframe=%d.", proc, uvd->curframe); + return; + } + + /* Grab the current frame */ + frame = &uvd->frame[uvd->curframe]; + + /* Optionally start at the beginning */ + if (fullframe) { + frame->curline = 0; + frame->seqRead_Length = 0; + } +#if 0 + { /* For debugging purposes only */ + char tmp[20]; + usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request); + info("testpattern: frame=%s", tmp); + } +#endif + /* Form every scan line */ + for (; frame->curline < VIDEOSIZE_Y(frame->request); frame->curline++) { + int i; + unsigned char *f = frame->data + + (VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL * frame->curline); + for (i=0; i < VIDEOSIZE_X(frame->request); i++) { + unsigned char cb=0x80; + unsigned char cg = 0; + unsigned char cr = 0; + + if (pmode == 1) { + if (frame->curline % 32 == 0) + cb = 0, cg = cr = 0xFF; + else if (i % 32 == 0) { + if (frame->curline % 32 == 1) + num_cell++; + cb = 0, cg = cr = 0xFF; + } else { + cb = ((num_cell*7) + num_pass) & 0xFF; + cg = ((num_cell*5) + num_pass*2) & 0xFF; + cr = ((num_cell*3) + num_pass*3) & 0xFF; + } + } else { + /* Just the blue screen */ + } + + *f++ = cb; + *f++ = cg; + *f++ = cr; + scan_length += 3; + } + } + + frame->frameState = FrameState_Done; + frame->seqRead_Length += scan_length; + ++num_pass; + + /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */ + usbvideo_OverlayStats(uvd, frame); +} + +/* + * usbvideo_HexDump() + * + * A debugging tool. Prints hex dumps. + * + * History: + * 29-Jul-2000 Added printing of offsets. + */ +void usbvideo_HexDump(const unsigned char *data, int len) +{ + const int bytes_per_line = 32; + char tmp[128]; /* 32*3 + 5 */ + int i, k; + + for (i=k=0; len > 0; i++, len--) { + if (i > 0 && ((i % bytes_per_line) == 0)) { + printk("%s\n", tmp); + k=0; + } + if ((i % bytes_per_line) == 0) + k += sprintf(&tmp[k], "%04x: ", i); + k += sprintf(&tmp[k], "%02x ", data[i]); + } + if (k > 0) + printk("%s\n", tmp); +} + +/* Debugging aid */ +void usbvideo_SayAndWait(const char *what) +{ + wait_queue_head_t wq; + init_waitqueue_head(&wq); + info("Say: %s", what); + interruptible_sleep_on_timeout (&wq, HZ*3); /* Timeout */ +} + +/* ******************************************************************** */ + +static void usbvideo_ClientIncModCount(uvd_t *uvd) +{ + static const char proc[] = "usbvideo_ClientIncModCount"; + if (uvd == NULL) { + err("%s: uvd == NULL", proc); + return; + } + if (uvd->handle == NULL) { + err("%s: uvd->handle == NULL", proc); + return; + } + if (uvd->handle->md_module == NULL) { + err("%s: uvd->handle->md_module == NULL", proc); + return; + } + __MOD_INC_USE_COUNT(uvd->handle->md_module); +} + +static void usbvideo_ClientDecModCount(uvd_t *uvd) +{ + static const char proc[] = "usbvideo_ClientDecModCount"; + if (uvd == NULL) { + err("%s: uvd == NULL", proc); + return; + } + if (uvd->handle == NULL) { + err("%s: uvd->handle == NULL", proc); + return; + } + if (uvd->handle->md_module == NULL) { + err("%s: uvd->handle->md_module == NULL", proc); + return; + } + __MOD_DEC_USE_COUNT(uvd->handle->md_module); +} + +int usbvideo_register( + usbvideo_t **pCams, + const int num_cams, + const int num_extra, + const char *driverName, + const usbvideo_cb_t *cbTbl, + struct module *md ) +{ + static const char proc[] = "usbvideo_register"; + usbvideo_t *cams; + int i, base_size; + + /* Check parameters for sanity */ + if ((num_cams <= 0) || (pCams == NULL) || (cbTbl == NULL)) { + err("%s: Illegal call", proc); + return -EINVAL; + } + + /* Check registration callback - must be set! */ + if (cbTbl->probe == NULL) { + err("%s: probe() is required!", proc); + return -EINVAL; + } + + base_size = num_cams * sizeof(uvd_t) + sizeof(usbvideo_t); + cams = (usbvideo_t *) kmalloc(base_size, GFP_KERNEL); + if (cams == NULL) { + err("Failed to allocate %d. bytes for usbvideo_t", base_size); + return -ENOMEM; + } + dbg("%s: Allocated $%p (%d. bytes) for %d. cameras", + proc, cams, base_size, num_cams); + memset(cams, 0, base_size); + + /* Copy callbacks, apply defaults for those that are not set */ + memmove(&cams->cb, cbTbl, sizeof(cams->cb)); + if (cams->cb.getFrame == NULL) + cams->cb.getFrame = usbvideo_GetFrame; + if (cams->cb.disconnect == NULL) + cams->cb.disconnect = usbvideo_Disconnect; +#if USES_PROC_FS + /* + * If both /proc fs callbacks are NULL then we assume that the driver + * does not need procfs services at all. Leave them NULL. + */ + cams->uses_procfs = (cams->cb.procfs_read != NULL) || (cams->cb.procfs_write == NULL); + if (cams->uses_procfs) { + if (cams->cb.procfs_read == NULL) + cams->cb.procfs_read = usbvideo_default_procfs_read_proc; + if (cams->cb.procfs_write == NULL) + cams->cb.procfs_write = usbvideo_default_procfs_write_proc; + } +#else /* !USES_PROC_FS */ + /* Report a warning so that user knows why there is no /proc entries */ + if ((cams->cb.procfs_read != NULL) || (cams->cb.procfs_write == NULL)) { + dbg("%s: /proc fs support requested but not configured!", proc); + } +#endif + cams->num_cameras = num_cams; + cams->cam = (uvd_t *) &cams[1]; + cams->md_module = md; + if (cams->md_module == NULL) + warn("%s: module == NULL!", proc); + init_MUTEX(&cams->lock); /* to 1 == available */ + + for (i = 0; i < num_cams; i++) { + uvd_t *up = &cams->cam[i]; + + up->handle = cams; + + /* Allocate user_data separately because of kmalloc's limits */ + if (num_extra > 0) { + up->user_size = num_cams * num_extra; + up->user_data = (char *) kmalloc(up->user_size, GFP_KERNEL); + if (up->user_data == NULL) { + up->user_size = 0; + err("%s: Failed to allocate user_data (%d. bytes)", + proc, up->user_size); + return -ENOMEM; + } + dbg("%s: Allocated cams[%d].user_data=$%p (%d. bytes)", + proc, i, up->user_data, up->user_size); + } + } + + /* + * Register ourselves with USB stack. + */ + strcpy(cams->drvName, (driverName != NULL) ? driverName : "Unknown"); + cams->usbdrv.name = cams->drvName; + cams->usbdrv.probe = cams->cb.probe; + cams->usbdrv.disconnect = cams->cb.disconnect; + +#if USES_PROC_FS + if (cams->uses_procfs) { + dbg("%s: Creating /proc filesystem entries.", proc); + usbvideo_procfs_level1_create(cams); + } +#endif + + /* + * Update global handle to usbvideo. This is very important + * because probe() can be called before usb_register() returns. + * If the handle is not yet updated then the probe() will fail. + */ + *pCams = cams; + usb_register(&cams->usbdrv); + + return 0; +} + +/* + * usbvideo_Deregister() + * + * Procedure frees all usbvideo and user data structures. Be warned that + * if you had some dynamically allocated components in ->user field then + * you should free them before calling here. + */ +void usbvideo_Deregister(usbvideo_t **pCams) +{ + static const char proc[] = "usbvideo_deregister"; + usbvideo_t *cams; + int i; + + if (pCams == NULL) { + err("%s: pCams == NULL", proc); + return; + } + cams = *pCams; + if (cams == NULL) { + err("%s: cams == NULL", proc); + return; + } + +#if USES_PROC_FS + if (cams->uses_procfs) { + dbg("%s: Deregistering filesystem entries.", proc); + usbvideo_procfs_level1_destroy(cams); + } +#endif + + dbg("%s: Deregistering %s driver.", proc, cams->drvName); + usb_deregister(&cams->usbdrv); + + dbg("%s: Deallocating cams=$%p (%d. cameras)", proc, cams, cams->num_cameras); + for (i=0; i < cams->num_cameras; i++) { + uvd_t *up = &cams->cam[i]; + int warning = 0; + + if (up->user_data != NULL) { + if (up->user_size <= 0) + ++warning; + } else { + if (up->user_size > 0) + ++warning; + } + if (warning) { + err("%s: Warning: user_data=$%p user_size=%d.", + proc, up->user_data, up->user_size); + } else { + dbg("%s: Freeing %d. $%p->user_data=$%p", + proc, i, up, up->user_data); + kfree(up->user_data); + } + } + /* Whole array was allocated in one chunk */ + dbg("%s: Freed %d uvd_t structures", + proc, cams->num_cameras); + kfree(cams); + *pCams = NULL; +} + +/* + * usbvideo_Disconnect() + * + * This procedure stops all driver activity. Deallocation of + * the interface-private structure (pointed by 'ptr') is done now + * (if we don't have any open files) or later, when those files + * are closed. After that driver should be removable. + * + * This code handles surprise removal. The uvd->user is a counter which + * increments on open() and decrements on close(). If we see here that + * this counter is not 0 then we have a client who still has us opened. + * We set uvd->remove_pending flag as early as possible, and after that + * all access to the camera will gracefully fail. These failures should + * prompt client to (eventually) close the video device, and then - in + * usbvideo_v4l_close() - we decrement uvd->uvd_used and usage counter. + * + * History: + * 22-Jan-2000 Added polling of MOD_IN_USE to delay removal until all users gone. + * 27-Jan-2000 Reworked to allow pending disconnects; see xxx_close() + * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + * 19-Oct-2000 Moved to usbvideo module. + */ +void usbvideo_Disconnect(struct usb_device *dev, void *ptr) +{ + static const char proc[] = "usbvideo_Disconnect"; + uvd_t *uvd = (uvd_t *) ptr; + int i; + + if ((dev == NULL) || (uvd == NULL)) { + err("%s($%p,$%p): Illegal call.", proc, dev, ptr); + return; + } + usbvideo_ClientIncModCount(uvd); + if (uvd->debug > 0) + info("%s(%p,%p.)", proc, dev, ptr); + + down(&uvd->lock); + uvd->remove_pending = 1; /* Now all ISO data will be ignored */ + + /* At this time we ask to cancel outstanding URBs */ + usbvideo_StopDataPump(uvd); + + for (i=0; i < USBVIDEO_NUMSBUF; i++) + usb_free_urb(uvd->sbuf[i].urb); + + usb_dec_dev_use(uvd->dev); + uvd->dev = NULL; /* USB device is no more */ + + if (uvd->user) + info("%s: In use, disconnect pending.", proc); + else + usbvideo_CameraRelease(uvd); + up(&uvd->lock); + info("USB camera disconnected."); + + usbvideo_ClientDecModCount(uvd); +} + +/* + * usbvideo_CameraRelease() + * + * This code does final release of uvd_t. This happens + * after the device is disconnected -and- all clients + * closed their files. + * + * History: + * 27-Jan-2000 Created. + */ +void usbvideo_CameraRelease(uvd_t *uvd) +{ + static const char proc[] = "usbvideo_CameraRelease"; + if (uvd == NULL) { + err("%s: Illegal call", proc); + return; + } + video_unregister_device(&uvd->vdev); + if (uvd->debug > 0) + info("%s: Video unregistered.", proc); + +#if USES_PROC_FS + assert(uvd->handle != NULL); + if (uvd->handle->uses_procfs) { + dbg("%s: Removing /proc/%s/ filesystem entries.", proc, uvd->handle->drvName); + usbvideo_procfs_level2_destroy(uvd); + } +#endif + + RingQueue_Free(&uvd->dp); + if (VALID_CALLBACK(uvd, userFree)) + GET_CALLBACK(uvd, userFree)(uvd); + uvd->uvd_used = 0; /* This is atomic, no need to take mutex */ +} + +/* + * usbvideo_find_struct() + * + * This code searches the array of preallocated (static) structures + * and returns index of the first one that isn't in use. Returns -1 + * if there are no free structures. + * + * History: + * 27-Jan-2000 Created. + */ +static int usbvideo_find_struct(usbvideo_t *cams) +{ + int u, rv = -1; + + if (cams == NULL) { + err("No usbvideo_t handle?"); + return -1; + } + down(&cams->lock); + for (u = 0; u < cams->num_cameras; u++) { + uvd_t *uvd = &cams->cam[u]; + if (!uvd->uvd_used) /* This one is free */ + { + uvd->uvd_used = 1; /* In use now */ + init_MUTEX(&uvd->lock); /* to 1 == available */ + uvd->dev = NULL; + rv = u; + break; + } + } + up(&cams->lock); + return rv; +} + +uvd_t *usbvideo_AllocateDevice(usbvideo_t *cams) +{ + int i, devnum; + uvd_t *uvd = NULL; + + if (cams == NULL) { + err("No usbvideo_t handle?"); + return NULL; + } + + devnum = usbvideo_find_struct(cams); + if (devnum == -1) { + err("IBM USB camera driver: Too many devices!"); + return NULL; + } + uvd = &cams->cam[devnum]; + dbg("Device entry #%d. at $%p", devnum, uvd); + + /* Not relying upon caller we increase module counter ourselves */ + usbvideo_ClientIncModCount(uvd); + + down(&uvd->lock); + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC); + if (uvd->sbuf[i].urb == NULL) { + err("usb_alloc_urb(%d.) failed.", FRAMES_PER_DESC); + uvd->uvd_used = 0; + uvd = NULL; + goto allocate_done; + } + } + uvd->user=0; + uvd->remove_pending = 0; + uvd->last_error = 0; + RingQueue_Initialize(&uvd->dp); + + /* Initialize video device structure */ + memset(&uvd->vdev, 0, sizeof(uvd->vdev)); + i = sprintf(uvd->vdev.name, "%s USB Camera", cams->drvName); + if (i >= sizeof(uvd->vdev.name)) { + err("Wrote too much into uvd->vdev.name, expect trouble!"); + } + uvd->vdev.type = VID_TYPE_CAPTURE; + uvd->vdev.hardware = VID_HARDWARE_CPIA; + uvd->vdev.open = usbvideo_v4l_open; + uvd->vdev.close = usbvideo_v4l_close; + uvd->vdev.read = usbvideo_v4l_read; + uvd->vdev.write = usbvideo_v4l_write; + uvd->vdev.ioctl = usbvideo_v4l_ioctl; + uvd->vdev.mmap = usbvideo_v4l_mmap; + uvd->vdev.initialize = usbvideo_v4l_initialize; + /* + * The client is free to overwrite those because we + * return control to the client's probe function right now. + */ +allocate_done: + up (&uvd->lock); + usbvideo_ClientDecModCount(uvd); + return uvd; +} + +int usbvideo_RegisterVideoDevice(uvd_t *uvd) +{ + static const char proc[] = "usbvideo_RegisterVideoDevice"; + char tmp1[20], tmp2[20]; /* Buffers for printing */ + + if (uvd == NULL) { + err("%s: Illegal call.", proc); + return -EINVAL; + } + if (uvd->video_endp == 0) { + info("%s: No video endpoint specified; data pump disabled.", proc); + } + if (uvd->paletteBits == 0) { + err("%s: No palettes specified!", proc); + return -EINVAL; + } + if (uvd->defaultPalette == 0) { + info("%s: No default palette!", proc); + } + + uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) * + VIDEOSIZE_Y(uvd->canvas) * V4L_BYTES_PER_PIXEL; + usbvideo_VideosizeToString(tmp1, sizeof(tmp1), uvd->videosize); + usbvideo_VideosizeToString(tmp2, sizeof(tmp2), uvd->canvas); + + if (uvd->debug > 0) { + info("%s: iface=%d. endpoint=$%02x paletteBits=$%08lx", + proc, uvd->iface, uvd->video_endp, uvd->paletteBits); + } + if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + err("%s: video_register_device failed", proc); + return -EPIPE; + } + if (uvd->debug > 1) { + info("%s: video_register_device() successful", proc); + } + if (uvd->dev == NULL) { + err("%s: uvd->dev == NULL", proc); + return -EINVAL; + } + + info("%s on /dev/video%d: canvas=%s videosize=%s", + (uvd->handle != NULL) ? uvd->handle->drvName : "???", + uvd->vdev.minor, tmp2, tmp1); + +#if USES_PROC_FS + assert(uvd->handle != NULL); + if (uvd->handle->uses_procfs) { + if (uvd->debug > 0) { + info("%s: Creating /proc/%s/ filesystem entries.", + proc, uvd->handle->drvName); + } + usbvideo_procfs_level2_create(uvd); + } +#endif + + usb_inc_dev_use(uvd->dev); + return 0; +} + +/* ******************************************************************** */ + +int usbvideo_v4l_initialize(struct video_device *dev) +{ + return 0; +} + +long usbvideo_v4l_write(struct video_device *dev, const char *buf, + unsigned long count, int noblock) +{ + return -EINVAL; +} + +int usbvideo_v4l_mmap(struct video_device *dev, const char *adr, unsigned long size) +{ + uvd_t *uvd = (uvd_t *) dev; + unsigned long start = (unsigned long) adr; + unsigned long page, pos; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return -EFAULT; + + if (size > (((2 * uvd->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + return -EINVAL; + + pos = (unsigned long) uvd->fbuf; + while (size > 0) { + page = usbvideo_kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +/* + * usbvideo_v4l_open() + * + * This is part of Video 4 Linux API. The driver can be opened by one + * client only (checks internal counter 'uvdser'). The procedure + * then allocates buffers needed for video processing. + * + * History: + * 22-Jan-2000 Rewrote, moved scratch buffer allocation here. Now the + * camera is also initialized here (once per connect), at + * expense of V4L client (it waits on open() call). + * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. + * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + */ +int usbvideo_v4l_open(struct video_device *dev, int flags) +{ + static const char proc[] = "usbvideo_v4l_open"; + uvd_t *uvd = (uvd_t *) dev; + const int sb_size = FRAMES_PER_DESC * uvd->iso_packet_len; + int i, errCode = 0; + + if (uvd->debug > 1) + info("%s($%p,$%08x", proc, dev, flags); + + usbvideo_ClientIncModCount(uvd); + down(&uvd->lock); + + if (uvd->user) { + err("%s: Someone tried to open an already opened device!", proc); + errCode = -EBUSY; + } else { + /* Clear statistics */ + memset(&uvd->stats, 0, sizeof(uvd->stats)); + + /* Clean pointers so we know if we allocated something */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) + uvd->sbuf[i].data = NULL; + + /* Allocate memory for the frame buffers */ + uvd->fbuf_size = USBVIDEO_NUMFRAMES * uvd->max_frame_size; + uvd->fbuf = usbvideo_rvmalloc(uvd->fbuf_size); + RingQueue_Allocate(&uvd->dp, 128*1024); /* FIXME #define */ + if ((uvd->fbuf == NULL) || + (!RingQueue_IsAllocated(&uvd->dp))) { + err("%s: Failed to allocate fbuf or dp", proc); + errCode = -ENOMEM; + } else { + /* Allocate all buffers */ + for (i=0; i < USBVIDEO_NUMFRAMES; i++) { + uvd->frame[i].frameState = FrameState_Unused; + uvd->frame[i].data = uvd->fbuf + i*(uvd->max_frame_size); + /* + * Set default sizes in case IOCTL (VIDIOCMCAPTURE) + * is not used (using read() instead). + */ + uvd->frame[i].canvas = uvd->canvas; + uvd->frame[i].seqRead_Index = 0; + } + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + uvd->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); + if (uvd->sbuf[i].data == NULL) { + errCode = -ENOMEM; + break; + } + } + } + if (errCode != 0) { + /* Have to free all that memory */ + if (uvd->fbuf != NULL) { + usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); + uvd->fbuf = NULL; + } + RingQueue_Free(&uvd->dp); + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + if (uvd->sbuf[i].data != NULL) { + kfree (uvd->sbuf[i].data); + uvd->sbuf[i].data = NULL; + } + } + } + } + + /* If so far no errors then we shall start the camera */ + if (errCode == 0) { + /* Start data pump if we have valid endpoint */ + if (uvd->video_endp != 0) + errCode = usbvideo_StartDataPump(uvd); + if (errCode == 0) { + if (VALID_CALLBACK(uvd, setupOnOpen)) { + if (uvd->debug > 1) + info("%s: setupOnOpen callback", proc); + errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd); + if (errCode < 0) { + err("%s: setupOnOpen callback failed (%d.).", + proc, errCode); + } else if (uvd->debug > 1) { + info("%s: setupOnOpen callback successful", proc); + } + } + if (errCode == 0) { + uvd->settingsAdjusted = 0; + if (uvd->debug > 1) + info("%s: Open succeeded.", proc); + uvd->user++; + } + } + } + up(&uvd->lock); + if (errCode != 0) + usbvideo_ClientDecModCount(uvd); + if (uvd->debug > 0) + info("%s: Returning %d.", proc, errCode); + return errCode; +} + +/* + * usbvideo_v4l_close() + * + * This is part of Video 4 Linux API. The procedure + * stops streaming and deallocates all buffers that were earlier + * allocated in usbvideo_v4l_open(). + * + * History: + * 22-Jan-2000 Moved scratch buffer deallocation here. + * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. + * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep. + */ +void usbvideo_v4l_close(struct video_device *dev) +{ + static const char proc[] = "usbvideo_v4l_close"; + uvd_t *uvd = (uvd_t *)dev; + int i; + + if (uvd->debug > 1) + info("%s($%p)", proc, dev); + + down(&uvd->lock); + usbvideo_StopDataPump(uvd); + usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); + uvd->fbuf = NULL; + RingQueue_Free(&uvd->dp); + + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + kfree(uvd->sbuf[i].data); + uvd->sbuf[i].data = NULL; + } + +#if USBVIDEO_REPORT_STATS + usbvideo_ReportStatistics(uvd); +#endif + + uvd->user--; + if (uvd->remove_pending) { + if (uvd->debug > 0) + info("usbvideo_v4l_close: Final disconnect."); + usbvideo_CameraRelease(uvd); + } + up(&uvd->lock); + usbvideo_ClientDecModCount(uvd); + + if (uvd->debug > 1) + info("%s: Completed.", proc); +} + +/* + * usbvideo_v4l_ioctl() + * + * This is part of Video 4 Linux API. The procedure handles ioctl() calls. + * + * History: + * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings. + */ +int usbvideo_v4l_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + uvd_t *uvd = (uvd_t *)dev; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return -EFAULT; + + switch (cmd) { + case VIDIOCGCAP: + { + if (copy_to_user(arg, &uvd->vcap, sizeof(uvd->vcap))) + return -EFAULT; + return 0; + } + case VIDIOCGCHAN: + { + if (copy_to_user(arg, &uvd->vchan, sizeof(uvd->vchan))) + return -EFAULT; + return 0; + } + case VIDIOCSCHAN: + { /* Not used but we return success */ + int v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGPICT: + { + if (copy_to_user(arg, &uvd->vpic, sizeof(uvd->vpic))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture tmp; + /* + * Use temporary 'video_picture' structure to preserve our + * own settings (such as color depth, palette) that we + * aren't allowing everyone (V4L client) to change. + */ + if (copy_from_user(&tmp, arg, sizeof(tmp))) + return -EFAULT; + uvd->vpic.brightness = tmp.brightness; + uvd->vpic.hue = tmp.hue; + uvd->vpic.colour = tmp.colour; + uvd->vpic.contrast = tmp.contrast; + uvd->settingsAdjusted = 0; /* Will force new settings */ + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + + if (copy_from_user(&vw, arg, sizeof(vw))) + return -EFAULT; + if (vw.flags) + return -EINVAL; + if (vw.clipcount) + return -EINVAL; + if (vw.width != VIDEOSIZE_X(uvd->canvas)) + return -EINVAL; + if (vw.height != VIDEOSIZE_Y(uvd->canvas)) + return -EINVAL; + + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + + vw.x = 0; + vw.y = 0; + vw.width = VIDEOSIZE_X(uvd->canvas); + vw.height = VIDEOSIZE_Y(uvd->canvas); + vw.chromakey = 0; + if (VALID_CALLBACK(uvd, getFPS)) + vw.flags = GET_CALLBACK(uvd, getFPS)(uvd); + else + vw.flags = 10; /* FIXME: do better! */ + + if (copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf vm; + + memset(&vm, 0, sizeof(vm)); + vm.size = uvd->max_frame_size * 2; + vm.frames = 2; + vm.offsets[0] = 0; + vm.offsets[1] = uvd->max_frame_size; + + if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) + return -EFAULT; + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) { + err("VIDIOCMCAPTURE: copy_from_user() failed."); + return -EFAULT; + } + if (uvd->debug >= 1) { + info("VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.", + vm.frame, vm.width, vm.height, vm.format); + } + /* + * Check if the requested size is supported. If the requestor + * requests too big a frame then we may be tricked into accessing + * outside of own preallocated frame buffer (in uvd->frame). + * This will cause oops or a security hole. Theoretically, we + * could only clamp the size down to acceptable bounds, but then + * we'd need to figure out how to insert our smaller buffer into + * larger caller's buffer... this is not an easy question. So we + * here just flatly reject too large requests, assuming that the + * caller will resubmit with smaller size. Callers should know + * what size we support (returned by VIDIOCGCAP). However vidcat, + * for one, does not care and allows to ask for any size. + */ + if ((vm.width > VIDEOSIZE_X(uvd->canvas)) || + (vm.height > VIDEOSIZE_Y(uvd->canvas))) { + if (uvd->debug > 0) { + info("VIDIOCMCAPTURE: Size=%dx%d too large; " + "allowed only up to %ldx%ld", vm.width, vm.height, + VIDEOSIZE_X(uvd->canvas), VIDEOSIZE_Y(uvd->canvas)); + } + return -EINVAL; + } + /* Check if the palette is supported */ + if (((1L << vm.format) & uvd->paletteBits) == 0) { + if (uvd->debug > 0) { + info("VIDIOCMCAPTURE: format=%d. not supported" + " (paletteBits=$%08lx)", + vm.format, uvd->paletteBits); + } + return -EINVAL; + } + if ((vm.frame != 0) && (vm.frame != 1)) { + err("VIDIOCMCAPTURE: vm.frame=%d. !E [0,1]", vm.frame); + return -EINVAL; + } + if (uvd->frame[vm.frame].frameState == FrameState_Grabbing) { + /* Not an error - can happen */ + } + uvd->frame[vm.frame].request = VIDEOSIZE(vm.width, vm.height); + uvd->frame[vm.frame].palette = vm.format; + + /* Mark it as ready */ + uvd->frame[vm.frame].frameState = FrameState_Ready; + + return usbvideo_NewFrame(uvd, vm.frame); + } + case VIDIOCSYNC: + { + int frameNum, ret; + + if (copy_from_user((void *)&frameNum, arg, sizeof(frameNum))) { + err("VIDIOCSYNC: copy_from_user() failed."); + return -EFAULT; + } + if (uvd->debug >= 1) + info("VIDIOCSYNC: syncing to frame %d.", frameNum); + if (uvd->flags & FLAGS_NO_DECODING) + ret = usbvideo_GetFrame(uvd, frameNum); + else if (VALID_CALLBACK(uvd, getFrame)) { + ret = GET_CALLBACK(uvd, getFrame)(uvd, frameNum); + if ((ret < 0) && (uvd->debug >= 1)) { + err("VIDIOCSYNC: getFrame() returned %d.", ret); + } + } else { + err("VIDIOCSYNC: getFrame is not set"); + ret = -EFAULT; + } + + /* + * The frame is in FrameState_Done_Hold state. Release it + * right now because its data is already mapped into + * the user space and it's up to the application to + * make use of it until it asks for another frame. + */ + uvd->frame[frameNum].frameState = FrameState_Unused; + return ret; + } + case VIDIOCGFBUF: + { + struct video_buffer vb; + + memset(&vb, 0, sizeof(vb)); + vb.base = NULL; /* frame buffer not supported, not used */ + + if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) + return -EFAULT; + + return 0; + } + case VIDIOCKEY: + return 0; + + case VIDIOCCAPTURE: + return -EINVAL; + + case VIDIOCSFBUF: + + case VIDIOCGTUNER: + case VIDIOCSTUNER: + + case VIDIOCGFREQ: + case VIDIOCSFREQ: + + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* + * usbvideo_v4l_read() + * + * This is mostly boring stuff. We simply ask for a frame and when it + * arrives copy all the video data from it into user space. There is + * no obvious need to override this method. + * + * History: + * 20-Oct-2000 Created. + * 01-Nov-2000 Added mutex (uvd->lock). + */ +long usbvideo_v4l_read(struct video_device *dev, char *buf, unsigned long count, int noblock) +{ + static const char proc[] = "usbvideo_v4l_read"; + uvd_t *uvd = (uvd_t *) dev; + int frmx = -1; + usbvideo_frame_t *frame; + + if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL)) + return -EFAULT; + + if (uvd->debug >= 1) + info("%s: %ld. bytes, noblock=%d.", proc, count, noblock); + + down(&uvd->lock); + + /* See if a frame is completed, then use it. */ + if ((uvd->frame[0].frameState == FrameState_Done) || + (uvd->frame[0].frameState == FrameState_Done_Hold) || + (uvd->frame[0].frameState == FrameState_Error)) { + frmx = 0; + } else if ((uvd->frame[1].frameState >= FrameState_Done) || + (uvd->frame[1].frameState == FrameState_Done_Hold) || + (uvd->frame[1].frameState >= FrameState_Done)) { + frmx = 1; + } + + /* FIXME: If we don't start a frame here then who ever does? */ + if (noblock && (frmx == -1)) { + count = -EAGAIN; + goto read_done; + } + + /* + * If no FrameState_Done, look for a FrameState_Grabbing state. + * See if a frame is in process (grabbing), then use it. + * We will need to wait until it becomes cooked, of course. + */ + if (frmx == -1) { + if (uvd->frame[0].frameState == FrameState_Grabbing) + frmx = 0; + else if (uvd->frame[1].frameState == FrameState_Grabbing) + frmx = 1; + } + + /* + * If no frame is active, start one. We don't care which one + * it will be, so #0 is as good as any. + * In read access mode we don't have convenience of VIDIOCMCAPTURE + * to specify the requested palette (video format) on per-frame + * basis. This means that we have to return data in -some- format + * and just hope that the client knows what to do with it. + * The default format is configured in uvd->defaultPalette field + * as one of VIDEO_PALETTE_xxx values. We stuff it into the new + * frame and initiate the frame filling process. + */ + if (frmx == -1) { + if (uvd->defaultPalette == 0) { + err("%s: No default palette; don't know what to do!", proc); + count = -EFAULT; + goto read_done; + } + frmx = 0; + /* + * We have no per-frame control over video size. + * Therefore we only can use whatever size was + * specified as default. + */ + uvd->frame[frmx].request = uvd->videosize; + uvd->frame[frmx].palette = uvd->defaultPalette; + uvd->frame[frmx].frameState = FrameState_Ready; + usbvideo_NewFrame(uvd, frmx); + /* Now frame 0 is supposed to start filling... */ + } + + /* + * Get a pointer to the active frame. It is either previously + * completed frame or frame in progress but not completed yet. + */ + frame = &uvd->frame[frmx]; + + /* + * Sit back & wait until the frame gets filled and postprocessed. + * If we fail to get the picture [in time] then return the error. + * In this call we specify that we want the frame to be waited for, + * postprocessed and switched into FrameState_Done_Hold state. This + * state is used to hold the frame as "fully completed" between + * subsequent partial reads of the same frame. + */ + if (frame->frameState != FrameState_Done_Hold) { + long rv = -EFAULT; + if (uvd->flags & FLAGS_NO_DECODING) + rv = usbvideo_GetFrame(uvd, frmx); + else if (VALID_CALLBACK(uvd, getFrame)) + rv = GET_CALLBACK(uvd, getFrame)(uvd, frmx); + else + err("getFrame is not set"); + if ((rv != 0) || (frame->frameState != FrameState_Done_Hold)) { + count = rv; + goto read_done; + } + } + + /* + * Copy bytes to user space. We allow for partial reads, which + * means that the user application can request read less than + * the full frame size. It is up to the application to issue + * subsequent calls until entire frame is read. + * + * First things first, make sure we don't copy more than we + * have - even if the application wants more. That would be + * a big security embarassment! + */ + if ((count + frame->seqRead_Index) > frame->seqRead_Length) + count = frame->seqRead_Length - frame->seqRead_Index; + + /* + * Copy requested amount of data to user space. We start + * copying from the position where we last left it, which + * will be zero for a new frame (not read before). + */ + if (copy_to_user(buf, frame->data + frame->seqRead_Index, count)) { + count = -EFAULT; + goto read_done; + } + + /* Update last read position */ + frame->seqRead_Index += count; + if (uvd->debug >= 1) { + err("%s: {copy} count used=%ld, new seqRead_Index=%ld", + proc, count, frame->seqRead_Index); + } + + /* Finally check if the frame is done with and "release" it */ + if (frame->seqRead_Index >= frame->seqRead_Length) { + /* All data has been read */ + frame->seqRead_Index = 0; + + /* Mark it as available to be used again. */ + uvd->frame[frmx].frameState = FrameState_Unused; + if (usbvideo_NewFrame(uvd, frmx ? 0 : 1)) { + err("%s: usbvideo_NewFrame failed.", proc); + } + } +read_done: + up(&uvd->lock); + return count; +} + +/* + * Make all of the blocks of data contiguous + */ +static int usbvideo_CompressIsochronous(uvd_t *uvd, urb_t *urb) +{ + char *cdata; + int i, totlen = 0; + + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (st < 0) { + if (uvd->debug >= 1) + err("Data error: packet=%d. len=%d. status=%d.", i, n, st); + uvd->stats.iso_err_count++; + continue; + } + + /* Detect and ignore empty packets */ + if (n <= 0) { + uvd->stats.iso_skip_count++; + continue; + } + totlen += n; /* Little local accounting */ + RingQueue_Enqueue(&uvd->dp, cdata, n); + } + return totlen; +} + +static void usbvideo_IsocIrq(struct urb *urb) +{ + int i, len; + uvd_t *uvd = urb->context; + + /* We don't want to do anything if we are about to be removed! */ + if (!CAMERA_IS_OPERATIONAL(uvd)) + return; +#if 0 + if (urb->actual_length > 0) { + info("urb=$%p status=%d. errcount=%d. length=%d.", + urb, urb->status, urb->error_count, urb->actual_length); + } else { + static int c = 0; + if (c++ % 100 == 0) + info("No Isoc data"); + } +#endif + + if (!uvd->streaming) { + if (uvd->debug >= 1) + info("Not streaming, but interrupt!"); + return; + } + + uvd->stats.urb_count++; + if (urb->actual_length <= 0) + goto urb_done_with; + + /* Copy the data received into ring queue */ + len = usbvideo_CompressIsochronous(uvd, urb); + uvd->stats.urb_length = len; + if (len <= 0) + goto urb_done_with; + + /* Here we got some data */ + uvd->stats.data_count += len; + RingQueue_WakeUpInterruptible(&uvd->dp); + +urb_done_with: + for (i = 0; i < FRAMES_PER_DESC; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + return; +} + +/* + * usbvideo_StartDataPump() + * + * History: + * 27-Jan-2000 Used ibmcam->iface, ibmcam->ifaceAltActive instead + * of hardcoded values. Simplified by using for loop, + * allowed any number of URBs. + */ +int usbvideo_StartDataPump(uvd_t *uvd) +{ + static const char proc[] = "usbvideo_StartDataPump"; + struct usb_device *dev = uvd->dev; + int i, errFlag; + + if (uvd->debug > 1) + info("%s($%p)", proc, uvd); + + if (!CAMERA_IS_OPERATIONAL(uvd)) { + err("%s: Camera is not operational",proc); + return -EFAULT; + } + uvd->curframe = -1; + + /* Alternate interface 1 is is the biggest frame size */ + i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); + if (i < 0) { + err("%s: usb_set_interface error", proc); + uvd->last_error = i; + return -EBUSY; + } + if (VALID_CALLBACK(uvd, videoStart)) + GET_CALLBACK(uvd, videoStart)(uvd); + else + err("%s: videoStart not set", proc); + + /* We double buffer the Iso lists */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + int j, k; + urb_t *urb = uvd->sbuf[i].urb; + urb->dev = dev; + urb->context = uvd; + urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = uvd->sbuf[i].data; + urb->complete = usbvideo_IsocIrq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC; + for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = uvd->iso_packet_len; + } + } + + /* Link URBs into a ring so that they invoke each other infinitely */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + if ((i+1) < USBVIDEO_NUMSBUF) + uvd->sbuf[i].urb->next = uvd->sbuf[i+1].urb; + else + uvd->sbuf[i].urb->next = uvd->sbuf[0].urb; + } + + /* Submit all URBs */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + errFlag = usb_submit_urb(uvd->sbuf[i].urb); + if (errFlag) + err("%s: usb_submit_isoc(%d) ret %d", proc, i, errFlag); + } + + uvd->streaming = 1; + if (uvd->debug > 1) + info("%s: streaming=1 video_endp=$%02x", proc, uvd->video_endp); + return 0; +} + +/* + * usbvideo_StopDataPump() + * + * This procedure stops streaming and deallocates URBs. Then it + * activates zero-bandwidth alt. setting of the video interface. + * + * History: + * 22-Jan-2000 Corrected order of actions to work after surprise removal. + * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values. + */ +void usbvideo_StopDataPump(uvd_t *uvd) +{ + static const char proc[] = "usbvideo_StopDataPump"; + int i, j; + + if (uvd->debug > 1) + info("%s($%p)", proc, uvd); + + if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) + return; + + /* Unschedule all of the iso td's */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + j = usb_unlink_urb(uvd->sbuf[i].urb); + if (j < 0) + err("%s: usb_unlink_urb() error %d.", proc, j); + } + if (uvd->debug > 1) + info("%s: streaming=0", proc); + uvd->streaming = 0; + + if (!uvd->remove_pending) { + /* Invoke minidriver's magic to stop the camera */ + if (VALID_CALLBACK(uvd, videoStop)) + GET_CALLBACK(uvd, videoStop)(uvd); + else + err("%s: videoStop not set" ,proc); + + /* Set packet size to 0 */ + j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); + if (j < 0) { + err("%s: usb_set_interface() error %d.", proc, j); + uvd->last_error = j; + } + } +} + +/* + * usbvideo_NewFrame() + * + * History: + * 29-Mar-00 Added copying of previous frame into the current one. + * 6-Aug-00 Added model 3 video sizes, removed redundant width, height. + */ +int usbvideo_NewFrame(uvd_t *uvd, int framenum) +{ + usbvideo_frame_t *frame; + int n; + + if (uvd->debug > 1) + info("usbvideo_NewFrame($%p,%d.)", uvd, framenum); + + /* If we're not grabbing a frame right now and the other frame is */ + /* ready to be grabbed into, then use it instead */ + if (uvd->curframe != -1) + return 0; + + /* If necessary we adjust picture settings between frames */ + if (!uvd->settingsAdjusted) { + if (VALID_CALLBACK(uvd, adjustPicture)) + GET_CALLBACK(uvd, adjustPicture)(uvd); + uvd->settingsAdjusted = 1; + } + + n = (framenum - 1 + USBVIDEO_NUMFRAMES) % USBVIDEO_NUMFRAMES; + if (uvd->frame[n].frameState == FrameState_Ready) + framenum = n; + + frame = &uvd->frame[framenum]; + + frame->frameState = FrameState_Grabbing; + frame->scanstate = ScanState_Scanning; + frame->seqRead_Length = 0; /* Accumulated in xxx_parse_data() */ + frame->deinterlace = Deinterlace_None; + frame->flags = 0; /* No flags yet, up to minidriver (or us) to set them */ + uvd->curframe = framenum; + + /* + * Normally we would want to copy previous frame into the current one + * before we even start filling it with data; this allows us to stop + * filling at any moment; top portion of the frame will be new and + * bottom portion will stay as it was in previous frame. If we don't + * do that then missing chunks of video stream will result in flickering + * portions of old data whatever it was before. + * + * If we choose not to copy previous frame (to, for example, save few + * bus cycles - the frame can be pretty large!) then we have an option + * to clear the frame before using. If we experience losses in this + * mode then missing picture will be black (no flickering). + * + * Finally, if user chooses not to clean the current frame before + * filling it with data then the old data will be visible if we fail + * to refill entire frame with new data. + */ + if (!(uvd->flags & FLAGS_SEPARATE_FRAMES)) { + /* This copies previous frame into this one to mask losses */ + memmove(frame->data, uvd->frame[1-framenum].data, uvd->max_frame_size); + } else { + if (uvd->flags & FLAGS_CLEAN_FRAMES) { + /* This provides a "clean" frame but slows things down */ + memset(frame->data, 0, uvd->max_frame_size); + } + } + return 0; +} + +/* + * usbvideo_CollectRawData() + * + * This procedure can be used instead of 'processData' callback if you + * only want to dump the raw data from the camera into the output + * device (frame buffer). You can look at it with V4L client, but the + * image will be unwatchable. The main purpose of this code and of the + * mode FLAGS_NO_DECODING is debugging and capturing of datastreams from + * new, unknown cameras. This procedure will be automatically invoked + * instead of the specified callback handler when uvd->flags has bit + * FLAGS_NO_DECODING set. Therefore, any regular build of any driver + * based on usbvideo can use this feature at any time. + */ +void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame) +{ + int n; + + assert(uvd != NULL); + assert(frame != NULL); + + /* Try to move data from queue into frame buffer */ + n = RingQueue_GetLength(&uvd->dp); + if (n > 0) { + int m; + /* See how much space we have left */ + m = uvd->max_frame_size - frame->seqRead_Length; + if (n > m) + n = m; + /* Now move that much data into frame buffer */ + RingQueue_Dequeue( + &uvd->dp, + frame->data + frame->seqRead_Length, + m); + frame->seqRead_Length += m; + } + /* See if we filled the frame */ + if (frame->seqRead_Length >= uvd->max_frame_size) { + frame->frameState = FrameState_Done; + uvd->curframe = -1; + uvd->stats.frame_num++; + } +} + +int usbvideo_GetFrame(uvd_t *uvd, int frameNum) +{ + static const char proc[] = "usbvideo_GetFrame"; + usbvideo_frame_t *frame = &uvd->frame[frameNum]; + + if (uvd->debug >= 2) + info("%s($%p,%d.)", proc, uvd, frameNum); + + switch (frame->frameState) { + case FrameState_Unused: + if (uvd->debug >= 2) + info("%s: FrameState_Unused", proc); + return -EINVAL; + case FrameState_Ready: + case FrameState_Grabbing: + case FrameState_Error: + { + int ntries, signalPending; + redo: + if (!CAMERA_IS_OPERATIONAL(uvd)) { + if (uvd->debug >= 2) + info("%s: Camera is not operational (1)", proc); + return -EIO; + } + ntries = 0; + do { + RingQueue_InterruptibleSleepOn(&uvd->dp); + signalPending = signal_pending(current); + if (!CAMERA_IS_OPERATIONAL(uvd)) { + if (uvd->debug >= 2) + info("%s: Camera is not operational (2)", proc); + return -EIO; + } + assert(uvd->fbuf != NULL); + if (signalPending) { + if (uvd->debug >= 2) + info("%s: Signal=$%08x", proc, signalPending); + if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) { + usbvideo_TestPattern(uvd, 1, 0); + uvd->curframe = -1; + uvd->stats.frame_num++; + if (uvd->debug >= 2) + info("%s: Forced test pattern screen", proc); + return 0; + } else { + /* Standard answer: Interrupted! */ + if (uvd->debug >= 2) + info("%s: Interrupted!", proc); + return -EINTR; + } + } else { + /* No signals - we just got new data in dp queue */ + if (uvd->flags & FLAGS_NO_DECODING) + usbvideo_CollectRawData(uvd, frame); + else if (VALID_CALLBACK(uvd, processData)) + GET_CALLBACK(uvd, processData)(uvd, frame); + else + err("%s: processData not set", proc); + } + } while (frame->frameState == FrameState_Grabbing); + if (uvd->debug >= 2) { + info("%s: Grabbing done; state=%d. (%lu. bytes)", + proc, frame->frameState, frame->seqRead_Length); + } + if (frame->frameState == FrameState_Error) { + int ret = usbvideo_NewFrame(uvd, frameNum); + if (ret < 0) { + err("%s: usbvideo_NewFrame() failed (%d.)", proc, ret); + return ret; + } + goto redo; + } + /* Note that we fall through to meet our destiny below */ + } + case FrameState_Done: + /* + * Do all necessary postprocessing of data prepared in + * "interrupt" code and the collecting code above. The + * frame gets marked as FrameState_Done by queue parsing code. + * This status means that we collected enough data and + * most likely processed it as we went through. However + * the data may need postprocessing, such as deinterlacing + * or picture adjustments implemented in software (horror!) + * + * As soon as the frame becomes "final" it gets promoted to + * FrameState_Done_Hold status where it will remain until the + * caller consumed all the video data from the frame. Then + * the empty shell of ex-frame is thrown out for dogs to eat. + * But we, worried about pets, will recycle the frame! + */ + uvd->stats.frame_num++; + if ((uvd->flags & FLAGS_NO_DECODING) == 0) { + if (VALID_CALLBACK(uvd, postProcess)) + GET_CALLBACK(uvd, postProcess)(uvd, frame); + if (frame->flags & USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST) + usbvideo_SoftwareContrastAdjustment(uvd, frame); + } + frame->frameState = FrameState_Done_Hold; + if (uvd->debug >= 2) + info("%s: Entered FrameState_Done_Hold state.", proc); + return 0; + + case FrameState_Done_Hold: + /* + * We stay in this state indefinitely until someone external, + * like ioctl() or read() call finishes digesting the frame + * data. Then it will mark the frame as FrameState_Unused and + * it will be released back into the wild to roam freely. + */ + if (uvd->debug >= 2) + info("%s: FrameState_Done_Hold state.", proc); + return 0; + } + + /* Catch-all for other cases. We shall not be here. */ + err("%s: Invalid state %d.", proc, frame->frameState); + frame->frameState = FrameState_Unused; + return 0; +} + +/* + * usbvideo_DeinterlaceFrame() + * + * This procedure deinterlaces the given frame. Some cameras produce + * only half of scanlines - sometimes only even lines, sometimes only + * odd lines. The deinterlacing method is stored in frame->deinterlace + * variable. + * + * Here we scan the frame vertically and replace missing scanlines with + * average between surrounding ones - before and after. If we have no + * line above then we just copy next line. Similarly, if we need to + * create a last line then preceding line is used. + */ +void usbvideo_DeinterlaceFrame(uvd_t *uvd, usbvideo_frame_t *frame) +{ + if ((uvd == NULL) || (frame == NULL)) + return; + + if ((frame->deinterlace == Deinterlace_FillEvenLines) || + (frame->deinterlace == Deinterlace_FillOddLines)) + { + const int v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + int i = (frame->deinterlace == Deinterlace_FillEvenLines) ? 0 : 1; + + for (; i < VIDEOSIZE_Y(frame->request); i += 2) { + const unsigned char *fs1, *fs2; + unsigned char *fd; + int ip, in, j; /* Previous and next lines */ + + /* + * Need to average lines before and after 'i'. + * If we go out of bounds seeking those lines then + * we point back to existing line. + */ + ip = i - 1; /* First, get rough numbers */ + in = i + 1; + + /* Now validate */ + if (ip < 0) + ip = in; + if (in >= VIDEOSIZE_Y(frame->request)) + in = ip; + + /* Sanity check */ + if ((ip < 0) || (in < 0) || + (ip >= VIDEOSIZE_Y(frame->request)) || + (in >= VIDEOSIZE_Y(frame->request))) + { + err("Error: ip=%d. in=%d. req.height=%ld.", + ip, in, VIDEOSIZE_Y(frame->request)); + break; + } + + /* Now we need to average lines 'ip' and 'in' to produce line 'i' */ + fs1 = frame->data + (v4l_linesize * ip); + fs2 = frame->data + (v4l_linesize * in); + fd = frame->data + (v4l_linesize * i); + + /* Average lines around destination */ + for (j=0; j < v4l_linesize; j++) { + fd[j] = (unsigned char)((((unsigned) fs1[j]) + + ((unsigned)fs2[j])) >> 1); + } + } + } + + /* Optionally display statistics on the screen */ + if (uvd->flags & FLAGS_OVERLAY_STATS) + usbvideo_OverlayStats(uvd, frame); +} + +/* + * usbvideo_SoftwareContrastAdjustment() + * + * This code adjusts the contrast of the frame, assuming RGB24 format. + * As most software image processing, this job is CPU-intensive. + * Get a camera that supports hardware adjustment! + * + * History: + * 09-Feb-2001 Created. + */ +void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd, usbvideo_frame_t *frame) +{ + static const char proc[] = "usbvideo_SoftwareContrastAdjustment"; + int i, j, v4l_linesize; + signed long adj; + const int ccm = 128; /* Color correction median - see below */ + + if ((uvd == NULL) || (frame == NULL)) { + err("%s: Illegal call.", proc); + return; + } + adj = (uvd->vpic.contrast - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + RESTRICT_TO_RANGE(adj, -ccm, ccm+1); + if (adj == 0) { + /* In rare case of no adjustment */ + return; + } + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + for (i=0; i < VIDEOSIZE_Y(frame->request); i++) { + unsigned char *fd = frame->data + (v4l_linesize * i); + for (j=0; j < v4l_linesize; j++) { + signed long v = (signed long) fd[j]; + /* Magnify up to 2 times, reduce down to zero */ + v = 128 + ((ccm + adj) * (v - 128)) / ccm; + RESTRICT_TO_RANGE(v, 0, 0xFF); /* Must flatten tails */ + fd[j] = (unsigned char) v; + } + } +} + +/* + * /proc interface + * + * We will be creating directories and entries under /proc/video using + * external 'video_proc_entry' directory which is exported by videodev.o + * module. Within that directory we will create $driver/ directory to + * uniquely and uniformly refer to our specific $driver. Within that + * directory we will finally create an entry that is named after the + * video device node - video3, for example. The format of that file + * is determined by callbacks that the minidriver may provide. If no + * callbacks are provided (neither read nor write) then we don't create + * the entry. + * + * Here is a sample directory entry: /proc/video/ibmcam/video3 + * + * The "file" video3 (in example above) is readable and writeable, in + * theory. If the minidriver provides callbacks to do reading and + * writing then both those procedures are supported. However if the + * driver leaves callbacks in default (NULL) state the default + * read and write handlers are used. The default read handler reports + * that the driver does not support /proc fs. The default write handler + * returns error code on any write attempt. + */ + +#if USES_PROC_FS + +extern struct proc_dir_entry *video_proc_entry; + +static void usbvideo_procfs_level1_create(usbvideo_t *ut) +{ + static const char proc[] = "usbvideo_procfs_level1_create"; + + if (ut == NULL) { + err("%s: ut == NULL", proc); + return; + } + if (video_proc_entry == NULL) { + err("%s: /proc/video/ doesn't exist.", proc); + return; + } + ut->procfs_dEntry = create_proc_entry(ut->drvName, S_IFDIR, video_proc_entry); + if (ut->procfs_dEntry != NULL) { + if (ut->md_module != NULL) + ut->procfs_dEntry->owner = ut->md_module; + } else { + err("%s: Unable to initialize /proc/video/%s", proc, ut->drvName); + } +} + +static void usbvideo_procfs_level1_destroy(usbvideo_t *ut) +{ + static const char proc[] = "usbvideo_procfs_level1_destroy"; + + if (ut == NULL) { + err("%s: ut == NULL", proc); + return; + } + if (ut->procfs_dEntry != NULL) { + remove_proc_entry(ut->drvName, video_proc_entry); + ut->procfs_dEntry = NULL; + } +} + +static void usbvideo_procfs_level2_create(uvd_t *uvd) +{ + static const char proc[] = "usbvideo_procfs_level2_create"; + + if (uvd == NULL) { + err("%s: uvd == NULL", proc); + return; + } + assert(uvd->handle != NULL); + if (uvd->handle->procfs_dEntry == NULL) { + err("%s: uvd->handle->procfs_dEntry == NULL", proc); + return; + } + + sprintf(uvd->videoName, "video%d", uvd->vdev.minor); + uvd->procfs_vEntry = create_proc_entry( + uvd->videoName, + S_IFREG | S_IRUGO | S_IWUSR, + uvd->handle->procfs_dEntry); + if (uvd->procfs_vEntry != NULL) { + uvd->procfs_vEntry->data = uvd; + uvd->procfs_vEntry->read_proc = uvd->handle->cb.procfs_read; + uvd->procfs_vEntry->write_proc = uvd->handle->cb.procfs_write; + } else { + err("%s: Failed to create entry \"%s\"", proc, uvd->videoName); + } +} + +static void usbvideo_procfs_level2_destroy(uvd_t *uvd) +{ + static const char proc[] = "usbvideo_procfs_level2_destroy"; + + if (uvd == NULL) { + err("%s: uvd == NULL", proc); + return; + } + if (uvd->procfs_vEntry != NULL) { + remove_proc_entry(uvd->videoName, uvd->procfs_vEntry); + uvd->procfs_vEntry = NULL; + } +} + +static int usbvideo_default_procfs_read_proc( + char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + char *out = page; + int len; + + /* Stay under PAGE_SIZE or else */ + out += sprintf(out, "This driver does not support /proc services.\n"); + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +static int usbvideo_default_procfs_write_proc( + struct file *file, const char *buffer, + unsigned long count, void *data) +{ + return -EINVAL; +} + +#endif /* USES_PROC_FS */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/usb/usbvideo.h linux.ac/drivers/usb/usbvideo.h --- linux.vanilla/drivers/usb/usbvideo.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/usb/usbvideo.h Sat May 26 18:17:23 2001 @@ -0,0 +1,424 @@ +/* + * 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, 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 usbvideo_h +#define usbvideo_h + +#include +#include +#include +#include + +/* Most helpful debugging aid */ +#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) + +#define USES_PROC_FS (defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)) +#define USBVIDEO_REPORT_STATS 1 /* Set to 0 to block statistics on close */ + +/* Bit flags (options) */ +#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) +#define FLAGS_MONOCHROME (1 << 1) +#define FLAGS_DISPLAY_HINTS (1 << 2) +#define FLAGS_OVERLAY_STATS (1 << 3) +#define FLAGS_FORCE_TESTPATTERN (1 << 4) +#define FLAGS_SEPARATE_FRAMES (1 << 5) +#define FLAGS_CLEAN_FRAMES (1 << 6) +#define FLAGS_NO_DECODING (1 << 7) + +/* Bit flags for frames (apply to the frame where they are specified) */ +#define USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST (1 << 0) + +/* Camera capabilities (maximum) */ +#define CAMERA_URB_FRAMES 32 +#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */ +#define FRAMES_PER_DESC (CAMERA_URB_FRAMES) +#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET) + +/* This macro restricts an int variable to an inclusive range */ +#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } + +#define V4L_BYTES_PER_PIXEL 3 /* Because we produce RGB24 */ + +/* + * Use this macro to construct constants for different video sizes. + * We have to deal with different video sizes that have to be + * configured in the device or compared against when we receive + * a data. Normally one would define a bunch of VIDEOSIZE_x_by_y + * #defines and that's the end of story. However this solution + * does not allow to convert between real pixel sizes and the + * constant (integer) value that may be used to tag a frame or + * whatever. The set of macros below constructs videosize constants + * from the pixel size and allows to reconstruct the pixel size + * from the combined value later. + */ +#define VIDEOSIZE(x,y) (((x) & 0xFFFFL) | (((y) & 0xFFFFL) << 16)) +#define VIDEOSIZE_X(vs) ((vs) & 0xFFFFL) +#define VIDEOSIZE_Y(vs) (((vs) >> 16) & 0xFFFFL) +typedef unsigned long videosize_t; + +/* + * This macro checks if the camera is still operational. The 'uvd' + * pointer must be valid, uvd->dev must be valid, we are not + * removing the device and the device has not erred on us. + */ +#define CAMERA_IS_OPERATIONAL(uvd) (\ + (uvd != NULL) && \ + ((uvd)->dev != NULL) && \ + ((uvd)->last_error == 0) && \ + (!(uvd)->remove_pending)) + +/* + * We use macros to do YUV -> RGB conversion because this is + * very important for speed and totally unimportant for size. + * + * YUV -> RGB Conversion + * --------------------- + * + * B = 1.164*(Y-16) + 2.018*(V-128) + * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) + * R = 1.164*(Y-16) + 1.596*(U-128) + * + * If you fancy integer arithmetics (as you should), hear this: + * + * 65536*B = 76284*(Y-16) + 132252*(V-128) + * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) + * 65536*R = 76284*(Y-16) + 104595*(U-128) + * + * Make sure the output values are within [0..255] range. + */ +#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) +#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \ + int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ + mm_y = (my) - 16; \ + mm_u = (mu) - 128; \ + mm_v = (mv) - 128; \ + mm_yc= mm_y * 76284; \ + mm_b = (mm_yc + 132252*mm_v ) >> 16; \ + mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \ + mm_r = (mm_yc + 104595*mm_u ) >> 16; \ + mb = LIMIT_RGB(mm_b); \ + mg = LIMIT_RGB(mm_g); \ + mr = LIMIT_RGB(mm_r); \ +} + +#define RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) % (rq)->length +#define RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n) +#define RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) % (rq)->length]) + +typedef struct { + unsigned char *queue; /* Data from the Isoc data pump */ + int length; /* How many bytes allocated for the queue */ + int wi; /* That's where we write */ + int ri; /* Read from here until you hit write index */ + wait_queue_head_t wqh; /* Processes waiting */ +} RingQueue_t; + +typedef enum { + ScanState_Scanning, /* Scanning for header */ + ScanState_Lines /* Parsing lines */ +} ScanState_t; + +/* Completion states of the data parser */ +typedef enum { + scan_Continue, /* Just parse next item */ + scan_NextFrame, /* Frame done, send it to V4L */ + scan_Out, /* Not enough data for frame */ + scan_EndParse /* End parsing */ +} ParseState_t; + +typedef enum { + FrameState_Unused, /* Unused (no MCAPTURE) */ + FrameState_Ready, /* Ready to start grabbing */ + FrameState_Grabbing, /* In the process of being grabbed into */ + FrameState_Done, /* Finished grabbing, but not been synced yet */ + FrameState_Done_Hold, /* Are syncing or reading */ + FrameState_Error, /* Something bad happened while processing */ +} FrameState_t; + +/* + * Some frames may contain only even or odd lines. This type + * specifies what type of deinterlacing is required. + */ +typedef enum { + Deinterlace_None=0, + Deinterlace_FillOddLines, + Deinterlace_FillEvenLines +} Deinterlace_t; + +struct usb_device; + +#define USBVIDEO_NUMFRAMES 2 /* How many frames we work with */ +#define USBVIDEO_NUMSBUF 2 /* How many URBs linked in a ring */ + +/* This structure represents one Isoc request - URB and buffer */ +typedef struct { + char *data; + urb_t *urb; +} usbvideo_sbuf_t; + +typedef struct { + char *data; /* Frame buffer */ + unsigned long header; /* Significant bits from the header */ + + videosize_t canvas; /* The canvas (max. image) allocated */ + videosize_t request; /* That's what the application asked for */ + unsigned short palette; /* The desired format */ + + FrameState_t frameState;/* State of grabbing */ + ScanState_t scanstate; /* State of scanning */ + Deinterlace_t deinterlace; + int flags; /* USBVIDEO_FRAME_FLAG_xxx bit flags */ + + int curline; /* Line of frame we're working on */ + + long seqRead_Length; /* Raw data length of frame */ + long seqRead_Index; /* Amount of data that has been already read */ + + void *user; /* Additional data that user may need */ +} usbvideo_frame_t; + +/* Statistics that can be overlaid on screen */ +typedef struct { + unsigned long frame_num; /* Sequential number of the frame */ + unsigned long urb_count; /* How many URBs we received so far */ + unsigned long urb_length; /* Length of last URB */ + unsigned long data_count; /* How many bytes we received */ + unsigned long header_count; /* How many frame headers we found */ + unsigned long iso_skip_count; /* How many empty ISO packets received */ + unsigned long iso_err_count; /* How many bad ISO packets received */ +} usbvideo_statistics_t; + +struct s_usbvideo_t; + +typedef struct { + struct video_device vdev; /* Must be the first field! */ + struct usb_device *dev; + struct s_usbvideo_t *handle; /* Points back to the usbvideo_t */ + void *user_data; /* Camera-dependent data */ + int user_size; /* Size of that camera-dependent data */ + int debug; /* Debug level for usbvideo */ + unsigned char iface; /* Video interface number */ + unsigned char video_endp; + unsigned char ifaceAltActive; + unsigned char ifaceAltInactive; /* Alt settings */ + unsigned long flags; /* FLAGS_USBVIDEO_xxx */ + unsigned long paletteBits; /* Which palettes we accept? */ + unsigned short defaultPalette; /* What palette to use for read() */ + struct semaphore lock; + int user; /* user count for exclusive use */ + + videosize_t videosize; /* Current setting */ + videosize_t canvas; /* This is the width,height of the V4L canvas */ + int max_frame_size; /* Bytes in one video frame */ + + int uvd_used; /* Is this structure in use? */ + int streaming; /* Are we streaming Isochronous? */ + int grabbing; /* Are we grabbing? */ + int settingsAdjusted; /* Have we adjusted contrast etc.? */ + int last_error; /* What calamity struck us? */ + + char *fbuf; /* Videodev buffer area */ + int fbuf_size; /* Videodev buffer size */ + + int curframe; + int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */ + + RingQueue_t dp; /* Isoc data pump */ + usbvideo_frame_t frame[USBVIDEO_NUMFRAMES]; + usbvideo_sbuf_t sbuf[USBVIDEO_NUMSBUF]; + + volatile int remove_pending; /* If set then about to exit */ + + struct video_picture vpic, vpic_old; /* Picture settings */ + struct video_capability vcap; /* Video capabilities */ + struct video_channel vchan; /* May be used for tuner support */ + usbvideo_statistics_t stats; + struct proc_dir_entry *procfs_vEntry; /* /proc/video/MYDRIVER/video2 */ + char videoName[32]; /* Holds name like "video7" */ +} uvd_t; + +/* + * usbvideo callbacks (virtual methods). They are set when usbvideo + * services are registered. All of these default to NULL, except those + * that default to usbvideo-provided methods. + */ +typedef struct { +#if defined(usb_device_id_ver) + /* New style probe (for 2.4.x kernels with hotplugging) */ + void *(*probe)(struct usb_device *, unsigned int,const struct usb_device_id *); +#else + /* Old style probe (for 2.2.x kernels) */ + void *(*probe)(struct usb_device *, unsigned int); +#endif + void (*userFree)(uvd_t *); + void (*disconnect)(struct usb_device *, void *); + int (*setupOnOpen)(uvd_t *); + void (*videoStart)(uvd_t *); + void (*videoStop)(uvd_t *); + void (*processData)(uvd_t *, usbvideo_frame_t *); + void (*postProcess)(uvd_t *, usbvideo_frame_t *); + void (*adjustPicture)(uvd_t *); + int (*getFPS)(uvd_t *); + int (*overlayHook)(uvd_t *, usbvideo_frame_t *); + int (*getFrame)(uvd_t *, int); + int (*procfs_read)(char *page,char **start,off_t off,int count,int *eof,void *data); + int (*procfs_write)(struct file *file,const char *buffer,unsigned long count,void *data); +} usbvideo_cb_t; + +struct s_usbvideo_t { + int num_cameras; /* As allocated */ + struct usb_driver usbdrv; /* Interface to the USB stack */ + char drvName[80]; /* Driver name */ + struct semaphore lock; /* Mutex protecting camera structures */ + usbvideo_cb_t cb; /* Table of callbacks (virtual methods) */ + struct video_device vdt; /* Video device template */ + uvd_t *cam; /* Array of camera structures */ + int uses_procfs; /* Non-zero if we create /proc entries */ + struct proc_dir_entry *procfs_dEntry; /* /proc/video/MYDRIVER */ + struct module *md_module; /* Minidriver module */ +}; +typedef struct s_usbvideo_t usbvideo_t; + +/* + * This macro retrieves callback address from the uvd_t object. + * No validity checks are done here, so be sure to check the + * callback beforehand with VALID_CALLBACK. + */ +#define GET_CALLBACK(uvd,cbName) ((uvd)->handle->cb.cbName) + +/* + * This macro returns either callback pointer or NULL. This is safe + * macro, meaning that most of components of data structures involved + * may be NULL - this only results in NULL being returned. You may + * wish to use this macro to make sure that the callback is callable. + * However keep in mind that those checks take time. + */ +#define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \ + ((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL) + +void RingQueue_Initialize(RingQueue_t *rq); +void RingQueue_Allocate(RingQueue_t *rq, int rqLen); +int RingQueue_IsAllocated(const RingQueue_t *rq); +void RingQueue_Free(RingQueue_t *rq); +int RingQueue_Dequeue(RingQueue_t *rq, unsigned char *dst, int len); +int RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n); +int RingQueue_GetLength(const RingQueue_t *rq); +void RingQueue_InterruptibleSleepOn(RingQueue_t *rq); +void RingQueue_WakeUpInterruptible(RingQueue_t *rq); + +void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame); +void usbvideo_DrawLine( + usbvideo_frame_t *frame, + int x1, int y1, + int x2, int y2, + unsigned char cr, unsigned char cg, unsigned char cb); +void usbvideo_HexDump(const unsigned char *data, int len); +void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame, int x, int y, int ch); +void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame, int x, int y, const char *str); +void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame); +void usbvideo_ReportStatistics(const uvd_t *uvd); +void usbvideo_SayAndWait(const char *what); +void usbvideo_TestPattern(uvd_t *uvd, int fullframe, int pmode); +void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs); + +/* Memory allocation routines */ +unsigned long usbvideo_uvirt_to_kva(pgd_t *pgd, unsigned long adr); +unsigned long usbvideo_uvirt_to_bus(unsigned long adr); +unsigned long usbvideo_kvirt_to_bus(unsigned long adr); +unsigned long usbvideo_kvirt_to_pa(unsigned long adr); +void *usbvideo_rvmalloc(unsigned long size); +void usbvideo_rvfree(void *mem, unsigned long size); + +int usbvideo_register( + usbvideo_t **pCams, + const int num_cams, + const int num_extra, + const char *driverName, + const usbvideo_cb_t *cbTable, + struct module *md); +uvd_t *usbvideo_AllocateDevice(usbvideo_t *cams); +int usbvideo_RegisterVideoDevice(uvd_t *uvd); +void usbvideo_Deregister(usbvideo_t **uvt); +void usbvideo_Disconnect(struct usb_device *dev, void *ptr); +void usbvideo_CameraRelease(uvd_t *uvd); + +void usbvideo_v4l_close(struct video_device *dev); +int usbvideo_v4l_initialize(struct video_device *dev); +int usbvideo_v4l_ioctl(struct video_device *dev, unsigned int cmd, void *arg); +int usbvideo_v4l_mmap(struct video_device *dev, const char *adr, unsigned long size); +int usbvideo_v4l_open(struct video_device *dev, int flags); +long usbvideo_v4l_read(struct video_device *dev, char *buf, + unsigned long count, int noblock); +long usbvideo_v4l_write(struct video_device *dev, const char *buf, + unsigned long count, int noblock); + +int usbvideo_GetFrame(uvd_t *uvd, int frameNum); +int usbvideo_NewFrame(uvd_t *uvd, int framenum); +int usbvideo_StartDataPump(uvd_t *uvd); +void usbvideo_StopDataPump(uvd_t *uvd); +void usbvideo_DeinterlaceFrame(uvd_t *uvd, usbvideo_frame_t *frame); +void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd, usbvideo_frame_t *frame); + +/* + * This code performs bounds checking - use it when working with + * new formats, or else you may get oopses all over the place. + * If pixel falls out of bounds then it gets shoved back (as close + * to place of offence as possible) and is painted bright red. + * + * There are two important concepts: frame width, height and + * V4L canvas width, height. The former is the area requested by + * the application -for this very frame-. The latter is the largest + * possible frame that we can serve (we advertise that via V4L ioctl). + * The frame data is expected to be formatted as lines of length + * VIDEOSIZE_X(fr->request), total VIDEOSIZE_Y(frame->request) lines. + */ +static inline void RGB24_PUTPIXEL( + usbvideo_frame_t *fr, + int ix, int iy, + unsigned char vr, + unsigned char vg, + unsigned char vb) +{ + register unsigned char *pf; + int limiter = 0, mx, my; + mx = ix; + my = iy; + if (mx < 0) { + mx=0; + limiter++; + } else if (mx >= VIDEOSIZE_X((fr)->request)) { + mx= VIDEOSIZE_X((fr)->request) - 1; + limiter++; + } + if (my < 0) { + my = 0; + limiter++; + } else if (my >= VIDEOSIZE_Y((fr)->request)) { + my = VIDEOSIZE_Y((fr)->request) - 1; + limiter++; + } + pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*VIDEOSIZE_X((fr)->request) + (ix)); + if (limiter) { + *pf++ = 0; + *pf++ = 0; + *pf++ = 0xFF; + } else { + *pf++ = (vb); + *pf++ = (vg); + *pf++ = (vr); + } +} + +#endif /* usbvideo_h */ diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/Config.in linux.ac/drivers/video/Config.in --- linux.vanilla/drivers/video/Config.in Mon Apr 30 15:13:28 2001 +++ linux.ac/drivers/video/Config.in Wed Apr 18 14:00:31 2001 @@ -59,6 +59,9 @@ if [ "$CONFIG_ATARI" = "y" ]; then bool ' Atari native chipset support' CONFIG_FB_ATARI tristate ' ATI Mach64 display support' CONFIG_FB_ATY + if [ "$CONFIG_FB_ATY" != "n" ]; then + define_bool CONFIG_FB_ATY_GX y + fi fi if [ "$CONFIG_PPC" = "y" ]; then bool ' Open Firmware frame buffer device support' CONFIG_FB_OF @@ -70,6 +73,9 @@ bool ' S3 Trio display support' CONFIG_FB_S3TRIO tristate ' VGA 16-color graphics console' CONFIG_FB_VGA16 fi + if [ "$CONFIG_PARISC" = "y" ]; then + bool ' Generic STI frame buffer device support' CONFIG_FB_STI + fi if [ "$CONFIG_MAC" = "y" ]; then define_bool CONFIG_FB_MAC y bool ' Apple "valkyrie" display support' CONFIG_FB_VALKYRIE @@ -123,9 +129,18 @@ bool ' Multihead support' CONFIG_FB_MATROX_MULTIHEAD fi tristate ' ATI Mach64 display support (EXPERIMENTAL)' CONFIG_FB_ATY - tristate ' ATI Rage 128 display support (EXPERIMENTAL)' CONFIG_FB_ATY128 + if [ "$CONFIG_FB_ATY" != "n" ]; then + bool ' Mach64 GX support (EXPERIMENTAL)' CONFIG_FB_ATY_GX + bool ' Mach64 CT/VT/GT/LT (incl. 3D RAGE) support' CONFIG_FB_ATY_CT + fi + tristate ' ATI Radeon display support (EXPERIMENTAL)' CONFIG_FB_RADEON + tristate ' ATI Rage128 display support (EXPERIMENTAL)' CONFIG_FB_ATY128 + tristate ' SIS acceleration (EXPERIMENTAL)' CONFIG_FB_SIS + if [ "$CONFIG_FB_SIS" != "n" ]; then + bool ' SIS 630/540/730 support' CONFIG_FB_SIS_300 + bool ' SIS 315H/315 support' CONFIG_FB_SIS_315 + fi tristate ' 3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)' CONFIG_FB_3DFX - tristate ' SIS 630/540 display support (EXPERIMENTAL)' CONFIG_FB_SIS fi fi if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then @@ -158,12 +173,22 @@ bool ' PCI framebuffers' CONFIG_FB_PCI if [ "$CONFIG_FB_PCI" != "n" ]; then tristate ' ATI Mach64 display support' CONFIG_FB_ATY + if [ "$CONFIG_FB_ATY" != "n" ]; then + define_bool CONFIG_FB_ATY_CT y + fi fi fi fi if [ "$CONFIG_HD64461" = "y" ]; then tristate ' HD64461 Frame Buffer support' CONFIG_FB_HIT fi + if [ "$CONFIG_DECSTATION" = "y" ]; then + if [ "$CONFIG_TC" = "y" ]; then + bool ' PMAG-BA TURBOchannel framebuffer support' CONFIG_FB_PMAG_BA + bool ' PMAGB-B TURBOchannel framebuffer spport' CONFIG_FB_PMAGB_B + bool ' Maxine (Personal DECstation) onboard framebuffer spport' CONFIG_FB_MAXINE + fi + fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate ' Virtual Frame Buffer support (ONLY FOR TESTING!)' CONFIG_FB_VIRTUAL fi @@ -227,9 +252,11 @@ "$CONFIG_FB_IGA" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ "$CONFIG_FB_P9100" = "y" -o "$CONFIG_FB_ATY128" = "y" -o \ - "$CONFIG_FB_RIVA" = "y" -o \ + "$CONFIG_FB_RIVA" = "y" -o "$CONFIG_FB_RADEON" = "y" -o \ "$CONFIG_FB_SGIVW" = "y" -o "$CONFIG_FB_CYBER2000" = "y" -o \ "$CONFIG_FB_SA1100" = "y" -o "$CONFIG_FB_3DFX" = "y" -o \ + "$CONFIG_FB_PMAG_BA" = "y" -o "$CONFIG_FB_PMAGB_B" = "y" -o \ + "$CONFIG_FB_MAXINE" = "y" -o \ "$CONFIG_FB_SIS" = "y" ]; then define_tristate CONFIG_FBCON_CFB8 y else @@ -247,6 +274,8 @@ "$CONFIG_FB_P9100" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \ "$CONFIG_FB_RIVA" = "m" -o "$CONFIG_FB_3DFX" = "m" -o \ "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_CYBER2000" = "m" -o \ + "$CONFIG_FB_PMAG_BA" = "m" -o "CONFIG_FB_PMAGB_B" = "m" -o \ + "$CONFIG_FB_MAXINE" = "m" -o "$CONFIG_FB_RADEON" = "m" -o \ "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_SIS" = "m" ]; then define_tristate CONFIG_FBCON_CFB8 m fi @@ -254,7 +283,7 @@ if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATY" = "y" -o \ "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_VESA" = "y" -o \ "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_TBOX" = "y" -o \ - "$CONFIG_FB_Q40" = "y" -o \ + "$CONFIG_FB_Q40" = "y" -o "$CONFIG_FB_RADEON" = "y" -o \ "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \ "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ @@ -276,21 +305,21 @@ "$CONFIG_FB_PM2" = "m" -o "$CONFIG_FB_SGIVW" = "m" -o \ "$CONFIG_FB_RIVA" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \ "$CONFIG_FB_CYBER2000" = "m" -o "$CONFIG_FB_SIS" = "m" -o \ - "$CONFIG_FB_SA1100" = "m" ]; then + "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_RADEON" = "m" ]; then define_tristate CONFIG_FBCON_CFB16 m fi fi if [ "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ "$CONFIG_FB_CLGEN" = "y" -o "$CONFIG_FB_VESA" = "y" -o \ "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ - "$CONFIG_FB_ATY128" = "y" -o \ + "$CONFIG_FB_ATY128" = "y" -o "$CONFIG_FB_RADEON" = "y" -o \ "$CONFIG_FB_CYBER2000" = "y" ]; then define_tristate CONFIG_FBCON_CFB24 y else if [ "$CONFIG_FB_ATY" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ "$CONFIG_FB_CLGEN" = "m" -o "$CONFIG_FB_VESA" = "m" -o \ "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ - "$CONFIG_FB_ATY128" = "m" -o \ + "$CONFIG_FB_ATY128" = "m" -o "$CONFIG_FB_RADEON" = "m" -o \ "$CONFIG_FB_CYBER2000" = "m" ]; then define_tristate CONFIG_FBCON_CFB24 m fi @@ -302,6 +331,7 @@ "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ "$CONFIG_FB_RIVA" = "y" -o "$CONFIG_FB_ATY128" = "y" -o \ "$CONFIG_FB_FM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" -o \ + "$CONFIG_FB_RADEON" = "y" -o \ "$CONFIG_FB_3DFX" = "y" -o "$CONFIG_FB_SIS" = "y" ]; then define_tristate CONFIG_FBCON_CFB32 y else @@ -311,7 +341,7 @@ "$CONFIG_FB_TGA" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ "$CONFIG_FB_RIVA" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \ - "$CONFIG_FB_3DFX" = "m" -o \ + "$CONFIG_FB_3DFX" = "m" -o "$CONFIG_FB_RADEON" = "m" -o \ "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_SIS" = "m" ]; then define_tristate CONFIG_FBCON_CFB32 m fi @@ -358,6 +388,9 @@ if [ "$CONFIG_FB_HGA" = "m" ]; then define_tristate CONFIG_FBCON_HGA m fi + fi + if [ "$CONFIG_FB_STI" = "y" ]; then + define_tristate CONFIG_FBCON_STI y fi fi bool ' Support only 8 pixels wide fonts' CONFIG_FBCON_FONTWIDTH8_ONLY diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/Makefile linux.ac/drivers/video/Makefile --- linux.vanilla/drivers/video/Makefile Mon Apr 30 15:13:28 2001 +++ linux.ac/drivers/video/Makefile Wed Apr 18 14:00:41 2001 @@ -22,6 +22,7 @@ obj-$(CONFIG_DUMMY_CONSOLE) += dummycon.o obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o obj-$(CONFIG_PROM_CONSOLE) += promcon.o promcon_tbl.o +obj-$(CONFIG_STI_CONSOLE) += sticon.o sticon-bmode.o sticore.o obj-$(CONFIG_VGA_CONSOLE) += vgacon.o obj-$(CONFIG_MDA_CONSOLE) += mdacon.o @@ -46,8 +47,8 @@ obj-$(CONFIG_FB_APOLLO) += dnfb.o obj-$(CONFIG_FB_Q40) += q40fb.o obj-$(CONFIG_FB_ATARI) += atafb.o -obj-$(CONFIG_FB_ATY) += atyfb.o obj-$(CONFIG_FB_ATY128) += aty128fb.o +obj-$(CONFIG_FB_RADEON) += radeonfb.o obj-$(CONFIG_FB_IGA) += igafb.o obj-$(CONFIG_FB_CONTROL) += controlfb.o obj-$(CONFIG_FB_PLATINUM) += platinumfb.o @@ -78,6 +79,10 @@ obj-$(CONFIG_FB_CGFOURTEEN) += cgfourteenfb.o sbusfb.o obj-$(CONFIG_FB_P9100) += p9100fb.o sbusfb.o obj-$(CONFIG_FB_LEO) += leofb.o sbusfb.o +obj-$(CONFIG_FB_STI) += stifb.o sticore.o fbgen.o +obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o +obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o +obj-$(CONFIG_FB_MAXINE) += maxinefb.o subdir-$(CONFIG_FB_MATROX) += matrox ifeq ($(CONFIG_FB_MATROX),y) @@ -94,6 +99,11 @@ obj-y += sis/sisfb.o endif +subdir-$(CONFIG_FB_ATY) += aty +ifeq ($(CONFIG_FB_ATY),y) +obj-y += aty/atyfb.o +endif + obj-$(CONFIG_FB_SUN3) += sun3fb.o obj-$(CONFIG_FB_BWTWO) += bwtwofb.o obj-$(CONFIG_FB_HGA) += hgafb.o @@ -121,6 +131,7 @@ obj-$(CONFIG_FBCON_MFB) += fbcon-mfb.o obj-$(CONFIG_FBCON_VGA) += fbcon-vga.o obj-$(CONFIG_FBCON_HGA) += fbcon-hga.o +obj-$(CONFIG_FBCON_STI) += fbcon-sti.o include $(TOPDIR)/Rules.make @@ -136,3 +147,4 @@ -e 's/dfont\(_uni.*\]\)/promfont\1 __initdata/' > promcon_tbl.c promcon_tbl.o: promcon_tbl.c $(TOPDIR)/include/linux/types.h + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/aty/Makefile linux.ac/drivers/video/aty/Makefile --- linux.vanilla/drivers/video/aty/Makefile Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/video/aty/Makefile Tue Apr 3 17:55:08 2001 @@ -0,0 +1,12 @@ + +O_TARGET := atyfb.o + +export-objs := atyfb_base.o mach64_accel.o + +obj-y := atyfb_base.o mach64_accel.o +obj-$(CONFIG_FB_ATY_GX) += mach64_gx.o +obj-$(CONFIG_FB_ATY_CT) += mach64_ct.o mach64_cursor.o +obj-m := $(O_TARGET) + +include $(TOPDIR)/Rules.make + diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/aty/atyfb.h linux.ac/drivers/video/aty/atyfb.h --- linux.vanilla/drivers/video/aty/atyfb.h Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/video/aty/atyfb.h Tue May 22 19:07:40 2001 @@ -0,0 +1,317 @@ + +/* + * ATI Frame Buffer Device Driver Core Definitions + */ + +#include + + + /* + * Elements of the Hardware specific atyfb_par structure + */ + +struct crtc { + u32 vxres; + u32 vyres; + u32 xoffset; + u32 yoffset; + u32 bpp; + u32 h_tot_disp; + u32 h_sync_strt_wid; + u32 v_tot_disp; + u32 v_sync_strt_wid; + u32 off_pitch; + u32 gen_cntl; + u32 dp_pix_width; /* acceleration */ + u32 dp_chain_mask; /* acceleration */ +}; + +struct pll_514 { + u8 m; + u8 n; +}; + +struct pll_18818 +{ + u32 program_bits; + u32 locationAddr; + u32 period_in_ps; + u32 post_divider; +}; + +struct pll_ct { + u8 pll_ref_div; + u8 pll_gen_cntl; + u8 mclk_fb_div; + u8 pll_vclk_cntl; + u8 vclk_post_div; + u8 vclk_fb_div; + u8 pll_ext_cntl; + u32 dsp_config; /* Mach64 GTB DSP */ + u32 dsp_on_off; /* Mach64 GTB DSP */ + u8 mclk_post_div_real; + u8 vclk_post_div_real; +}; + +union aty_pll { + struct pll_ct ct; + struct pll_514 ibm514; + struct pll_18818 ics2595; +}; + + + /* + * The Hardware parameters for each card + */ + +struct atyfb_par { + struct crtc crtc; + union aty_pll pll; + u32 accel_flags; +}; + +struct aty_cursor { + int enable; + int on; + int vbl_cnt; + int blink_rate; + u32 offset; + struct { + u16 x, y; + } pos, hot, size; + u32 color[2]; + u8 bits[8][64]; + u8 mask[8][64]; + u8 *ram; + struct timer_list *timer; +}; + +struct fb_info_aty { + struct fb_info fb_info; + struct fb_info_aty *next; + unsigned long ati_regbase_phys; + unsigned long ati_regbase; + unsigned long frame_buffer_phys; + unsigned long frame_buffer; + unsigned long clk_wr_offset; + struct pci_mmap_map *mmap_map; + struct aty_cursor *cursor; + struct aty_cmap_regs *aty_cmap_regs; + struct { u8 red, green, blue, pad; } palette[256]; + struct atyfb_par default_par; + struct atyfb_par current_par; + u32 features; + u32 total_vram; + u32 ref_clk_per; + u32 pll_per; + u32 mclk_per; + u8 bus_type; + u8 ram_type; + u8 mem_refresh_rate; + const struct aty_dac_ops *dac_ops; + const struct aty_pll_ops *pll_ops; + struct display disp; + struct display_switch dispsw; + union { +#ifdef FBCON_HAS_CFB16 + u16 cfb16[16]; +#endif +#ifdef FBCON_HAS_CFB24 + u32 cfb24[16]; +#endif +#ifdef FBCON_HAS_CFB32 + u32 cfb32[16]; +#endif + } fbcon_cmap; + u8 blitter_may_be_busy; +#ifdef __sparc__ + u8 mmaped; + int open; + int vtconsole; + int consolecnt; +#endif +#ifdef CONFIG_PMAC_PBOOK + unsigned char *save_framebuffer; + unsigned long save_pll[64]; +#endif +}; + + + /* + * Mach64 features + */ + +#define M64_HAS(feature) ((info)->features & (M64F_##feature)) + +#define M64F_RESET_3D 0x00000001 +#define M64F_MAGIC_FIFO 0x00000002 +#define M64F_GTB_DSP 0x00000004 +#define M64F_FIFO_24 0x00000008 +#define M64F_SDRAM_MAGIC_PLL 0x00000010 +#define M64F_MAGIC_POSTDIV 0x00000020 +#define M64F_INTEGRATED 0x00000040 +#define M64F_CT_BUS 0x00000080 +#define M64F_VT_BUS 0x00000100 +#define M64F_MOBIL_BUS 0x00000200 +#define M64F_GX 0x00000400 +#define M64F_CT 0x00000800 +#define M64F_VT 0x00001000 +#define M64F_GT 0x00002000 +#define M64F_MAGIC_VRAM_SIZE 0x00004000 +#define M64F_G3_PB_1_1 0x00008000 +#define M64F_G3_PB_1024x768 0x00010000 +#define M64F_EXTRA_BRIGHT 0x00020000 +#define M64F_LT_SLEEP 0x00040000 +#define M64F_XL_DLL 0x00080000 + + + /* + * Register access + */ + +static inline u32 aty_ld_le32(int regindex, + const struct fb_info_aty *info) +{ + /* Hack for bloc 1, should be cleanly optimized by compiler */ + if (regindex >= 0x400) + regindex -= 0x800; + +#if defined(__mc68000__) + return le32_to_cpu(*((volatile u32 *)(info->ati_regbase+regindex))); +#else + return readl (info->ati_regbase + regindex); +#endif +} + +static inline void aty_st_le32(int regindex, u32 val, + const struct fb_info_aty *info) +{ + /* Hack for bloc 1, should be cleanly optimized by compiler */ + if (regindex >= 0x400) + regindex -= 0x800; + +#if defined(__mc68000__) + *((volatile u32 *)(info->ati_regbase+regindex)) = cpu_to_le32(val); +#else + writel (val, info->ati_regbase + regindex); +#endif +} + +static inline u8 aty_ld_8(int regindex, + const struct fb_info_aty *info) +{ + /* Hack for bloc 1, should be cleanly optimized by compiler */ + if (regindex >= 0x400) + regindex -= 0x800; + + return readb (info->ati_regbase + regindex); +} + +static inline void aty_st_8(int regindex, u8 val, + const struct fb_info_aty *info) +{ + /* Hack for bloc 1, should be cleanly optimized by compiler */ + if (regindex >= 0x400) + regindex -= 0x800; + + writeb (val, info->ati_regbase + regindex); +} + +static inline u8 aty_ld_pll(int offset, const struct fb_info_aty *info) +{ + u8 res; + + /* write addr byte */ + aty_st_8(CLOCK_CNTL + 1, (offset << 2), info); + /* read the register value */ + res = aty_ld_8(CLOCK_CNTL + 2, info); + return res; +} + + + /* + * DAC operations + */ + +struct aty_dac_ops { + int (*set_dac)(const struct fb_info_aty *info, const union aty_pll *pll, + u32 bpp, u32 accel); +}; + +extern const struct aty_dac_ops aty_dac_ibm514; /* IBM RGB514 */ +extern const struct aty_dac_ops aty_dac_ati68860b; /* ATI 68860-B */ +extern const struct aty_dac_ops aty_dac_att21c498; /* AT&T 21C498 */ +extern const struct aty_dac_ops aty_dac_unsupported; /* unsupported */ +extern const struct aty_dac_ops aty_dac_ct; /* Integrated */ + + + /* + * Clock operations + */ + +struct aty_pll_ops { + int (*var_to_pll)(const struct fb_info_aty *info, u32 vclk_per, u8 bpp, + union aty_pll *pll); + u32 (*pll_to_var)(const struct fb_info_aty *info, + const union aty_pll *pll); + void (*set_pll)(const struct fb_info_aty *info, const union aty_pll *pll); +}; + +extern const struct aty_pll_ops aty_pll_ati18818_1; /* ATI 18818 */ +extern const struct aty_pll_ops aty_pll_stg1703; /* STG 1703 */ +extern const struct aty_pll_ops aty_pll_ch8398; /* Chrontel 8398 */ +extern const struct aty_pll_ops aty_pll_att20c408; /* AT&T 20C408 */ +extern const struct aty_pll_ops aty_pll_ibm514; /* IBM RGB514 */ +extern const struct aty_pll_ops aty_pll_unsupported; /* unsupported */ +extern const struct aty_pll_ops aty_pll_ct; /* Integrated */ + + +extern void aty_set_pll_ct(const struct fb_info_aty *info, + const union aty_pll *pll); +extern void aty_calc_pll_ct(const struct fb_info_aty *info, + struct pll_ct *pll); + + + /* + * Hardware cursor support + */ + +extern struct aty_cursor *aty_init_cursor(struct fb_info_aty *fb); +extern void atyfb_cursor(struct display *p, int mode, int x, int y); +extern void aty_set_cursor_color(struct fb_info_aty *fb); +extern void aty_set_cursor_shape(struct fb_info_aty *fb); +extern int atyfb_set_font(struct display *d, int width, int height); + + + /* + * Hardware acceleration + */ + +static inline void wait_for_fifo(u16 entries, const struct fb_info_aty *info) +{ + while ((aty_ld_le32(FIFO_STAT, info) & 0xffff) > + ((u32)(0x8000 >> entries))); +} + +static inline void wait_for_idle(struct fb_info_aty *info) +{ + wait_for_fifo(16, info); + while ((aty_ld_le32(GUI_STAT, info) & 1)!= 0); + info->blitter_may_be_busy = 0; +} + +extern void aty_reset_engine(const struct fb_info_aty *info); +extern void aty_init_engine(const struct atyfb_par *par, + struct fb_info_aty *info); +extern void aty_rectfill(int dstx, int dsty, u_int width, u_int height, + u_int color, struct fb_info_aty *info); + + + /* + * Text console acceleration + */ + +extern const struct display_switch fbcon_aty8; +extern const struct display_switch fbcon_aty16; +extern const struct display_switch fbcon_aty24; +extern const struct display_switch fbcon_aty32; diff -u --new-file --recursive --exclude-from /usr/src/exclude linux.vanilla/drivers/video/aty/atyfb_base.c linux.ac/drivers/video/aty/atyfb_base.c --- linux.vanilla/drivers/video/aty/atyfb_base.c Thu Jan 1 01:00:00 1970 +++ linux.ac/drivers/video/aty/atyfb_base.c Tue May 22 19:07:40 2001 @@ -0,0 +1,2896 @@ + +/* + * ATI Frame Buffer Device Driver Core + * + * Copyright (C) 1997-2001 Geert Uytterhoeven + * Copyright (C) 1998 Bernd Harries + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * + * This driver supports the following ATI graphics chips: + * - ATI Mach64 + * + * To do: add support for + * - ATI Rage128 (from aty128fb.c) + * - ATI Radeon (from radeonfb.c) + * + * This driver is partly based on the PowerMac console driver: + * + * Copyright (C) 1996 Paul Mackerras + * + * and on the PowerMac ATI/mach64 display driver: + * + * Copyright (C) 1997 Michael AK Tesch + * + * with work by Jon Howell + * Harry AC Eaton + * Anthony Tong + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + * Many thanks to Nitya from ATI devrel for support and patience ! + */ + +/****************************************************************************** + + TODO: + + - cursor support on all cards and all ramdacs. + - cursor parameters controlable via ioctl()s. + - guess PLL and MCLK based on the original PLL register values initialized + by the BIOS or Open Firmware (if they are initialized). + + (Anyone to help with this?) + +******************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include